chore: save current changes

This commit is contained in:
liaibo
2025-11-23 20:04:29 +08:00
parent 994dd4e67c
commit 5d0e4c4cb6
10 changed files with 695 additions and 191 deletions
@@ -1,5 +1,6 @@
import { NextResponse } from "next/server";
import { createSupabaseRouteClient } from "@/lib/supabase/server";
import type { DocumentTableSnapshot, TableRowData } from "@/types/online-table";
interface RouteContext {
params: {
@@ -7,19 +8,43 @@ interface RouteContext {
};
}
export async function GET(request: Request, context: RouteContext) {
type UpdateTableRequest = {
title?: string;
snapshot?: DocumentTableSnapshot | null;
rows?: TableRowData[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
viewPreferences?: Record<string, any>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
schema?: Record<string, any>;
};
const extractTableId = async (context: RouteContext) => {
// Next.js 16 / Turbopack 下 params 是 Promise,需要等待。
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (await (context as any).params).tableId as string;
};
const getAuthUser = async () => {
const supabase = await createSupabaseRouteClient();
const { data: { user } } = await supabase.auth.getUser();
const { data, error } = await supabase.auth.getUser();
if (error) {
console.error("Auth getUser error:", error);
}
if (!data?.user) {
return { supabase, user: null as const };
}
return { supabase, user: data.user };
};
export async function GET(request: Request, context: RouteContext) {
const { supabase, user } = await getAuthUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
// 强制 await params,以解决 Next.js 16/Turbopack 错误。
// 必须忽略类型检查,因为 Next.js 16/Turbopack 在运行时将 params 视为 Promise。
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const tableId = (await (context as any).params).tableId;
const tableId = await extractTableId(context);
if (!tableId) {
return NextResponse.json({ error: "Missing tableId" }, { status: 400 });
@@ -49,3 +74,160 @@ export async function GET(request: Request, context: RouteContext) {
return NextResponse.json({ error: "Internal Server Error" }, { status: 500 });
}
}
export async function PATCH(request: Request, context: RouteContext) {
const { supabase, user } = await getAuthUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const tableId = await extractTableId(context);
if (!tableId) {
return NextResponse.json({ error: "Missing tableId" }, { status: 400 });
}
let body: UpdateTableRequest | null = null;
try {
body = await request.json() as UpdateTableRequest;
} catch (error) {
console.error("Invalid payload:", error);
return NextResponse.json({ error: "Invalid payload" }, { status: 400 });
}
try {
const { data: tableMeta, error: metaError } = await supabase
.from("document_tables")
.select("id, workspace_id, document_id")
.eq("id", tableId)
.single();
if (metaError || !tableMeta) {
console.error("Table not found or load error:", metaError);
return NextResponse.json({ error: "Table not found" }, { status: 404 });
}
const updatePayload: Record<string, unknown> = {
updated_by: user.id,
last_synced_at: new Date().toISOString(),
};
if (body?.title !== undefined) {
updatePayload.title = body.title;
}
if (body?.schema) {
updatePayload.schema = body.schema;
}
if (body?.snapshot !== undefined) {
updatePayload.snapshot = body.snapshot ?? {};
}
if (body?.viewPreferences) {
updatePayload.view_preferences = body.viewPreferences;
}
const { error: updateError } = await supabase
.from("document_tables")
.update(updatePayload)
.eq("id", tableId);
if (updateError) {
console.error("Failed to update table:", updateError);
return NextResponse.json({ error: "Failed to update table", details: updateError.message }, { status: 500 });
}
const nextRows = body?.rows ?? body?.snapshot?.rows ?? [];
if (Array.isArray(nextRows)) {
const { error: deleteRowsError } = await supabase
.from("document_table_rows")
.delete()
.eq("table_id", tableId);
if (deleteRowsError) {
console.error("Failed to clear old rows:", deleteRowsError);
return NextResponse.json({ error: "Failed to update rows", details: deleteRowsError.message }, { status: 500 });
}
const sanitizedRows = nextRows
.filter((row) => row && typeof row === "object" && Object.keys(row).length > 0);
if (sanitizedRows.length > 0) {
const rowsToInsert = sanitizedRows.map((row, index) => ({
table_id: tableId,
workspace_id: tableMeta.workspace_id,
document_id: tableMeta.document_id,
row_index: index,
row_data: row,
row_hash: null,
is_deleted: false,
updated_by: user.id,
}));
const { error: insertError } = await supabase
.from("document_table_rows")
.insert(rowsToInsert);
if (insertError) {
console.error("Failed to insert rows:", insertError);
return NextResponse.json({ error: "Failed to insert rows", details: insertError.message }, { status: 500 });
}
}
}
const { data: latest, error: fetchError } = await supabase
.from("document_tables")
.select("*")
.eq("id", tableId)
.single();
if (fetchError || !latest) {
console.error("Failed to fetch updated table:", fetchError);
return NextResponse.json({ error: "Failed to load updated table", details: fetchError?.message }, { status: 500 });
}
return NextResponse.json(latest, { status: 200 });
} catch (error) {
console.error("API error:", error);
const message = error instanceof Error ? error.message : "Internal Server Error";
return NextResponse.json({ error: message }, { status: 500 });
}
}
export async function DELETE(request: Request, context: RouteContext) {
const { supabase, user } = await getAuthUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const tableId = await extractTableId(context);
if (!tableId) {
return NextResponse.json({ error: "Missing tableId" }, { status: 400 });
}
try {
const { error: deleteRowsError } = await supabase
.from("document_table_rows")
.delete()
.eq("table_id", tableId);
if (deleteRowsError) {
console.error("Failed to delete table rows:", deleteRowsError);
return NextResponse.json({ error: "Failed to delete table rows" }, { status: 500 });
}
const { error: deleteTableError } = await supabase
.from("document_tables")
.delete()
.eq("id", tableId);
if (deleteTableError) {
console.error("Failed to delete table:", deleteTableError);
return NextResponse.json({ error: "Failed to delete table" }, { status: 500 });
}
return NextResponse.json({ success: true }, { status: 200 });
} catch (error) {
console.error("API error:", error);
return NextResponse.json({ error: "Internal Server Error" }, { status: 500 });
}
}
@@ -48,6 +48,8 @@ export async function POST(request: Request) {
document_id: documentId,
title: title,
schema: schema,
view_preferences: {},
is_archived: false,
created_by: user.id,
updated_by: user.id,
snapshot: snapshot ?? {},