Revert "merge: sync GitHub staging to Gitea"
Deploy staging to test server / deploy (push) Failing after 14m49s
Deploy staging to test server / deploy (push) Failing after 14m49s
This reverts commita55c69115d, reversing changes made to02c9c9f3d6.
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
import crypto from "node:crypto";
|
||||
import { NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
import { requireAdminSession } from "@/lib/admin/auth";
|
||||
import { isPostgresError, queryAdminRows } from "@/lib/admin/database";
|
||||
import { adminErrorResponse } from "@/lib/admin/http";
|
||||
import { suggestedEpayUrls } from "@/lib/epay/config";
|
||||
import { encryptEpayKey } from "@/lib/epay/encryption";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
|
||||
const httpUrl = z.string().trim().min(1).max(2048).url().refine((value) => /^https?:\/\//i.test(value), "必须使用 HTTP(S)");
|
||||
const settingsSchema = z.object({
|
||||
gatewayUrl: httpUrl,
|
||||
pid: z.string().trim().min(1).max(200),
|
||||
notifyUrl: httpUrl,
|
||||
returnUrl: httpUrl,
|
||||
siteName: z.string().trim().min(1).max(100),
|
||||
chatEnabled: z.boolean(),
|
||||
newKey: z.string().min(1).max(1000).optional(),
|
||||
}).strict();
|
||||
|
||||
type SettingsRow = {
|
||||
gateway_url: string;
|
||||
pid: string;
|
||||
encrypted_key: string;
|
||||
notify_url: string;
|
||||
return_url: string;
|
||||
site_name: string;
|
||||
chat_enabled: boolean;
|
||||
updated_at?: Date;
|
||||
};
|
||||
|
||||
function publicSettings(row: SettingsRow, source: "database" | "environment") {
|
||||
return {
|
||||
gatewayUrl: row.gateway_url,
|
||||
pid: row.pid,
|
||||
notifyUrl: row.notify_url,
|
||||
returnUrl: row.return_url,
|
||||
siteName: row.site_name,
|
||||
chatEnabled: row.chat_enabled,
|
||||
keyConfigured: Boolean(row.encrypted_key),
|
||||
complete: Boolean(row.gateway_url && row.pid && row.encrypted_key && row.notify_url && row.return_url && row.site_name),
|
||||
source,
|
||||
updatedAt: source === "database" ? row.updated_at?.toISOString() ?? null : null,
|
||||
};
|
||||
}
|
||||
|
||||
function environmentSettings() {
|
||||
const defaults = suggestedEpayUrls();
|
||||
const row: SettingsRow = {
|
||||
gateway_url: process.env.EPAY_GATEWAY_URL?.trim() || "",
|
||||
pid: process.env.EPAY_PID?.trim() || "",
|
||||
encrypted_key: process.env.EPAY_KEY?.trim() ? "configured" : "",
|
||||
notify_url: process.env.EPAY_NOTIFY_URL?.trim() || defaults.notifyUrl,
|
||||
return_url: process.env.EPAY_RETURN_URL?.trim() || defaults.returnUrl,
|
||||
site_name: process.env.EPAY_SITE_NAME?.trim() || "Jyotisha",
|
||||
chat_enabled: ["true", "1"].includes(process.env.EPAY_CHAT_ENABLED?.trim().toLowerCase() || ""),
|
||||
};
|
||||
return publicSettings(row, "environment");
|
||||
}
|
||||
|
||||
async function databaseRow() {
|
||||
try {
|
||||
const rows = await queryAdminRows<SettingsRow>(`
|
||||
select gateway_url, pid, encrypted_key, notify_url, return_url, site_name, chat_enabled, updated_at
|
||||
from public.epay_settings
|
||||
where id = true
|
||||
limit 1
|
||||
`);
|
||||
return rows[0] ?? null;
|
||||
} catch (error) {
|
||||
if (isPostgresError(error) && error.code === "42P01") return null;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
await requireAdminSession("read");
|
||||
const row = await databaseRow();
|
||||
if (row) return NextResponse.json(publicSettings(row, "database"));
|
||||
const settings = environmentSettings();
|
||||
return NextResponse.json(settings.complete || settings.keyConfigured
|
||||
? settings
|
||||
: { ...settings, source: "unconfigured" });
|
||||
} catch (error) {
|
||||
return adminErrorResponse(error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function PUT(request: Request) {
|
||||
try {
|
||||
const session = await requireAdminSession("write");
|
||||
const parsed = settingsSchema.safeParse(await request.json().catch(() => null));
|
||||
if (!parsed.success) return NextResponse.json({ error: "易支付配置参数不正确" }, { status: 400 });
|
||||
|
||||
const existing = await databaseRow();
|
||||
if (!existing && !parsed.data.newKey) {
|
||||
return NextResponse.json({ error: "首次保存数据库配置时必须输入新的商户密钥" }, { status: 400 });
|
||||
}
|
||||
const encryptedKey = parsed.data.newKey
|
||||
? encryptEpayKey(parsed.data.newKey)
|
||||
: existing!.encrypted_key;
|
||||
try {
|
||||
const rows = await queryAdminRows<SettingsRow>(`
|
||||
select * from public.admin_save_epay_settings(
|
||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12
|
||||
)
|
||||
`, [
|
||||
session.user.id,
|
||||
session.user.email,
|
||||
session.role,
|
||||
crypto.randomUUID(),
|
||||
parsed.data.gatewayUrl.replace(/\/+$/, ""),
|
||||
parsed.data.pid,
|
||||
encryptedKey,
|
||||
parsed.data.notifyUrl,
|
||||
parsed.data.returnUrl,
|
||||
parsed.data.siteName,
|
||||
parsed.data.chatEnabled,
|
||||
Boolean(parsed.data.newKey),
|
||||
]);
|
||||
if (!rows[0]) return NextResponse.json({ error: "保存易支付配置失败" }, { status: 500 });
|
||||
return NextResponse.json(publicSettings(rows[0], "database"));
|
||||
} catch {
|
||||
return NextResponse.json({ error: "保存易支付配置失败" }, { status: 500 });
|
||||
}
|
||||
} catch (error) {
|
||||
return adminErrorResponse(error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { AdminAuthorizationError, requireAdminSession } from "@/lib/admin/auth";
|
||||
import { adminErrorResponse } from "@/lib/admin/http";
|
||||
import { epaySubmitUrl, readEpayConfig } from "@/lib/epay/config";
|
||||
import { assertPublicGatewayUrl } from "@/lib/epay/gateway-policy";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
|
||||
function reachableStatus(status: number) {
|
||||
return status >= 200 && status < 500;
|
||||
}
|
||||
|
||||
export async function POST() {
|
||||
try {
|
||||
await requireAdminSession("write");
|
||||
const config = await readEpayConfig();
|
||||
const submitUrl = epaySubmitUrl(config.gatewayUrl);
|
||||
await assertPublicGatewayUrl(submitUrl);
|
||||
const startedAt = performance.now();
|
||||
let response = await fetch(submitUrl, {
|
||||
method: "HEAD",
|
||||
redirect: "manual",
|
||||
signal: AbortSignal.timeout(8_000),
|
||||
});
|
||||
if (response.status === 405 || response.status === 501) {
|
||||
response = await fetch(submitUrl, {
|
||||
method: "GET",
|
||||
redirect: "manual",
|
||||
signal: AbortSignal.timeout(8_000),
|
||||
});
|
||||
}
|
||||
const available = reachableStatus(response.status);
|
||||
return NextResponse.json({
|
||||
available,
|
||||
message: available ? "当前已保存的易支付配置可访问" : "当前已保存的易支付配置暂不可用",
|
||||
latencyMs: Math.round(performance.now() - startedAt),
|
||||
status: response.status,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof AdminAuthorizationError) return adminErrorResponse(error);
|
||||
return NextResponse.json({
|
||||
available: false,
|
||||
message: "当前已保存的易支付配置暂不可用",
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user