106 lines
3.3 KiB
TypeScript
106 lines
3.3 KiB
TypeScript
'use client';
|
|
|
|
import { AssessmentRecord, EncryptedAssessmentRecord } from '@/lib/assessment-types';
|
|
import {
|
|
decryptAnonymousRecord,
|
|
encryptRecordForAnonymousProfile,
|
|
} from '@/lib/record-crypto';
|
|
|
|
const SESSION_KEY = 'mindscope_anonymous_session';
|
|
|
|
export interface AnonymousSession {
|
|
codeName: string;
|
|
password: string;
|
|
}
|
|
|
|
export interface AnonymousProfile {
|
|
id: string;
|
|
codeName: string;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
lastSeenAt: string;
|
|
}
|
|
|
|
interface AnonymousEncryptedLoginResult {
|
|
profile: AnonymousProfile;
|
|
records: EncryptedAssessmentRecord[];
|
|
}
|
|
|
|
export interface AnonymousLoginResult {
|
|
profile: AnonymousProfile;
|
|
records: AssessmentRecord[];
|
|
}
|
|
|
|
async function parseResponse<T>(response: Response): Promise<T> {
|
|
const data = await response.json().catch(() => ({}));
|
|
if (!response.ok) {
|
|
throw new Error(typeof data.error === 'string' ? data.error : '请求失败');
|
|
}
|
|
return data as T;
|
|
}
|
|
|
|
export function getAnonymousSession(): AnonymousSession | null {
|
|
try {
|
|
const raw = sessionStorage.getItem(SESSION_KEY);
|
|
if (!raw) return null;
|
|
const parsed = JSON.parse(raw) as Partial<AnonymousSession>;
|
|
return parsed.codeName && parsed.password
|
|
? { codeName: parsed.codeName, password: parsed.password }
|
|
: null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function saveAnonymousSession(session: AnonymousSession) {
|
|
sessionStorage.setItem(SESSION_KEY, JSON.stringify(session));
|
|
}
|
|
|
|
export function clearAnonymousSession() {
|
|
sessionStorage.removeItem(SESSION_KEY);
|
|
}
|
|
|
|
async function decryptLoginResult(
|
|
encryptedResult: AnonymousEncryptedLoginResult,
|
|
session: AnonymousSession,
|
|
): Promise<AnonymousLoginResult> {
|
|
const records = await Promise.all(
|
|
encryptedResult.records.map((record) => decryptAnonymousRecord(record, session.codeName, session.password)),
|
|
);
|
|
return {
|
|
profile: encryptedResult.profile,
|
|
records: records.sort((a, b) => b.completedAt.localeCompare(a.completedAt)),
|
|
};
|
|
}
|
|
|
|
export async function loginAnonymousProfile(session: AnonymousSession) {
|
|
const encryptedResult = await fetch('/api/anonymous/profile', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(session),
|
|
}).then((response) => parseResponse<AnonymousEncryptedLoginResult>(response));
|
|
saveAnonymousSession(session);
|
|
return decryptLoginResult(encryptedResult, session);
|
|
}
|
|
|
|
export async function syncAnonymousRecord(record: AssessmentRecord, session = getAnonymousSession()) {
|
|
if (!session) return null;
|
|
const encryptedRecord = await encryptRecordForAnonymousProfile(record, session.codeName, session.password);
|
|
const result = await fetch('/api/anonymous/records', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ ...session, encryptedRecord }),
|
|
}).then((response) => parseResponse<{ record: EncryptedAssessmentRecord }>(response));
|
|
return decryptAnonymousRecord(result.record, session.codeName, session.password);
|
|
}
|
|
|
|
export async function deleteAnonymousProfile(session: AnonymousSession) {
|
|
const result = await fetch('/api/anonymous/profile', {
|
|
method: 'DELETE',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(session),
|
|
}).then((response) => parseResponse<{ ok: boolean }>(response));
|
|
clearAnonymousSession();
|
|
return result;
|
|
}
|