Replace bare console.error() calls with logError() utility across all 47 API route handlers. logError() prints timestamp + context + message + stack trace + extra request data to stderr, and redacts sensitive fields (password, token, secret, key, etc.) from logged objects.
103 lines
3.6 KiB
TypeScript
103 lines
3.6 KiB
TypeScript
import { logError } from "@/lib/logger";
|
|
import { NextRequest, NextResponse } from "next/server";
|
|
import { query } from "@/lib/db";
|
|
import { createAuditLog } from "@/lib/log";
|
|
|
|
export const runtime = "nodejs";
|
|
|
|
async function ensureTablesExist() {
|
|
try {
|
|
await query(`
|
|
CREATE TABLE IF NOT EXISTS internal_email_recipients (
|
|
id BIGSERIAL PRIMARY KEY, email VARCHAR(255) NOT NULL,
|
|
name VARCHAR(255) NOT NULL DEFAULT '', is_active BOOLEAN NOT NULL DEFAULT true,
|
|
created_by BIGINT NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
)
|
|
`);
|
|
await query(`CREATE INDEX IF NOT EXISTS idx_internal_email_recipients_email ON internal_email_recipients (LOWER(email))`);
|
|
await query(`
|
|
CREATE TABLE IF NOT EXISTS admin_ai_config (
|
|
id BIGSERIAL PRIMARY KEY, config_key VARCHAR(100) NOT NULL UNIQUE,
|
|
config_value TEXT NOT NULL DEFAULT '', updated_by BIGINT, updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
)
|
|
`);
|
|
} catch { /* ignore */ }
|
|
}
|
|
|
|
// GET /api/admin/daily-report/ai-config — get AI config (values, not secrets)
|
|
export async function GET() {
|
|
try {
|
|
await ensureTablesExist();
|
|
const result = await query<{ config_key: string; config_value: string }>(
|
|
`SELECT config_key, config_value, updated_at::text as updated_at
|
|
FROM admin_ai_config
|
|
WHERE config_key IN ('ai_model', 'ai_api_key', 'ai_enabled', 'smtp_host', 'smtp_port', 'smtp_username', 'smtp_password', 'smtp_from', 'smtp_tls', 'report_enabled')`
|
|
);
|
|
|
|
const config: Record<string, string> = {};
|
|
for (const row of result.rows) {
|
|
// Mask secrets
|
|
if (row.config_key === "ai_api_key" || row.config_key === "smtp_password") {
|
|
config[row.config_key] = row.config_value ? "***" : "";
|
|
} else {
|
|
config[row.config_key] = row.config_value;
|
|
}
|
|
}
|
|
|
|
return NextResponse.json({ config });
|
|
} catch (e) {
|
|
logError("Get AI config error:", e);
|
|
return NextResponse.json({ error: "服务器错误" }, { status: 500 });
|
|
}
|
|
}
|
|
|
|
// PUT /api/admin/daily-report/ai-config — upsert AI config
|
|
export async function PUT(req: NextRequest) {
|
|
try {
|
|
const body = await req.json() as Record<string, string>;
|
|
const adminUserId = parseInt(req.headers.get("x-admin-user-id") || "0", 10);
|
|
const allowedKeys = [
|
|
"ai_model", "ai_api_key", "ai_enabled", "basic_api_url",
|
|
"smtp_host", "smtp_port", "smtp_username", "smtp_password", "smtp_from", "smtp_tls",
|
|
"report_enabled",
|
|
];
|
|
|
|
const updates: string[] = [];
|
|
const vals: unknown[] = [];
|
|
let idx = 1;
|
|
|
|
for (const [key, value] of Object.entries(body)) {
|
|
if (!allowedKeys.includes(key)) continue;
|
|
updates.push(`($${idx++}, $${idx++}, $${idx++})`);
|
|
vals.push(key, value, adminUserId);
|
|
}
|
|
|
|
if (updates.length === 0) {
|
|
return NextResponse.json({ error: "没有需要保存的配置" }, { status: 400 });
|
|
}
|
|
|
|
await query(
|
|
`INSERT INTO admin_ai_config (config_key, config_value, updated_by)
|
|
VALUES ${updates.join(", ")}
|
|
ON CONFLICT (config_key) DO UPDATE SET
|
|
config_value = EXCLUDED.config_value,
|
|
updated_by = EXCLUDED.updated_by,
|
|
updated_at = NOW()`,
|
|
vals
|
|
);
|
|
|
|
await createAuditLog({
|
|
userId: adminUserId,
|
|
username: req.headers.get("x-admin-username") || "unknown",
|
|
action: "update",
|
|
resource: "admin_ai_config",
|
|
resourceId: "bulk-update",
|
|
});
|
|
|
|
return NextResponse.json({ success: true });
|
|
} catch (e) {
|
|
logError("Update AI config error:", e);
|
|
return NextResponse.json({ error: "服务器错误" }, { status: 500 });
|
|
}
|
|
}
|