import { NextResponse } from "next/server"; import { libraryProfileFromSubjectRow, parseChartSubjectWrite, subjectLimitReached, } from "@/lib/chart-subject-write"; import { CHART_SUBJECT_API_SELECT, MAX_SAVED_SUBJECTS } from "@/lib/subject-birth"; import { scheduleConsultPacketWarm } from "@/lib/consult-packet-warm"; import { createServerSupabaseClient } from "@/lib/supabase/server"; import { isSupabaseConfigurationError } from "@/lib/supabase/config"; const SUBJECT_LIMIT_MESSAGE = "最多保存 5 个人的星盘"; function errorMessage(error: unknown, fallback: string) { return error && typeof error === "object" && "message" in error && typeof error.message === "string" ? error.message : fallback; } function databaseFailure(error: unknown): NextResponse | null { const message = errorMessage(error, ""); if (message.includes("subject_limit_reached")) { return NextResponse.json({ error: SUBJECT_LIMIT_MESSAGE, code: "subject_limit_reached" }, { status: 409 }); } if (message.includes("reported_birth_time_is_immutable")) { return NextResponse.json({ error: "填报的出生时间不能再改", code: "reported_birth_time_is_immutable" }, { status: 409 }); } if (message.includes("chart_subject_active_birth_is_locked") || message.includes("chart_subject_birth_status_locked")) { return NextResponse.json({ error: "不能直接写入排盘采用时间", code: "active_birth_locked" }, { status: 403 }); } if (message.includes("chart_subject_self_mirror_retired")) { return NextResponse.json({ error: "本人资料保存在账户里", code: "self_mirror_retired" }, { status: 409 }); } return null; } function toClientProfile(row: Record) { return { id: row.id, role: "other", profile: libraryProfileFromSubjectRow(row), updated_at: row.updated_at, }; } export async function GET() { try { const supabase = await createServerSupabaseClient(); const { data: { user } } = await supabase.auth.getUser(); if (!user) return NextResponse.json({ error: "请先登录" }, { status: 401 }); const { data, error } = await supabase .from("chart_profiles") .select(CHART_SUBJECT_API_SELECT) .eq("user_id", user.id) .eq("role", "other") .order("updated_at", { ascending: false }); if (error) throw error; const profiles = (data ?? []).map((row: unknown) => toClientProfile(row as Record)); return NextResponse.json({ profiles, limit: MAX_SAVED_SUBJECTS, atLimit: subjectLimitReached(profiles.length), }); } catch (error) { if (isSupabaseConfigurationError(error)) { return NextResponse.json({ error: "数据库尚未配置", code: "DATABASE_NOT_CONFIGURED" }, { status: 503 }); } return NextResponse.json({ error: errorMessage(error, "星盘库暂时不可用") }, { status: 500 }); } } export async function POST(request: Request) { try { const supabase = await createServerSupabaseClient(); const { data: { user } } = await supabase.auth.getUser(); if (!user) return NextResponse.json({ error: "请先登录" }, { status: 401 }); const body = await request.json().catch(() => null) as { role?: unknown; profile?: unknown } | null; if (body?.role === "self") { return NextResponse.json({ error: "本人资料保存在账户里", code: "self_mirror_retired" }, { status: 409 }); } const parsed = parseChartSubjectWrite(body?.profile); if (!parsed.ok) return NextResponse.json({ error: parsed.error }, { status: 400 }); const existing = await supabase .from("chart_profiles") .select("id") .eq("user_id", user.id) .eq("role", "other") .limit(MAX_SAVED_SUBJECTS - 1); if (existing.error) throw existing.error; if (subjectLimitReached(Array.isArray(existing.data) ? existing.data.length : 0)) { return NextResponse.json({ error: SUBJECT_LIMIT_MESSAGE, code: "subject_limit_reached" }, { status: 409 }); } const { data, error } = await supabase .from("chart_profiles") .insert({ user_id: user.id, role: "other", profile: parsed.write.profile, updated_at: new Date().toISOString(), ...parsed.write.columns, }) .select(CHART_SUBJECT_API_SELECT) .single(); if (error) { const mapped = databaseFailure(error); if (mapped) return mapped; throw error; } scheduleConsultPacketWarm(parsed.write.columns); return NextResponse.json({ profile: toClientProfile(data as Record) }); } catch (error) { if (isSupabaseConfigurationError(error)) { return NextResponse.json({ error: "数据库尚未配置", code: "DATABASE_NOT_CONFIGURED" }, { status: 503 }); } const mapped = databaseFailure(error); if (mapped) return mapped; return NextResponse.json({ error: errorMessage(error, "星盘保存失败") }, { status: 500 }); } }