feat: AI-powered login page with daily greeting and animated blob background
- Add public /api/greeting endpoint that generates a daily AI greeting (cached per day) - Replace static hero text with dynamic AI greeting (ChatGPT/Claude style) - Add Stripe-style animated gradient blobs with 14 daily-rotating color palettes - Use Plus Jakarta Sans font and Sparkles icon for modern AI look - Update subtitle to "Driven by multiple AI agents" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import { errorHandler } from "./middlewares/error.middleware";
|
||||
import { apiLogger } from "./middlewares/logger.middleware";
|
||||
import authRoutes from "./routes/auth";
|
||||
import twilioWebhookRoutes from "./routes/twilio-webhooks";
|
||||
import greetingRoutes from "./routes/greeting";
|
||||
import { authenticateJWT } from "./middlewares/auth.middleware";
|
||||
import dotenv from "dotenv";
|
||||
import { startBackupCron } from "./cron/backupCheck";
|
||||
@@ -73,6 +74,7 @@ app.use(
|
||||
app.use("/uploads", express.static(path.join(process.cwd(), "uploads")));
|
||||
|
||||
app.use("/api/auth", authRoutes);
|
||||
app.use("/api/greeting", greetingRoutes);
|
||||
// Twilio webhooks are public — Twilio sends no JWT token
|
||||
app.use("/api/twilio", express.urlencoded({ extended: false }), twilioWebhookRoutes);
|
||||
// All other API routes require JWT
|
||||
|
||||
61
apps/Backend/src/routes/greeting.ts
Normal file
61
apps/Backend/src/routes/greeting.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import express, { Request, Response } from "express";
|
||||
import { prisma as db } from "@repo/db/client";
|
||||
import { resolveAiProvider, getLlm } from "../ai/llm-factory";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const FALLBACK_GREETINGS = [
|
||||
"How can I help you today?",
|
||||
"What can I do for you today?",
|
||||
"Ready when you are.",
|
||||
"What's on your mind today?",
|
||||
"Let's get started.",
|
||||
];
|
||||
|
||||
let cachedGreeting: { text: string; date: string } | null = null;
|
||||
|
||||
function todayKey() {
|
||||
return new Date().toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
router.get("/", async (_req: Request, res: Response): Promise<any> => {
|
||||
try {
|
||||
const today = todayKey();
|
||||
|
||||
if (cachedGreeting && cachedGreeting.date === today) {
|
||||
return res.status(200).json({ greeting: cachedGreeting.text });
|
||||
}
|
||||
|
||||
const aiSettings = await db.aiSettings.findFirst();
|
||||
const activeAi = aiSettings ? resolveAiProvider(aiSettings) : null;
|
||||
|
||||
if (!activeAi) {
|
||||
const fallback = FALLBACK_GREETINGS[Math.floor(Math.random() * FALLBACK_GREETINGS.length)];
|
||||
return res.status(200).json({ greeting: fallback });
|
||||
}
|
||||
|
||||
const llm = getLlm(activeAi.provider, activeAi.key, activeAi.model);
|
||||
const result = await llm.invoke([
|
||||
{
|
||||
role: "system",
|
||||
content: "Generate a single short AI greeting, similar to how ChatGPT or Claude greets users. Keep it to one short sentence. Examples of the style: 'How can I help you today?', 'What's on your mind?', 'Ready when you are.', 'What can I do for you today?'. Be warm but concise. Do not use emojis. Do not mention names. Vary each greeting.",
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: "Generate a short greeting.",
|
||||
},
|
||||
]);
|
||||
|
||||
const greeting = typeof result.content === "string"
|
||||
? result.content.trim()
|
||||
: FALLBACK_GREETINGS[0]!;
|
||||
|
||||
cachedGreeting = { text: greeting, date: today };
|
||||
return res.status(200).json({ greeting });
|
||||
} catch {
|
||||
const fallback = FALLBACK_GREETINGS[Math.floor(Math.random() * FALLBACK_GREETINGS.length)];
|
||||
return res.status(200).json({ greeting: fallback });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user