fix: save profiles through account route

This commit is contained in:
732642856
2026-07-18 12:01:41 +08:00
parent 681df09f83
commit 0456c0b5cc
3 changed files with 85 additions and 11 deletions
+69 -1
View File
@@ -1,5 +1,5 @@
import { NextResponse } from "next/server";
import { isAdminEmail } from "@/lib/supabase/admin";
import { createAdminSupabaseClient, isAdminEmail } from "@/lib/supabase/admin";
import {
isSupabaseConfigurationError,
} from "@/lib/supabase/config";
@@ -7,6 +7,27 @@ import { createServerSupabaseClient } from "@/lib/supabase/server";
export const runtime = "nodejs";
type ProfilePatchPayload = {
name?: unknown;
birth_date?: unknown;
birth_time?: unknown;
country_code?: unknown;
province_code?: unknown;
city_code?: unknown;
district_code?: unknown;
latitude?: unknown;
longitude?: unknown;
timezone_offset?: unknown;
};
function nullableString(value: unknown) {
return typeof value === "string" && value.trim() ? value.trim() : null;
}
function nullableNumber(value: unknown) {
return typeof value === "number" && Number.isFinite(value) ? value : null;
}
export async function GET() {
try {
const supabase = await createServerSupabaseClient();
@@ -38,3 +59,50 @@ export async function GET() {
return NextResponse.json({ error: "账户服务暂时不可用" }, { status: 500 });
}
}
export async function PATCH(request: Request) {
try {
const supabase = await createServerSupabaseClient();
const { data: { user }, error: authError } = await supabase.auth.getUser();
if (authError || !user) {
return NextResponse.json({ error: "请先登录" }, { status: 401 });
}
const payload = await request.json().catch(() => null) as ProfilePatchPayload | null;
if (!payload || typeof payload !== "object") {
return NextResponse.json({ error: "账户资料格式不正确" }, { status: 400 });
}
const admin = createAdminSupabaseClient();
const { data, error } = await admin
.from("profiles")
.upsert({
id: user.id,
name: nullableString(payload.name),
birth_date: nullableString(payload.birth_date),
birth_time: nullableString(payload.birth_time),
country_code: nullableString(payload.country_code),
province_code: nullableString(payload.province_code),
city_code: nullableString(payload.city_code),
district_code: nullableString(payload.district_code),
latitude: nullableNumber(payload.latitude),
longitude: nullableNumber(payload.longitude),
timezone_offset: nullableNumber(payload.timezone_offset),
updated_at: new Date().toISOString(),
}, { onConflict: "id" })
.select("id")
.single();
if (error || !data) {
return NextResponse.json({ error: "暂时无法保存账户资料" }, { status: 500 });
}
return NextResponse.json({ ok: true });
} catch (error) {
if (isSupabaseConfigurationError(error)) {
return NextResponse.json({ error: "Supabase 尚未配置", code: "SUPABASE_NOT_CONFIGURED" }, { status: 503 });
}
return NextResponse.json({ error: "账户服务暂时不可用" }, { status: 500 });
}
}
+10 -10
View File
@@ -1320,10 +1320,10 @@ export default function Home() {
if (!account) throw new Error("账户尚未加载完成");
if (process.env.NODE_ENV === "development" && uiPreview.current) return;
const birthPlace = selectedBirthPlace(nextProfile);
const { data, error } = await createBrowserSupabaseClient()
.from("profiles")
.upsert({
id: account.user.id,
const response = await fetch("/api/account", {
method: "PATCH",
headers: { "content-type": "application/json" },
body: JSON.stringify({
name: nextProfile.name.trim() || null,
birth_date: nextProfile.date || null,
birth_time: nextProfile.time || null,
@@ -1334,12 +1334,12 @@ export default function Home() {
latitude: birthPlace?.lat ?? null,
longitude: birthPlace?.lon ?? null,
timezone_offset: birthPlace?.tz ?? null,
updated_at: new Date().toISOString(),
}, { onConflict: "id" })
.select("id")
.maybeSingle();
if (error) throw error;
if (!data) throw new Error("账户档案不存在,请重新登录后再试。");
}),
});
if (!response.ok) {
const payload = await response.json().catch(() => null) as { error?: string } | null;
throw new Error(payload?.error || "账户资料暂时无法保存。");
}
await saveCloudChartProfile({ ...buildSelfChartRecord(nextProfile), updatedAt: timestamp() }).catch(() => null);
}
@@ -4,5 +4,11 @@ import test from "node:test";
test("upserts a missing profile when saving account details", () => {
const source = readFileSync(new URL("../src/app/page.tsx", import.meta.url), "utf8");
assert.match(source, /fetch\("\/api\/account",\s*\{\s*method:\s*"PATCH"/s);
});
test("account route upserts profiles with the server admin client", () => {
const source = readFileSync(new URL("../src/app/api/account/route.ts", import.meta.url), "utf8");
assert.match(source, /createAdminSupabaseClient\(\)/);
assert.match(source, /\.from\("profiles"\)\s*\.upsert\(/);
});