This commit is contained in:
Your Name committed 2026-09-29 12:48:52 +07:00
1 parent 683f54fd95
commit 44a613f8ec
12 files changed
+1227 -9

No files matched your search

+262
View File
@@ -0,0 +1,262 @@
import { Elysia, t } from "elysia";
import prisma from "../lib/prisma";
import { adminOnly } from "../middleware/auth";
export const DEFAULT_AKERIA_KNOWLEDGE = {
brandName: "AKÉRIA",
tagline: "Soft Asian Elegance & Mindful Beauty Sanctuary",
story: `AKÉRIA is a modern beauty e-commerce and lifestyle brand born from a reverence for soft Asian/Japanese-inspired serenity.
We believe that beauty and well-being are intimate daily rituals of self-restoration. Every product is thoughtfully curated to transform ordinary spaces and skincare routines into luminous, tranquil sanctuaries. Our design and philosophy balance modern minimalism with gentle, time-honored botanical sensibilities.`,
targetAudience: `• Primary Persona: Modern women & young professionals (ages 20–38) seeking refined, gentle, and aesthetic lifestyle products.
• Demographics: Urban, design-conscious, value self-care rituals, clean beauty, and sensory living.
• Psychographics: Drawn to Japanese/Korean soft minimalism, pastel elegance, gentle glow, and mindful calm.
• Pain Points: Tired of overly aggressive, dark, or cluttered beauty branding; desires products that feel like a breath of fresh air.`,
toneOfVoice: `• Core Personality: Bright, feminine, clean, fresh, premium-friendly, elegant, youthful, soft, luminous.
• Language Style: Warm, welcoming, sensory-rich, gentle, and polished. Uses evocative yet accessible phrasing.
• Words & Phrases We Love: Serenity, luminous glow, gentle ritual, mindful calm, airy softness, blossom, delicate harmony.
• Words & Phrases We Avoid: Hyperbolic sales hype ("cheapest", "best ever"), harsh slang, aggressive discount urgency, dark/cynical humor.`,
visualArtPrompt: `You are the Visual Art Director for AKÉRIA.
AKÉRIA is a modern beauty e-commerce and lifestyle brand with a soft Asian/Japanese-inspired sensibility.
BRAND MOOD
Bright, feminine, clean, fresh, premium-friendly, elegant, youthful, soft, luminous.
COLOR PALETTE
Primary:
- Coral Rose #F28F96
- Sakura Blush #F5C2C9
- Warm Ivory #F7EFE6
Secondary:
- Soft Lavender #DCCDE3
- Soft Peach #F4D4C2
- Pure White #FFFFFF
Contrast:
- Charcoal #2D2D2D
COLOR DISTRIBUTION
- 55–65% Warm Ivory / White
- 15–20% Coral Rose / Sakura Blush
- 10–15% Soft Peach / Lavender
- 5–10% Charcoal for contrast
LIGHTING
Soft diffused beauty lighting.
Bright but not overexposed.
Warm-neutral white balance.
Luminous skin.
Soft highlights.
Low-to-medium contrast.
Fresh commercial beauty campaign feel.
PHOTOGRAPHY STYLE
Modern Asian beauty editorial.
Clean skin texture.
Natural glossy makeup.
Soft floral details.
Elegant product styling.
Light airy composition.
Premium e-commerce campaign photography.
MATERIALS & TEXTURES
Frosted glass.
Clear acrylic.
Soft satin.
Glossy cosmetic packaging.
Light marble.
Soft petals.
Minimal reflective surfaces.
BACKGROUND
Prefer:
Warm ivory.
Soft blush gradient.
White.
Very pale peach.
Subtle lavender.
WARDROBE
Prefer:
White.
Ivory.
Blush.
Soft beige.
Dusty rose.
Pale lavender.
ACCENT ELEMENTS
Use cherry blossom, soft petals, subtle glow, fine sparkles, or clean beauty props.
Keep these subtle and refined.
AVOID
Hot pink.
Neon colors.
Strong red.
Electric blue.
Bright green.
Strong yellow.
Dark dramatic backgrounds.
Heavy black.
Cyberpunk lighting.
Overly luxurious gold.
Overly mature fashion styling.
IMAGE COMPOSITION
Keep generous negative space for typography or website UI.
Use clean commercial framing.
Hero subject should feel approachable, polished, and aspirational.
IMPORTANT
All backgrounds, props, flowers, clothing, packaging accents, and decorative elements must remain within the AKÉRIA color palette.
The final image must immediately feel like AKÉRIA:
bright beauty commerce, soft Asian elegance, modern, friendly, and premium.`,
productUsps: `• Hero Product Lines: Pure Aroma & Botanical Oils, Reed Diffusers, Ambient Room Sprays, Gentle Skincare Rituals.
• Core Differentiators: 100% gentle botanical extractions, alcohol-free and safe for sensitive spaces.
• Formulations: Cruelty-free, clean ingredients, sustainable frosted glass packaging.
• Emotional Outcome: Instant sense of relaxation, soft luxurious ambiance, mindful mental clarity.`,
marketingGuidelines: `• TikTok: Short ASMR unboxing, soft aesthetic morning routines, relaxing evening wind-downs, visual calm reels.
• Instagram: Clean feed aesthetic, generous white space, soft sakura/ivory color theme, product macro shots with soft natural light.
• Website Copy: Storytelling focus, serene headlines, sensory product descriptions, clear value propositions.
• Calls-to-Action (CTAs): "Discover Your Ritual", "Explore the Collection", "Embrace Serenity", "Begin Your Glow".
• Core Hashtags: #AKERIA #SoftBeauty #AromaRitual #MindfulLiving #AsianElegance #CleanAesthetic`,
customRules: `1. Always keep visual and verbal assets strictly within the soft Asian/Japanese beauty aesthetic.
2. Never introduce harsh contrast, neon highlights, or aggressive dark palettes.
3. Every response or content generated should feel polite, premium, approachable, and calming.`
};
export function compileBrandPrompt(knowledge: any): string {
if (knowledge.masterPromptOverride && knowledge.masterPromptOverride.trim()) {
return knowledge.masterPromptOverride.trim();
}
const sections: string[] = [];
sections.push(`# BRAND KNOWLEDGE DIRECTIVE: ${knowledge.brandName || "AKÉRIA"}`);
if (knowledge.tagline) {
sections.push(`**Tagline:** ${knowledge.tagline}`);
}
if (knowledge.story) {
sections.push(`## 1. BRAND STORY & CORE PHILOSOPHY\n${knowledge.story.trim()}`);
}
if (knowledge.targetAudience) {
sections.push(`## 2. TARGET AUDIENCE & PERSONA\n${knowledge.targetAudience.trim()}`);
}
if (knowledge.toneOfVoice) {
sections.push(`## 3. TONE OF VOICE & MESSAGING STYLE\n${knowledge.toneOfVoice.trim()}`);
}
if (knowledge.visualArtPrompt) {
sections.push(`## 4. VISUAL ART DIRECTION & PHOTOGRAPHY DIRECTIVE\n${knowledge.visualArtPrompt.trim()}`);
}
if (knowledge.productUsps) {
sections.push(`## 5. PRODUCTS, SIGNATURE NOTES & USPs\n${knowledge.productUsps.trim()}`);
}
if (knowledge.marketingGuidelines) {
sections.push(`## 6. MARKETING CHANNELS & CONTENT GUIDELINES\n${knowledge.marketingGuidelines.trim()}`);
}
if (knowledge.customRules) {
sections.push(`## 7. STRICT BRAND COMPLIANCE & RULES\n${knowledge.customRules.trim()}`);
}
sections.push(`---\n*This document is the single source of truth for all LLM, AI Image, and Copywriting tasks representing ${knowledge.brandName || "AKÉRIA"}.*`);
return sections.join("\n\n");
}
export const brandKnowledgeRoute = new Elysia({ prefix: "/brand-knowledge" })
.get("/", async () => {
let knowledge = await prisma.brandKnowledge.findFirst();
if (!knowledge) {
knowledge = await prisma.brandKnowledge.create({
data: DEFAULT_AKERIA_KNOWLEDGE,
});
}
return {
...knowledge,
compiledPrompt: compileBrandPrompt(knowledge),
};
})
.get("/compiled-prompt", async () => {
let knowledge = await prisma.brandKnowledge.findFirst();
if (!knowledge) {
knowledge = await prisma.brandKnowledge.create({
data: DEFAULT_AKERIA_KNOWLEDGE,
});
}
return {
brandName: knowledge.brandName,
compiledPrompt: compileBrandPrompt(knowledge),
updatedAt: knowledge.updatedAt,
};
})
.use(adminOnly)
.put(
"/",
async ({ body, set }) => {
try {
const data = body as any;
let knowledge = await prisma.brandKnowledge.findFirst();
if (!knowledge) {
knowledge = await prisma.brandKnowledge.create({
data: {
...DEFAULT_AKERIA_KNOWLEDGE,
...data,
},
});
} else {
knowledge = await prisma.brandKnowledge.update({
where: { id: knowledge.id },
data: {
brandName: data.brandName !== undefined ? data.brandName : knowledge.brandName,
tagline: data.tagline !== undefined ? data.tagline : knowledge.tagline,
story: data.story !== undefined ? data.story : knowledge.story,
targetAudience: data.targetAudience !== undefined ? data.targetAudience : knowledge.targetAudience,
toneOfVoice: data.toneOfVoice !== undefined ? data.toneOfVoice : knowledge.toneOfVoice,
visualArtPrompt: data.visualArtPrompt !== undefined ? data.visualArtPrompt : knowledge.visualArtPrompt,
productUsps: data.productUsps !== undefined ? data.productUsps : knowledge.productUsps,
marketingGuidelines: data.marketingGuidelines !== undefined ? data.marketingGuidelines : knowledge.marketingGuidelines,
customRules: data.customRules !== undefined ? data.customRules : knowledge.customRules,
masterPromptOverride: data.masterPromptOverride !== undefined ? data.masterPromptOverride : knowledge.masterPromptOverride,
},
});
}
return {
success: true,
data: {
...knowledge,
compiledPrompt: compileBrandPrompt(knowledge),
},
};
} catch (err) {
console.error("[BrandKnowledge] Update error:", err);
set.status = 500;
return { success: false, error: "Failed to update brand knowledge" };
}
},
{
body: t.Object({
brandName: t.Optional(t.String()),
tagline: t.Optional(t.Nullable(t.String())),
story: t.Optional(t.Nullable(t.String())),
targetAudience: t.Optional(t.Nullable(t.String())),
toneOfVoice: t.Optional(t.Nullable(t.String())),
visualArtPrompt: t.Optional(t.Nullable(t.String())),
productUsps: t.Optional(t.Nullable(t.String())),
marketingGuidelines: t.Optional(t.Nullable(t.String())),
customRules: t.Optional(t.Nullable(t.String())),
masterPromptOverride: t.Optional(t.Nullable(t.String())),
}),
}
);
+14 -1
View File
@@ -18,6 +18,7 @@ export async function saveStoreSettingsTranslations(settingsId: number, payload:
if (item.newsletterButtonText !== undefined) updateData.newsletterButtonText = item.newsletterButtonText;
if (item.defaultSeoTitle !== undefined) updateData.defaultSeoTitle = item.defaultSeoTitle;
if (item.defaultSeoDescription !== undefined) updateData.defaultSeoDescription = item.defaultSeoDescription;
if (item.brandPrompt !== undefined) updateData.brandPrompt = item.brandPrompt;
const existing = await prisma.storeSettingsTranslation.findUnique({
where: { storeSettingsId_langCode: { storeSettingsId: settingsId, langCode: lang } }
@@ -82,6 +83,12 @@ export async function saveStoreSettingsTranslations(settingsId: number, payload:
? (lang === 'th' ? payload.defaultSeoDescription : undefined)
: payload.defaultSeoDescription?.[lang];
const brandPrompt = typeof langObj?.brandPrompt === 'string' || langObj?.brandPrompt === null
? langObj.brandPrompt
: typeof payload.brandPrompt === 'string' || payload.brandPrompt === null
? (lang === 'th' ? payload.brandPrompt : undefined)
: payload.brandPrompt?.[lang];
if (
footerDescription === undefined &&
newsletterTitle === undefined &&
@@ -89,7 +96,8 @@ export async function saveStoreSettingsTranslations(settingsId: number, payload:
newsletterPlaceholder === undefined &&
newsletterButtonText === undefined &&
defaultSeoTitle === undefined &&
defaultSeoDescription === undefined
defaultSeoDescription === undefined &&
brandPrompt === undefined
) {
continue;
}
@@ -102,6 +110,7 @@ export async function saveStoreSettingsTranslations(settingsId: number, payload:
if (newsletterButtonText !== undefined) updateData.newsletterButtonText = newsletterButtonText;
if (defaultSeoTitle !== undefined) updateData.defaultSeoTitle = defaultSeoTitle;
if (defaultSeoDescription !== undefined) updateData.defaultSeoDescription = defaultSeoDescription;
if (brandPrompt !== undefined) updateData.brandPrompt = brandPrompt;
const existing = await prisma.storeSettingsTranslation.findUnique({
where: { storeSettingsId_langCode: { storeSettingsId: settingsId, langCode: lang } }
@@ -155,6 +164,7 @@ export const settingsRoute = new Elysia({ prefix: "/settings" })
const localizedDescription = translation?.footerDescription;
const localizedSeoTitle = translation?.defaultSeoTitle;
const localizedSeoDescription = translation?.defaultSeoDescription;
const localizedBrandPrompt = translation?.brandPrompt;
const headerBehavior = (settings.themeTokens as any)?.headerBehavior ?? 'hide_on_scroll';
const mobileHeaderBehavior = (settings.themeTokens as any)?.mobileHeaderBehavior ?? headerBehavior;
@@ -169,6 +179,7 @@ export const settingsRoute = new Elysia({ prefix: "/settings" })
defaultSeoTitle: localizedSeoTitle ?? settingsData.defaultSeoTitle,
defaultSeoDescription: localizedSeoDescription ?? settingsData.defaultSeoDescription,
defaultOgImage: settingsData.defaultOgImage,
brandPrompt: localizedBrandPrompt ?? settingsData.brandPrompt,
};
} catch (err) {
console.error("[Settings] Error:", err);
@@ -342,6 +353,7 @@ export const settingsRoute = new Elysia({ prefix: "/settings" })
defaultSeoTitle: t.Optional(t.Nullable(t.String())),
defaultSeoDescription: t.Optional(t.Nullable(t.String())),
defaultOgImage: t.Optional(t.Nullable(t.String())),
brandPrompt: t.Optional(t.Nullable(t.String())),
translations: t.Optional(t.Any()),
})
})
@@ -426,6 +438,7 @@ export const settingsRoute = new Elysia({ prefix: "/settings" })
defaultSeoTitle: t.Optional(t.Nullable(t.String())),
defaultSeoDescription: t.Optional(t.Nullable(t.String())),
defaultOgImage: t.Optional(t.Nullable(t.String())),
brandPrompt: t.Optional(t.Nullable(t.String())),
translations: t.Optional(t.Any()),
})
})