feat: implement dashboard state management, API proxy layer, and modular UI components for weather monitoring and payments

This commit is contained in:
2569718930@qq.com
2026-05-14 15:05:13 +08:00
parent 02add6d994
commit c9d03fd3e1
30 changed files with 2213 additions and 1933 deletions
+67
View File
@@ -1,4 +1,10 @@
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import {
applyAuthResponseCookies,
buildBackendRequestHeaders,
} from "@/lib/backend-auth";
import { buildCachedJsonResponse } from "@/lib/http-cache";
const PASSTHROUGH_UPSTREAM_STATUSES = new Set([
400,
@@ -70,3 +76,64 @@ export function buildProxyExceptionResponse(
return NextResponse.json(body, { status: options.status ?? 500 });
}
export async function proxyBackendJsonGet(
req: NextRequest,
options: {
cacheControl?: string;
conditionalResponse?: boolean;
detailLimit?: number;
error?: string;
fetchCache?: RequestCache;
includeSupabaseIdentity?: boolean;
publicMessage: string;
revalidateSeconds?: number;
signal?: AbortSignal;
statusOnException?: number;
timeoutPublicMessage?: string;
url: string;
},
) {
let auth: Awaited<ReturnType<typeof buildBackendRequestHeaders>> | null = null;
try {
auth = await buildBackendRequestHeaders(req, {
includeSupabaseIdentity: options.includeSupabaseIdentity ?? false,
});
const res = await fetch(options.url, {
headers: auth.headers,
...(options.fetchCache
? { cache: options.fetchCache }
: { next: { revalidate: options.revalidateSeconds ?? 30 } }),
signal: options.signal,
});
if (!res.ok) {
const raw = await res.text();
const response = buildUpstreamErrorResponse(res.status, raw, {
detailLimit: options.detailLimit,
error: options.error,
});
return applyAuthResponseCookies(response, auth.response);
}
const data = await res.json();
const response =
options.cacheControl && options.conditionalResponse !== false
? buildCachedJsonResponse(req, data, options.cacheControl)
: NextResponse.json(data, {
headers: options.cacheControl
? { "Cache-Control": options.cacheControl }
: undefined,
});
return applyAuthResponseCookies(response, auth.response);
} catch (error) {
const timedOut = options.signal?.aborted === true;
const response = buildProxyExceptionResponse(error, {
publicMessage:
timedOut && options.timeoutPublicMessage
? options.timeoutPublicMessage
: options.publicMessage,
status: timedOut ? 504 : options.statusOnException,
});
return auth ? applyAuthResponseCookies(response, auth.response) : response;
}
}