mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-09-02 09:30:23 +08:00
fix(admin): address Copilot review findings
- Reflect built-in defaults for boolean settings (ALLOW_PRIVATE_URLS defaults on) and allow clearing a saved boolean back to default, so the SSRF toggle matches actual runtime behavior. - Harden JSON loading: filter settings values to strings only, and schema-validate stored ADMIN_PROVIDERS entries, dropping malformed ones instead of letting them reach runtime code. - Set beforeunload returnValue so the unsaved-changes prompt shows in all browsers; reject non-finite numbers in settings validation. - Fix README/CN/JA docs that claimed the panel auto-generates AI_MODELS_CONFIG (providers are merged at read time, not written). - Add unit tests for corrupted-file value filtering and provider schema validation.
This commit is contained in:
@@ -91,7 +91,13 @@ export function loadAdminProviders(): StoredAdminProvider[] {
|
||||
if (!raw) return []
|
||||
try {
|
||||
const parsed = JSON.parse(raw)
|
||||
return Array.isArray(parsed) ? parsed : []
|
||||
if (!Array.isArray(parsed)) return []
|
||||
// Validate each entry's shape — a malformed/hand-edited value must
|
||||
// not reach runtime code that assumes provider/models exist.
|
||||
return parsed.flatMap((entry) => {
|
||||
const result = AdminProviderSchema.safeParse(entry)
|
||||
return result.success ? [result.data as StoredAdminProvider] : []
|
||||
})
|
||||
} catch {
|
||||
console.error("[admin-providers] Failed to parse stored providers")
|
||||
return []
|
||||
|
||||
@@ -22,6 +22,9 @@ export interface SettingDef {
|
||||
min?: number
|
||||
max?: number
|
||||
placeholder?: string
|
||||
// Built-in default applied at runtime when the value is unset, so the UI
|
||||
// can reflect actual behavior (e.g. ALLOW_PRIVATE_URLS defaults to "true").
|
||||
default?: string
|
||||
// Value is only picked up at process start (module-load readers)
|
||||
restartRequired?: boolean
|
||||
}
|
||||
@@ -134,6 +137,8 @@ export const SETTINGS_REGISTRY: SettingDef[] = [
|
||||
label: "Allow Private URLs",
|
||||
description:
|
||||
"Turn off to block requests to private IPs and internal hostnames (SSRF protection).",
|
||||
// Unset means allowed at runtime (ssrf-protection: !== "false")
|
||||
default: "true",
|
||||
},
|
||||
|
||||
// ── Observability ────────────────────────────────────────────────
|
||||
|
||||
@@ -29,8 +29,18 @@ export function loadSettings(): Record<string, string> {
|
||||
try {
|
||||
const raw = fs.readFileSync(getSettingsPath(), "utf8")
|
||||
const parsed = JSON.parse(raw) as SettingsFile
|
||||
cachedSettings =
|
||||
parsed && typeof parsed.values === "object" ? parsed.values : {}
|
||||
// Keep only string values — a hand-edited or corrupted file could
|
||||
// hold null/arrays/numbers that would otherwise be overlaid onto
|
||||
// process.env and coerce to junk like "[object Object]".
|
||||
const values: Record<string, string> = {}
|
||||
const raw_values =
|
||||
parsed && typeof parsed.values === "object" && parsed.values
|
||||
? parsed.values
|
||||
: {}
|
||||
for (const [key, value] of Object.entries(raw_values)) {
|
||||
if (typeof value === "string") values[key] = value
|
||||
}
|
||||
cachedSettings = values
|
||||
} catch (err: any) {
|
||||
if (err?.code !== "ENOENT") {
|
||||
console.error("[admin-settings] Failed to read settings file:", err)
|
||||
|
||||
Reference in New Issue
Block a user