x
This commit is contained in:
1 parent
48aebb2ec9
commit
b693c7188c
8 files changed
+267
-4
No files matched your search
@@ -3,22 +3,112 @@ import prisma from "../lib/prisma";
|
||||
import { adminOnly } from "../middleware/auth";
|
||||
|
||||
export const settingsRoute = new Elysia({ prefix: "/settings" })
|
||||
.get("/", async ({ set }) => {
|
||||
.get("/", async ({ set, query }) => {
|
||||
try {
|
||||
let settings = await prisma.storeSettings.findFirst();
|
||||
let settings = await prisma.storeSettings.findFirst({ include: { translations: true } });
|
||||
if (!settings) {
|
||||
settings = await prisma.storeSettings.create({
|
||||
data: {}
|
||||
data: {},
|
||||
include: { translations: true }
|
||||
});
|
||||
}
|
||||
return settings;
|
||||
const { translations, ...settingsData } = settings;
|
||||
const requestedLang = typeof query?.lang === "string" ? query.lang : undefined;
|
||||
const localizedDescription = requestedLang
|
||||
? translations.find((translation) => translation.langCode === requestedLang)?.footerDescription
|
||||
: undefined;
|
||||
return localizedDescription ? { ...settingsData, footerDescription: localizedDescription } : settingsData;
|
||||
} catch (err) {
|
||||
console.error("[Settings] Error:", err);
|
||||
set.status = 500;
|
||||
return { error: "Failed to fetch settings" };
|
||||
}
|
||||
})
|
||||
/**
|
||||
* Dedicated Footer Brand contract. This keeps the editable Brand block
|
||||
* separate from footer link menus while reusing the existing StoreSettings
|
||||
* columns and StoreSettingsTranslation rows.
|
||||
*/
|
||||
.get("/footer-brand", async ({ set }) => {
|
||||
try {
|
||||
const settings = await prisma.storeSettings.findFirst({ include: { translations: true } });
|
||||
const base = settings ?? await prisma.storeSettings.create({ data: {}, include: { translations: true } });
|
||||
const descriptions: Record<string, string> = {};
|
||||
for (const translation of base.translations ?? []) {
|
||||
if (translation.footerDescription) descriptions[translation.langCode] = translation.footerDescription;
|
||||
}
|
||||
if (base.footerDescription && !descriptions.th) descriptions.th = base.footerDescription;
|
||||
return {
|
||||
version: base.updatedAt.toISOString(),
|
||||
brand: {
|
||||
brandName: base.brandName,
|
||||
logoUrl: base.logoUrl ?? null,
|
||||
description: descriptions,
|
||||
socialLinks: {
|
||||
facebookUrl: base.facebookUrl ?? null,
|
||||
instagramUrl: base.instagramUrl ?? null,
|
||||
tiktokUrl: base.tiktokUrl ?? null,
|
||||
},
|
||||
},
|
||||
};
|
||||
} catch (err) {
|
||||
console.error("[Settings] Footer brand fetch error:", err);
|
||||
set.status = 500;
|
||||
return { error: "Failed to fetch footer brand" };
|
||||
}
|
||||
})
|
||||
.use(adminOnly)
|
||||
.patch("/footer-brand", async ({ body, set }) => {
|
||||
try {
|
||||
const payload = body as any;
|
||||
const settings = await prisma.storeSettings.findFirst({ include: { translations: true } });
|
||||
const current = settings ?? await prisma.storeSettings.create({ data: {}, include: { translations: true } });
|
||||
const version = current.updatedAt.toISOString();
|
||||
if (payload.expectedVersion && payload.expectedVersion !== version) {
|
||||
set.status = 409;
|
||||
return { error: "Footer brand has changed since it was read", code: "VERSION_CONFLICT", expectedVersion: payload.expectedVersion, currentVersion: version };
|
||||
}
|
||||
|
||||
const updates: any = {};
|
||||
if (payload.brandName !== undefined) {
|
||||
if (typeof payload.brandName !== "string" || !payload.brandName.trim()) throw new Error("brandName must be a non-empty string");
|
||||
updates.brandName = payload.brandName.trim();
|
||||
}
|
||||
for (const field of ["logoUrl", "facebookUrl", "instagramUrl", "tiktokUrl"]) {
|
||||
if (payload[field] !== undefined) {
|
||||
if (payload[field] !== null && typeof payload[field] !== "string") throw new Error(`${field} must be a string or null`);
|
||||
updates[field] = payload[field];
|
||||
}
|
||||
}
|
||||
const description = payload.description ?? payload.footerDescription;
|
||||
if (description !== undefined && (typeof description !== "object" || Array.isArray(description))) throw new Error("description must be an object keyed by locale");
|
||||
if (payload.dryRun) {
|
||||
return { dryRun: true, version, wouldUpdate: { ...updates, description: description ?? undefined } };
|
||||
}
|
||||
|
||||
const updated = Object.keys(updates).length
|
||||
? await prisma.storeSettings.update({ where: { id: current.id }, data: updates })
|
||||
: current;
|
||||
if (description) {
|
||||
for (const [langCode, value] of Object.entries(description)) {
|
||||
if (typeof langCode !== "string" || typeof value !== "string") throw new Error("description values must be strings");
|
||||
const existing = await prisma.storeSettingsTranslation.findUnique({ where: { storeSettingsId_langCode: { storeSettingsId: current.id, langCode } } });
|
||||
if (existing) await prisma.storeSettingsTranslation.update({ where: { id: existing.id }, data: { footerDescription: value } });
|
||||
else await prisma.storeSettingsTranslation.create({ data: { storeSettingsId: current.id, langCode, footerDescription: value } });
|
||||
if (langCode === "th") await prisma.storeSettings.update({ where: { id: current.id }, data: { footerDescription: value } });
|
||||
}
|
||||
}
|
||||
const result = await prisma.storeSettings.findUnique({ where: { id: current.id }, include: { translations: true } });
|
||||
const descriptions: Record<string, string> = {};
|
||||
for (const translation of result?.translations ?? []) if (translation.footerDescription) descriptions[translation.langCode] = translation.footerDescription;
|
||||
if (result?.footerDescription && !descriptions.th) descriptions.th = result.footerDescription;
|
||||
return { success: true, version: result?.updatedAt.toISOString(), brand: { brandName: result?.brandName, logoUrl: result?.logoUrl ?? null, description: descriptions, socialLinks: { facebookUrl: result?.facebookUrl ?? null, instagramUrl: result?.instagramUrl ?? null, tiktokUrl: result?.tiktokUrl ?? null } } };
|
||||
} catch (err: any) {
|
||||
console.error("[Settings] Footer brand update error:", err);
|
||||
set.status = 400;
|
||||
return { error: err?.message || "Failed to update footer brand" };
|
||||
}
|
||||
}, { body: t.Any() })
|
||||
.put("/", async ({ body, set }) => {
|
||||
try {
|
||||
const data = body as any;
|
||||
|
||||
Reference in new issue
Block a user