0.3 增加登录模块

This commit is contained in:
liaibo
2026-01-18 05:13:53 +08:00
parent df2e9f5339
commit 90a38b48cf
86 changed files with 2041 additions and 148 deletions
@@ -28,13 +28,11 @@ export async function POST(request: Request) {
return NextResponse.json({ error: "缺少 requestId/callId" }, { status: 400 });
}
const userId = (() => {
if (isConvexEnabled()) {
const auth = requireAuthContext();
return auth.userId;
}
return null;
})();
let userId: string | null = null;
if (isConvexEnabled()) {
const auth = await requireAuthContext();
userId = auth.userId;
}
const resolvedUserId = async () => {
if (userId) return userId;
@@ -77,7 +77,7 @@ export async function POST(request: Request) {
const convexOn = isConvexEnabled();
const { userId, supabase, convexClient } = await (async () => {
if (convexOn) {
const { auth, client } = getAuthedConvexClient();
const { auth, client } = await getAuthedConvexClient();
return { userId: auth.userId, supabase: null as any, convexClient: client };
}
const supabase = await createSupabaseRouteClient();
@@ -0,0 +1,110 @@
import { NextResponse } from "next/server";
import { isConvexEnabled } from "@/lib/convex/enabled";
interface SignInRequest {
email: string;
password: string;
name?: string;
flow: "signIn" | "signUp";
}
/**
* POST /api/auth/signin
*
* 处理登录/注册请求
* - 在 Convex 模式下调用 Convex mutation
* - 在 Supabase 模式下调用 Supabase Auth
*/
export async function POST(request: Request) {
// Convex 模式
if (isConvexEnabled()) {
try {
const body = await request.json() as SignInRequest;
const { email, password, name, flow } = body;
// TODO: 调用 Convex Auth mutation 处理登录/注册
// 当前暂时返回未实现错误
return NextResponse.json(
{ error: "Convex Auth API 路由暂未实现,请稍后实现" },
{ status: 501 }
);
} catch (error) {
console.error("Convex Auth error:", error);
return NextResponse.json(
{ error: "服务器错误" },
{ status: 500 }
);
}
}
// Supabase 模式(保留兼容)
try {
const { createSupabaseRouteClient } = await import("@/lib/supabase/server");
const supabase = await createSupabaseRouteClient();
const body = await request.json() as SignInRequest;
const { email, password, name, flow } = body;
if (flow === "signUp") {
// 注册
const { data, error } = await supabase.auth.signUp({
email,
password,
options: {
data: {
name: name ?? "",
},
},
});
if (error) {
return NextResponse.json(
{ error: error.message },
{ status: 400 }
);
}
// 注册成功后自动登录
const { error: signInError } = await supabase.auth.signInWithPassword({
email,
password,
});
if (signInError) {
return NextResponse.json(
{ error: signInError.message },
{ status: 401 }
);
}
return NextResponse.json(
{ message: "注册并登录成功" },
{ status: 200 }
);
} else {
// 登录
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
});
if (error) {
return NextResponse.json(
{ error: error.message },
{ status: 401 }
);
}
return NextResponse.json(
{ message: "登录成功" },
{ status: 200 }
);
}
} catch (error) {
console.error("Supabase Auth error:", error);
return NextResponse.json(
{ error: "服务器错误" },
{ status: 500 }
);
}
}
@@ -26,7 +26,7 @@ export async function POST(request: Request) {
}
if (isConvexEnabled()) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const client = getConvexHttpClient();
const source = await client.query(api.documents.getContent, { userId: auth.userId, id: sourceDocumentId });
@@ -113,4 +113,3 @@ export async function POST(request: Request) {
if (error) return NextResponse.json({ error: error.message }, { status: 500 });
return NextResponse.json({ ok: true });
}
@@ -16,7 +16,7 @@ export async function GET(request: Request) {
}
if (isConvexEnabled()) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const client = getConvexHttpClient();
const doc = await client.query(api.documents.getContent, { userId: auth.userId, id: sourceDocumentId });
if (!doc) return NextResponse.json({ error: "页面不存在或无权限" }, { status: 404 });
@@ -46,4 +46,3 @@ export async function GET(request: Request) {
return NextResponse.json({ ok: true, block: hit.block });
}
@@ -26,7 +26,7 @@ export async function POST(request: Request) {
}
if (isConvexEnabled()) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const client = getConvexHttpClient();
const source = await client.query(api.documents.getContent, { userId: auth.userId, id: sourceDocumentId });
@@ -97,4 +97,3 @@ export async function POST(request: Request) {
return NextResponse.json({ ok: true });
}
@@ -20,7 +20,7 @@ export async function POST(request: Request) {
}
if (isConvexEnabled()) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const client = getConvexHttpClient();
const doc = await client.query(api.documents.getContent, { userId: auth.userId, id: sourceDocumentId });
if (!doc) return NextResponse.json({ error: "页面不存在或无权限" }, { status: 404 });
@@ -62,4 +62,3 @@ export async function POST(request: Request) {
return NextResponse.json({ ok: true });
}
@@ -0,0 +1,46 @@
import { NextResponse } from "next/server";
import { requireAuthContext } from "@/lib/auth/authContext";
import { getConvexHttpClient } from "@/lib/convex/server";
import { api } from "@/lib/convex/api";
export const dynamic = "force-dynamic";
type Payload =
| { type: "document"; documentId: string }
| { type: "mindmap"; docId: string; mindmapId: string }
| { type: "media"; assetId: string };
export async function POST(request: Request) {
const auth = await requireAuthContext();
const client = getConvexHttpClient();
const body = (await request.json().catch(() => null)) as Payload | null;
if (!body) {
return NextResponse.json({ error: "缺少请求体" }, { status: 400 });
}
if (body.type === "document") {
const documentId = String(body.documentId ?? "").trim();
if (!documentId) return NextResponse.json({ error: "缺少 documentId" }, { status: 400 });
const result = await client.mutation(api.jobs.enqueueRagIndexDocument, { userId: auth.userId, documentId });
return NextResponse.json(result);
}
if (body.type === "mindmap") {
const docId = String(body.docId ?? "").trim();
const mindmapId = String(body.mindmapId ?? "").trim();
if (!docId) return NextResponse.json({ error: "缺少 docId" }, { status: 400 });
if (!mindmapId) return NextResponse.json({ error: "缺少 mindmapId" }, { status: 400 });
const result = await client.mutation(api.jobs.enqueueRagIndexMindmap, { userId: auth.userId, docId, mindmapId });
return NextResponse.json(result);
}
if (body.type === "media") {
const assetId = String(body.assetId ?? "").trim();
if (!assetId) return NextResponse.json({ error: "缺少 assetId" }, { status: 400 });
const result = await client.mutation(api.jobs.enqueueRagIndexMediaAsset, { userId: auth.userId, assetId });
return NextResponse.json(result);
}
return NextResponse.json({ error: "不支持的 type" }, { status: 400 });
}
@@ -7,7 +7,7 @@ import { api } from "@/lib/convex/api";
export const dynamic = "force-dynamic";
export async function POST(request: Request) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const client = getConvexHttpClient();
const { ms }: { ms?: number } = await request.json().catch(() => ({}));
@@ -23,7 +23,7 @@ export async function POST(request: Request) {
}
export async function GET(request: Request) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const client = getConvexHttpClient();
const url = new URL(request.url);
@@ -39,4 +39,3 @@ export async function GET(request: Request) {
return NextResponse.json(job);
}
@@ -6,7 +6,7 @@ export const dynamic = "force-dynamic";
export async function GET() {
try {
const auth = requireAuthContext();
const auth = await requireAuthContext();
return NextResponse.json({ ok: true, auth }, { status: 200 });
} catch (err) {
const status = err instanceof HttpError ? err.status : 500;
@@ -9,7 +9,7 @@ export const dynamic = "force-dynamic";
export async function GET(request: Request) {
if (isConvexEnabled()) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const url = new URL(request.url);
const documentId = url.searchParams.get("documentId") ?? "";
@@ -163,7 +163,7 @@ function replaceAssetRefsInContent(content: Json | null, assetMap: Map<string, {
export async function POST(request: Request) {
if (isConvexEnabled()) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const client = getConvexHttpClient();
const payload = (await request.json()) as CopyTreePayload;
@@ -22,7 +22,7 @@ export const dynamic = "force-dynamic";
export async function POST(request: Request) {
if (isConvexEnabled()) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const client = getConvexHttpClient();
const { parentId, title, blocks }: CreateChildPayload = await request.json();
@@ -41,7 +41,7 @@ export async function POST(request: Request) {
}
async function handleCreateRequestConvex(request: Request) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const client = getConvexHttpClient();
const { parentId }: { parentId?: string | null } = await request.json();
@@ -9,7 +9,7 @@ export const dynamic = "force-dynamic";
export async function POST(request: Request) {
if (isConvexEnabled()) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const { documentId } = await request.json();
if (!documentId) {
return NextResponse.json({ error: "缺少 documentId" }, { status: 400 });
@@ -48,7 +48,7 @@ async function copyMindmapIfExists(sourceId: string, targetId: string) {
export async function POST(request: Request) {
if (isConvexEnabled()) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const client = getConvexHttpClient();
const { documentId }: DuplicatePayload = await request.json();
@@ -14,7 +14,7 @@ interface EmbedPayload {
export async function POST(request: Request) {
if (isConvexEnabled()) {
const { auth, client } = getAuthedConvexClient();
const { auth, client } = await getAuthedConvexClient();
const { sourceId, targetId }: EmbedPayload = await request.json();
@@ -13,7 +13,7 @@ interface EmptyTrashPayload {
export async function POST(request: Request) {
if (isConvexEnabled()) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const { workspaceId }: EmptyTrashPayload = await request.json();
if (!workspaceId) {
return NextResponse.json({ error: "缺少 workspaceId" }, { status: 400 });
@@ -13,7 +13,7 @@ interface MovePayload {
export async function POST(request: Request) {
if (isConvexEnabled()) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const { documentId, parentId = null, position }: MovePayload = await request.json();
const sortOrder = Number.isFinite(position) ? Math.floor(position) : 0;
const client = getConvexHttpClient();
@@ -24,7 +24,7 @@ const COLUMN_MAP: Record<keyof PageOptionsState, keyof Database["public"]["Table
export async function POST(request: Request) {
if (isConvexEnabled()) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const { documentId, options }: OptionsPayload = await request.json();
if (!documentId || !options) {
return NextResponse.json({ error: "缺少参数" }, { status: 400 });
@@ -9,7 +9,7 @@ export const dynamic = "force-dynamic";
export async function POST(request: Request) {
if (isConvexEnabled()) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const { documentId } = await request.json();
if (!documentId) {
return NextResponse.json({ error: "缺少 documentId" }, { status: 400 });
@@ -9,7 +9,7 @@ export const dynamic = "force-dynamic";
export async function POST(request: Request) {
if (isConvexEnabled()) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const { documentId } = await request.json();
if (!documentId) {
return NextResponse.json({ error: "缺少 documentId" }, { status: 400 });
@@ -12,7 +12,7 @@ interface SavePayload {
export async function POST(request: Request) {
if (isConvexEnabled()) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const { documentId, content }: SavePayload = await request.json();
const client = getConvexHttpClient();
await client.mutation(api.documents.updateContent, {
@@ -13,7 +13,7 @@ interface StatsPayload {
export async function POST(request: Request) {
if (isConvexEnabled()) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const { documentId, stats }: StatsPayload = await request.json();
if (!documentId || !stats) {
return NextResponse.json({ error: "缺少参数" }, { status: 400 });
@@ -12,7 +12,7 @@ interface RenamePayload {
export async function POST(request: Request) {
if (isConvexEnabled()) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const { documentId, title }: RenamePayload = await request.json();
const client = getConvexHttpClient();
await client.mutation(api.documents.updateTitle, {
@@ -2,6 +2,9 @@
import { NextResponse } from "next/server";
import { createSupabaseRouteClient } from "@/lib/supabase/server";
import { isConvexEnabled } from "@/lib/convex/enabled";
import { getConvexHttpClient } from "@/lib/convex/server";
import { api } from "@/lib/convex/api";
export async function GET(request: Request) {
const url = new URL(request.url);
@@ -11,6 +14,28 @@ export async function GET(request: Request) {
return NextResponse.json({ code: 400, msg: "gridKey 参数缺失" }, { status: 400 });
}
// Convex 模式
if (isConvexEnabled()) {
try {
const client = getConvexHttpClient();
const result = await client.query(api.tables.getByGridKey, { gridKey });
if (!result) {
return NextResponse.json({ code: 404, msg: "未查询到相关数据" }, { status: 404 });
}
return NextResponse.json({
code: 200,
msg: "ok",
data: result,
});
} catch (error) {
console.error("Convex API error:", error);
return NextResponse.json({ code: 500, msg: "服务器错误" }, { status: 500 });
}
}
// Supabase 模式
const supabase = await createSupabaseRouteClient();
const { data, error } = await supabase
.from("document_tables")
@@ -4,6 +4,9 @@ import { NextResponse } from "next/server";
import { createDefaultTableSnapshot } from "@/lib/online-table";
import { createSupabaseRouteClient } from "@/lib/supabase/server";
import type { DocumentTableSnapshot, TableSchema } from "@/types/online-table";
import { isConvexEnabled } from "@/lib/convex/enabled";
import { getConvexHttpClient } from "@/lib/convex/server";
import { api } from "@/lib/convex/api";
const fallbackSchema: TableSchema = {
columns: [],
@@ -19,6 +22,38 @@ export async function POST(request: Request) {
return NextResponse.json({ code: 400, msg: "gridKey 参数缺失" }, { status: 400 });
}
// Convex 模式
if (isConvexEnabled()) {
try {
const client = getConvexHttpClient();
const tableData = await client.query(api.tables.getByGridKeyFull, { gridKey });
if (!tableData) {
return NextResponse.json({ code: 404, msg: "未查询到相关数据" }, { status: 404 });
}
const snapshot = (tableData.snapshot as DocumentTableSnapshot | null) ?? null;
const schema = (tableData.schema as TableSchema | null) ?? fallbackSchema;
let payload: unknown[] = [];
if (snapshot?.luckysheet && Array.isArray(snapshot.luckysheet)) {
payload = snapshot.luckysheet;
} else {
const defaultSnapshot = createDefaultTableSnapshot(schema);
payload = defaultSnapshot.luckysheet ?? [];
}
const body = JSON.stringify(payload);
return new NextResponse(body, {
headers: { "Content-Type": "text/plain; charset=utf-8" },
});
} catch (error) {
console.error("Convex API error:", error);
return NextResponse.json({ code: 500, msg: "服务器错误" }, { status: 500 });
}
}
// Supabase 模式
const supabase = await createSupabaseRouteClient();
const { data, error } = await supabase
.from("document_tables")
@@ -12,7 +12,7 @@ export async function GET(request: Request) {
if (isConvexEnabled()) {
let auth;
try {
auth = requireAuthContext();
auth = await requireAuthContext();
} catch (err) {
if (err instanceof HttpError) {
return NextResponse.json({ error: "未登录" }, { status: err.status });
@@ -85,7 +85,7 @@ export async function POST(request: Request) {
if (isConvexEnabled()) {
let auth;
try {
auth = requireAuthContext();
auth = await requireAuthContext();
} catch (err) {
if (err instanceof HttpError) {
return NextResponse.json({ error: "未登录" }, { status: err.status });
@@ -71,7 +71,7 @@ export async function POST(request: Request) {
if (isConvexEnabled()) {
let auth;
try {
auth = requireAuthContext();
auth = await requireAuthContext();
} catch (err) {
if (err instanceof HttpError) {
return NextResponse.json({ error: "未登录" }, { status: err.status });
@@ -32,7 +32,7 @@ export async function POST(request: Request) {
if (isConvexEnabled()) {
let auth;
try {
auth = requireAuthContext();
auth = await requireAuthContext();
} catch (err) {
if (err instanceof HttpError) {
return NextResponse.json({ error: "未登录" }, { status: err.status });
@@ -32,7 +32,7 @@ export async function POST(request: Request) {
if (isConvexEnabled()) {
let auth;
try {
auth = requireAuthContext();
auth = await requireAuthContext();
} catch (err) {
if (err instanceof HttpError) {
return NextResponse.json({ error: "未登录" }, { status: err.status });
@@ -68,7 +68,7 @@ export async function GET(request: Request) {
if (isConvexEnabled()) {
let auth;
try {
auth = requireAuthContext();
auth = await requireAuthContext();
} catch (err) {
if (err instanceof HttpError) {
return NextResponse.json({ error: "未登录" }, { status: err.status });
@@ -98,7 +98,7 @@ export async function GET(request: Request) {
// 说明:Convex + MinIO 模式下,这个接口目前主要用于“外链文件”走 ONLYOFFICE 的场景。
// 由于外链本身已经是可访问的 URL,这里只做最小透传。
try {
requireAuthContext();
await requireAuthContext();
} catch (err) {
if (err instanceof HttpError) {
return NextResponse.json({ error: "未登录" }, { status: err.status });
@@ -24,7 +24,7 @@ export async function POST(request: Request) {
if (isConvexEnabled()) {
let auth;
try {
auth = requireAuthContext();
auth = await requireAuthContext();
} catch (err) {
if (err instanceof HttpError) {
return NextResponse.json({ error: "未登录" }, { status: err.status });
@@ -33,7 +33,7 @@ async function purgeTrashFolder(folder: string): Promise<number> {
export async function POST(request: Request) {
if (isConvexEnabled()) {
const { auth, client } = getAuthedConvexClient();
const { auth, client } = await getAuthedConvexClient();
const { workspaceId }: EmptyTrashPayload = await request.json().catch(() => ({}));
if (!workspaceId) {
return NextResponse.json({ error: "缺少 workspaceId" }, { status: 400 });
@@ -24,7 +24,7 @@ export async function POST(
const { docId, mindmapId } = await params;
if (isConvexEnabled()) {
const { auth, client } = getAuthedConvexClient();
const { auth, client } = await getAuthedConvexClient();
const payload = (await request.json().catch(() => null)) as RequestPayload | null;
const ops = Array.isArray(payload?.ops) ? payload!.ops : [];
@@ -117,7 +117,7 @@ export async function GET(
const { docId, mindmapId } = await params;
if (isConvexEnabled()) {
const { auth, client } = getAuthedConvexClient();
const { auth, client } = await getAuthedConvexClient();
const res = await client.query(api.mindmaps.get, {
userId: auth.userId,
docId,
@@ -171,7 +171,7 @@ export async function POST(
const { docId, mindmapId } = await params;
if (isConvexEnabled()) {
const { auth, client } = getAuthedConvexClient();
const { auth, client } = await getAuthedConvexClient();
const { data, createOnly } = (await request.json().catch(() => ({ data: null }))) as {
data?: unknown;
createOnly?: boolean;
@@ -241,7 +241,7 @@ export async function DELETE(
const { docId, mindmapId } = await params;
if (isConvexEnabled()) {
const { auth, client } = getAuthedConvexClient();
const { auth, client } = await getAuthedConvexClient();
try {
const result = await client.mutation(api.mindmaps.softDelete, {
userId: auth.userId,
@@ -316,7 +316,7 @@ export async function PATCH(
const { docId, mindmapId } = await params;
if (isConvexEnabled()) {
const { auth, client } = getAuthedConvexClient();
const { auth, client } = await getAuthedConvexClient();
const { action } = (await request.json().catch(() => ({}))) as { action?: string };
if (action !== "restore" && action !== "purge") {
return NextResponse.json({ error: "不支持的操作" }, { status: 400 });
@@ -45,7 +45,7 @@ export async function GET(
const { docId: id } = await params;
if (isConvexEnabled()) {
const { auth, client } = getAuthedConvexClient();
const { auth, client } = await getAuthedConvexClient();
const mindmapId = `legacy-${id}`;
const res = await client.query(api.mindmaps.get, {
userId: auth.userId,
@@ -106,7 +106,7 @@ export async function POST(
const { docId: id } = await params;
if (isConvexEnabled()) {
const { auth, client } = getAuthedConvexClient();
const { auth, client } = await getAuthedConvexClient();
const mindmapId = `legacy-${id}`;
const { data } = await request.json();
const result = await client.mutation(api.mindmaps.put, {
@@ -161,7 +161,7 @@ export async function DELETE(
const { docId: id } = await params;
if (isConvexEnabled()) {
const { auth, client } = getAuthedConvexClient();
const { auth, client } = await getAuthedConvexClient();
const mindmapId = `legacy-${id}`;
const result = await client.mutation(api.mindmaps.softDelete, {
userId: auth.userId,
@@ -6,7 +6,7 @@ import { api } from "@/lib/convex/api";
export async function GET(request: Request) {
if (isConvexEnabled()) {
const { auth, client } = getAuthedConvexClient();
const { auth, client } = await getAuthedConvexClient();
const { searchParams } = new URL(request.url);
const workspaceId = searchParams.get("workspaceId");
const pageId = searchParams.get("pageId");
@@ -20,7 +20,7 @@ const isValidDisplayMode = (mode: string): mode is DisplayMode => mode === "inli
export async function POST(request: Request) {
if (isConvexEnabled()) {
const { auth, client } = getAuthedConvexClient();
const { auth, client } = await getAuthedConvexClient();
const body = (await request.json()) as RecordReferencePayload;
if (!body?.workspaceId || !body?.sourcePageId || !body?.targetPageId) {
return NextResponse.json({ error: "缺少必要参数" }, { status: 400 });
@@ -148,7 +148,7 @@ const fetchOcrMatches = async (
export async function POST(request: Request) {
if (isConvexEnabled()) {
const { auth, client } = getAuthedConvexClient();
const { auth, client } = await getAuthedConvexClient();
const payload = (await request.json()) as DocumentSearchRequest;
const workspaceId = payload.workspaceId;
@@ -11,7 +11,7 @@ interface RecentPayload {
export async function POST(request: Request) {
if (isConvexEnabled()) {
const { auth, client } = getAuthedConvexClient();
const { auth, client } = await getAuthedConvexClient();
const { workspaceId, documentId }: RecentPayload = await request.json();
if (!workspaceId || !documentId) {
+1 -1
View File
@@ -72,7 +72,7 @@ function extractMindmapImageAssetIdsFromData(input: unknown): string[] {
export async function GET(request: Request) {
if (isConvexEnabled()) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const client = getConvexHttpClient();
const url = new URL(request.url);
@@ -1,6 +1,9 @@
import { NextResponse } from "next/server";
import { createSupabaseRouteClient } from "@/lib/supabase/server";
import type { DocumentTableSnapshot, TableRowData } from "@/types/online-table";
import { isConvexEnabled } from "@/lib/convex/enabled";
import { getAuthedConvexClient } from "@/lib/convex/route";
import { api } from "@/lib/convex/api";
interface RouteContext {
params: Promise<{
@@ -35,19 +38,38 @@ const getAuthUser = async () => {
};
export async function GET(request: Request, context: RouteContext) {
const tableId = await extractTableId(context);
if (!tableId) {
return NextResponse.json({ error: "Missing tableId" }, { status: 400 });
}
// Convex 模式
if (isConvexEnabled()) {
const { auth, client } = await getAuthedConvexClient();
try {
const table = await client.query(api.tables.get, {
userId: auth.userId,
tableId,
});
if (!table) {
return NextResponse.json({ error: "Table not found" }, { status: 404 });
}
return NextResponse.json(table, { status: 200 });
} catch (error) {
console.error("Convex API error:", error);
return NextResponse.json({ error: "Internal Server Error" }, { status: 500 });
}
}
// Supabase 模式
const { supabase, user } = await getAuthUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
// 强制 await params,以解决 Next.js 16/Turbopack 错误。
const tableId = await extractTableId(context);
if (!tableId) {
return NextResponse.json({ error: "Missing tableId" }, { status: 400 });
}
try {
// 假设 RLS 确保了用户只能访问其有权限的表格
const { data: tableData, error } = await supabase
@@ -74,17 +96,47 @@ export async function GET(request: Request, context: RouteContext) {
}
export async function PATCH(request: Request, context: RouteContext) {
const tableId = await extractTableId(context);
if (!tableId) {
return NextResponse.json({ error: "Missing tableId" }, { status: 400 });
}
// Convex 模式
if (isConvexEnabled()) {
const { auth, client } = await getAuthedConvexClient();
try {
const body = await request.json() as UpdateTableRequest;
const result = await client.mutation(api.tables.update, {
userId: auth.userId,
tableId,
title: body.title,
schema: body.schema,
view_preferences: body.viewPreferences,
snapshot: body.snapshot,
rows: body.rows,
});
// 重新获取更新后的表格数据
const updated = await client.query(api.tables.get, {
userId: auth.userId,
tableId,
});
return NextResponse.json(updated, { status: 200 });
} catch (error) {
console.error("Convex API error:", error);
return NextResponse.json({ error: "Internal Server Error" }, { status: 500 });
}
}
// Supabase 模式
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;
@@ -191,17 +243,34 @@ export async function PATCH(request: Request, context: RouteContext) {
}
export async function DELETE(request: Request, context: RouteContext) {
const tableId = await extractTableId(context);
if (!tableId) {
return NextResponse.json({ error: "Missing tableId" }, { status: 400 });
}
// Convex 模式
if (isConvexEnabled()) {
const { auth, client } = await getAuthedConvexClient();
try {
await client.mutation(api.tables.purge, {
userId: auth.userId,
tableId,
});
return NextResponse.json({ success: true }, { status: 200 });
} catch (error) {
console.error("Convex API error:", error);
return NextResponse.json({ error: "Internal Server Error" }, { status: 500 });
}
}
// Supabase 模式
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")
@@ -1,6 +1,9 @@
import { NextResponse } from "next/server";
import { createSupabaseRouteClient } from "@/lib/supabase/server";
import { DocumentTableSnapshot, TableSchema } from "@/types/online-table";
import { isConvexEnabled } from "@/lib/convex/enabled";
import { getAuthedConvexClient } from "@/lib/convex/route";
import { api } from "@/lib/convex/api";
// 定义请求体类型
interface CreateTableRequestBody {
@@ -11,6 +14,46 @@ interface CreateTableRequestBody {
}
export async function POST(request: Request) {
// Convex 模式
if (isConvexEnabled()) {
const { auth, client } = await getAuthedConvexClient();
try {
const { documentId, title, schema, snapshot } = await request.json() as CreateTableRequestBody;
if (!documentId || !title || !schema) {
return NextResponse.json({ error: "Missing required fields" }, { status: 400 });
}
// 获取 document 所在的 workspace_id
const document = await client.query(api.documents.getMeta, {
userId: auth.userId,
id: documentId,
});
if (!document) {
return NextResponse.json({ error: "Document not found" }, { status: 404 });
}
// 创建表格
const result = await client.mutation(api.tables.create, {
userId: auth.userId,
workspaceId: document.workspace_id,
documentId,
title,
schema,
snapshot,
});
return NextResponse.json(result, { status: 201 });
} catch (error) {
console.error("Convex API error:", error);
return NextResponse.json({ error: "Internal Server Error" }, { status: 500 });
}
}
// Supabase 模式
const supabase = await createSupabaseRouteClient();
const { data: { user } } = await supabase.auth.getUser();
@@ -9,7 +9,7 @@ export const dynamic = "force-dynamic";
export async function POST(request: Request) {
if (isConvexEnabled()) {
const auth = requireAuthContext();
const auth = await requireAuthContext();
const payload = await request.json().catch(() => ({}));
const workspaceId = payload.workspaceId as string | undefined;
if (!workspaceId) {