Add image upload to site settings with old-file cleanup
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
export type FieldType = "text" | "textarea" | "url";
|
||||
export type FieldType = "text" | "textarea" | "url" | "image";
|
||||
|
||||
export type SettingsField = {
|
||||
key: string;
|
||||
@@ -39,7 +39,7 @@ export const SETTINGS_GROUPS: SettingsGroup[] = [
|
||||
{ key: "hero_subtitle", label: "副標題", type: "textarea" },
|
||||
{ key: "hero_primary_cta", label: "主要按鈕文字" },
|
||||
{ key: "hero_secondary_cta", label: "次要按鈕文字" },
|
||||
{ key: "hero_image", label: "主視覺圖片 URL", type: "url", placeholder: "https://..." },
|
||||
{ key: "hero_image", label: "主視覺圖片", type: "image" },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -47,7 +47,7 @@ export const SETTINGS_GROUPS: SettingsGroup[] = [
|
||||
fields: [
|
||||
{ key: "seo_default_title", label: "預設標題" },
|
||||
{ key: "seo_default_description", label: "預設描述", type: "textarea" },
|
||||
{ key: "og_image", label: "分享圖片 URL (OG image)", type: "url", placeholder: "https://..." },
|
||||
{ key: "og_image", label: "分享圖片 (OG image)", type: "image" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
---
|
||||
import AdminLayout from "../../layouts/AdminLayout.astro";
|
||||
import ImageField from "../../components/admin/ImageField.astro";
|
||||
import { ALL_SETTING_KEYS, SETTINGS_GROUPS } from "../../data/settings-fields";
|
||||
import { siteSettings } from "../../db/schema";
|
||||
import { getSettings } from "../../data/content";
|
||||
import { getDb } from "../../lib/db";
|
||||
import { getEnv } from "../../lib/env";
|
||||
import { parseForm } from "../../lib/form";
|
||||
import { deleteMedia } from "../../lib/media";
|
||||
import { settingsInput } from "../../schemas";
|
||||
|
||||
export const prerender = false;
|
||||
|
||||
const db = getDb(getEnv().DB);
|
||||
const env = getEnv();
|
||||
const db = getDb(env.DB);
|
||||
|
||||
const IMAGE_SETTING_KEYS = new Set(
|
||||
SETTINGS_GROUPS.flatMap((g) =>
|
||||
g.fields.filter((f) => f.type === "image").map((f) => f.key),
|
||||
),
|
||||
);
|
||||
|
||||
let saved = false;
|
||||
let error = "";
|
||||
@@ -21,6 +30,7 @@ if (Astro.request.method === "POST") {
|
||||
if (!parsed.ok) {
|
||||
error = Object.values(parsed.errors)[0] ?? "設定有誤。";
|
||||
} else {
|
||||
const before = await getSettings(db);
|
||||
const now = new Date();
|
||||
const submitted = new Set(
|
||||
[...form.keys()].filter((k) => ALL_SETTING_KEYS.includes(k)),
|
||||
@@ -28,6 +38,9 @@ if (Astro.request.method === "POST") {
|
||||
for (const key of ALL_SETTING_KEYS) {
|
||||
if (!submitted.has(key)) continue;
|
||||
const value = String((parsed.data as Record<string, unknown>)[key] ?? "");
|
||||
if (IMAGE_SETTING_KEYS.has(key) && before[key] !== value) {
|
||||
await deleteMedia(env, before[key]);
|
||||
}
|
||||
await db
|
||||
.insert(siteSettings)
|
||||
.values({ key, value, updatedAt: now })
|
||||
@@ -56,11 +69,31 @@ const settings = await getSettings(db);
|
||||
<div class="grid2">
|
||||
{group.fields.map((f) => (
|
||||
<div style={f.type === "textarea" ? "grid-column:1/-1" : ""}>
|
||||
{f.type === "image" ? (
|
||||
<ImageField
|
||||
name={f.key}
|
||||
label={f.label}
|
||||
value={settings[f.key] ?? ""}
|
||||
scope="settings"
|
||||
id={f.key}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<label for={f.key}>{f.label}</label>
|
||||
{f.type === "textarea" ? (
|
||||
<textarea id={f.key} name={f.key} placeholder={f.placeholder}>{settings[f.key] ?? ""}</textarea>
|
||||
<textarea id={f.key} name={f.key} placeholder={f.placeholder}>
|
||||
{settings[f.key] ?? ""}
|
||||
</textarea>
|
||||
) : (
|
||||
<input id={f.key} name={f.key} type="text" value={settings[f.key] ?? ""} placeholder={f.placeholder} />
|
||||
<input
|
||||
id={f.key}
|
||||
name={f.key}
|
||||
type="text"
|
||||
value={settings[f.key] ?? ""}
|
||||
placeholder={f.placeholder}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user