import { getSupabaseAdmin } from '../_shared/supabaseAdmin.js'; import type { AggregatedSkillScore, CertificationTrack, ProjectEvidence, PublicProfile, ValidationSummary, } from '../_types.js'; type JsonRecord = Record; type UserRow = { id: string; username: string; referral_code: string | null; }; type CertificateRow = { id: string; topic: string; project_title: string | null; issued_at: string; metadata: JsonRecord | null; }; const CANONICAL_PUBLIC_DOMAIN = 'https://getcert.eu.cc'; function appBaseUrl(): string { const configured = (process.env.CERTIFICATE_VERIFY_BASE_URL || process.env.APP_ORIGIN || '').trim(); if ( !configured || configured.includes('hf.space') || configured.includes('huggingface.co') || configured.includes('vercel.app') || configured.includes('localhost') ) { return CANONICAL_PUBLIC_DOMAIN; } return configured.replace(/\/$/, ''); } function toStringValue(value: unknown): string | null { if (typeof value !== 'string') { return null; } const trimmed = value.trim(); return trimmed.length > 0 ? trimmed : null; } function toNumberValue(value: unknown): number | null { if (typeof value === 'number' && Number.isFinite(value)) { return value; } if (typeof value === 'string' && value.trim()) { const parsed = Number(value); if (Number.isFinite(parsed)) { return parsed; } } return null; } function getObject(value: unknown): JsonRecord | null { if (!value || typeof value !== 'object' || Array.isArray(value)) { return null; } return value as JsonRecord; } function parseValidationSummary(metadata: JsonRecord): ValidationSummary | null { const nested = getObject(metadata.validation_summary); if (!nested) { return null; } const suspiciousSignals = Array.isArray(nested.suspicious_signals) ? nested.suspicious_signals.filter((item): item is string => typeof item === 'string') : []; return { overallPassed: Boolean(nested.overall_passed), commitPassed: Boolean(nested.commit_passed), readmePassed: Boolean(nested.readme_passed), proofPassed: Boolean(nested.proof_passed), commitCount: Math.round(toNumberValue(nested.commit_count) ?? 0), fileCount: Math.round(toNumberValue(nested.file_count) ?? 0), meaningfulFileCount: Math.round(toNumberValue(nested.meaningful_file_count) ?? 0), readmeRichness: Math.round(toNumberValue(nested.readme_richness) ?? 0), suspiciousSignals, }; } function getDifficultyWeight(projectType: string | null): number { if (projectType === 'data') { return 1.3; } if (projectType === 'python') { return 1.15; } if (projectType === 'web' || projectType === 'fullstack') { return 1.2; } return 1; } function getDifficultyLevel(weight: number): 'beginner' | 'intermediate' | 'advanced' { if (weight >= 1.26) { return 'advanced'; } if (weight >= 1.16) { return 'intermediate'; } return 'beginner'; } function roundValue(value: number): number { return Math.round(value * 100) / 100; } function buildPassConfidence(finalScore: number, summary: ValidationSummary | null): number { if (!summary) { return Math.max(0, Math.min(100, Math.round(finalScore * 0.8))); } let confidence = finalScore * 0.6; confidence += summary.overallPassed ? 22 : 10; confidence += Math.min(10, summary.commitCount * 0.6); confidence += summary.readmePassed ? 8 : 2; return Math.max(0, Math.min(100, Math.round(confidence))); } export async function getPublicProfile(userId: string): Promise { const supabase = getSupabaseAdmin(); const base = appBaseUrl(); const { data: user, error: userError } = await supabase .from('users') .select('id, username, referral_code') .eq('id', userId) .single(); if (userError || !user) { throw new Error('User not found'); } const { data: certificates, error: certError } = await supabase .from('certificates') .select('id, topic, project_title, issued_at, metadata') .eq('user_id', userId) .order('issued_at', { ascending: false }); const { count: referredUsersCount } = await supabase .from('users') .select('id', { count: 'exact', head: true }) .eq('referred_by', userId); if (certError) { throw new Error(`Failed to fetch certificates: ${certError.message}`); } const rows = (certificates || []) as CertificateRow[]; const allProjects: ProjectEvidence[] = rows.map((item) => { const metadata = item.metadata || {}; const validationSummary = parseValidationSummary(metadata); const projectType = toStringValue(metadata.project_type); const difficultyWeight = getDifficultyWeight(projectType); const finalScore = Math.round(toNumberValue(metadata.final_score) ?? 0); const commitCount = Math.round( toNumberValue(metadata.commit_count) ?? toNumberValue(validationSummary?.commitCount) ?? 0 ); const fileCount = Math.round(toNumberValue(validationSummary?.fileCount) ?? 0); const readmeScore = Math.round(toNumberValue(validationSummary?.readmeRichness) ?? 0); const track = (toStringValue(metadata.certification_track) || 'verifiedcert') as CertificationTrack; return { certificateId: toStringValue(metadata.certificate_id), certificateDbId: item.id, skill: item.topic, projectTitle: item.project_title, projectType, difficultyLevel: getDifficultyLevel(difficultyWeight), difficultyWeight, githubRepoUrl: toStringValue(metadata.github_repo_url) || toStringValue(metadata.github_url), liveDemoUrl: toStringValue(metadata.live_demo_url) || toStringValue(metadata.proof_url), finalScore, commitCount, fileCount, validationSummary, passConfidence: buildPassConfidence(finalScore, validationSummary), issuedAt: item.issued_at, track, trustSignals: { commitCount, validationPassed: validationSummary ? validationSummary.overallPassed : finalScore >= 60, readmeScore, projectType, }, }; }); const projects = allProjects.filter((p) => p.track === 'verifiedcert'); const skillAccumulator = new Map(); for (const project of projects) { const current = skillAccumulator.get(project.skill) || { totalScore: 0, weightedScore: 0, totalWeight: 0, count: 0, }; current.totalScore += project.finalScore; current.weightedScore += project.finalScore * project.difficultyWeight; current.totalWeight += project.difficultyWeight; current.count += 1; skillAccumulator.set(project.skill, current); } const aggregatedSkills: AggregatedSkillScore[] = Array.from(skillAccumulator.entries()) .map(([skill, aggregate]) => ({ skill, averageScore: roundValue(aggregate.totalScore / Math.max(1, aggregate.count)), weightedScore: roundValue(aggregate.weightedScore / Math.max(1, aggregate.totalWeight)), projectCount: aggregate.count, totalWeight: roundValue(aggregate.totalWeight), })) .sort((left, right) => right.weightedScore - left.weightedScore); const totalCertificates = rows.length; const verifiedProjects = projects; const verifiedCount = verifiedProjects.length; const totalCommitCount = verifiedProjects.reduce((sum, project) => sum + project.commitCount, 0); const averageFinalScore = verifiedCount ? roundValue(verifiedProjects.reduce((sum, project) => sum + project.finalScore, 0) / verifiedCount) : 0; const averageReadmeScore = verifiedCount ? roundValue(verifiedProjects.reduce((sum, project) => sum + project.trustSignals.readmeScore, 0) / verifiedCount) : 0; const validationPassRate = verifiedCount ? roundValue( (verifiedProjects.filter((project) => project.trustSignals.validationPassed).length / verifiedCount) * 100 ) : 0; const certificatesPayload = rows.map((item) => { const metadata = item.metadata || {}; const projectType = toStringValue(metadata.project_type); const track = (toStringValue(metadata.certification_track) || 'verifiedcert') as CertificationTrack; return { id: item.id, skill: item.topic, projectTitle: item.project_title, issuedAt: item.issued_at, githubUrl: toStringValue(metadata.github_repo_url) || toStringValue(metadata.github_url), proofUrl: toStringValue(metadata.live_demo_url) || toStringValue(metadata.proof_url), certificateId: toStringValue(metadata.certificate_id), finalScore: Math.round(toNumberValue(metadata.final_score) ?? 0), difficultyLevel: getDifficultyLevel(getDifficultyWeight(projectType)), track, }; }); const quickCertCount = allProjects.filter((p) => p.track === 'quickcert').length; const verifiedCertCount = verifiedCount; return { userId, username: (user as UserRow).username, referralCode: (user as UserRow).referral_code, referredUsersCount: referredUsersCount || 0, certificates: certificatesPayload, aggregatedSkills, projects, trustMetrics: { totalCertificates, averageFinalScore, totalCommitCount, validationPassRate, averageReadmeScore, topSkill: aggregatedSkills[0]?.skill || null, quickCertCount, verifiedCertCount, }, shareLinks: { profile: `${base}/user/${encodeURIComponent(userId)}`, recruiter: `${base}/recruiter/${encodeURIComponent(userId)}`, referral: (user as UserRow).referral_code ? `${base}/?ref=${encodeURIComponent((user as UserRow).referral_code as string)}` : null, }, }; }