165 lines
4.9 KiB
JavaScript
165 lines
4.9 KiB
JavaScript
import express from 'express';
|
|
import cors from 'cors';
|
|
import dotenv from 'dotenv';
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import { GoogleGenerativeAI } from '@google/generative-ai';
|
|
import { getPromptForActivity } from './prompts.js';
|
|
|
|
dotenv.config();
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
const app = express();
|
|
const port = 3001;
|
|
|
|
app.use(cors());
|
|
app.use(express.json());
|
|
|
|
// Initialize Gemini API
|
|
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || '');
|
|
|
|
// ============================================
|
|
// GALLERY STORAGE - Recent 6 generations
|
|
// ============================================
|
|
const GALLERY_FILE = path.join(__dirname, 'gallery.json');
|
|
const MAX_GALLERY_SIZE = 6;
|
|
|
|
// Load gallery from file
|
|
function loadGallery() {
|
|
try {
|
|
if (fs.existsSync(GALLERY_FILE)) {
|
|
const data = fs.readFileSync(GALLERY_FILE, 'utf8');
|
|
return JSON.parse(data);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading gallery:', error);
|
|
}
|
|
return [];
|
|
}
|
|
|
|
// Save gallery to file
|
|
function saveGallery(gallery) {
|
|
try {
|
|
fs.writeFileSync(GALLERY_FILE, JSON.stringify(gallery, null, 2));
|
|
} catch (error) {
|
|
console.error('Error saving gallery:', error);
|
|
}
|
|
}
|
|
|
|
// Add item to gallery (keeps only recent 6)
|
|
function addToGallery(item) {
|
|
const gallery = loadGallery();
|
|
|
|
// Add new item at the beginning
|
|
gallery.unshift({
|
|
...item,
|
|
id: Date.now(),
|
|
createdAt: new Date().toISOString()
|
|
});
|
|
|
|
// Keep only the most recent 6
|
|
while (gallery.length > MAX_GALLERY_SIZE) {
|
|
gallery.pop();
|
|
}
|
|
|
|
saveGallery(gallery);
|
|
return gallery;
|
|
}
|
|
|
|
// ============================================
|
|
// API ENDPOINTS
|
|
// ============================================
|
|
|
|
// Get recent gallery items
|
|
app.get('/api/gallery', (req, res) => {
|
|
try {
|
|
const gallery = loadGallery();
|
|
res.json(gallery);
|
|
} catch (error) {
|
|
console.error('Gallery fetch error:', error);
|
|
res.status(500).json({ error: 'Failed to fetch gallery' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/surprise', async (req, res) => {
|
|
try {
|
|
if (!process.env.GEMINI_API_KEY) {
|
|
return res.status(500).json({ error: 'Missing API Key' });
|
|
}
|
|
const model = genAI.getGenerativeModel({ model: "gemini-2.5-pro" });
|
|
const prompt = `
|
|
Generate ONE creative, fun, specific theme for a children's worksheet (e.g. "Space-Rex Coding", "Underwater Hamster Tea Party").
|
|
Pick a random age between 3 and 7.
|
|
Pick a random activity type from: coloring, numbers, writing, find-way, counting.
|
|
|
|
Return JSON ONLY:
|
|
{
|
|
"theme": "string",
|
|
"age": number,
|
|
"section": "string"
|
|
}
|
|
`;
|
|
|
|
const result = await model.generateContent(prompt);
|
|
const text = result.response.text();
|
|
const jsonStr = text.replace(/```json/g, '').replace(/```/g, '').trim();
|
|
res.json(JSON.parse(jsonStr));
|
|
} catch (error) {
|
|
console.error('Surprise error:', error);
|
|
res.status(500).json({ error: 'Failed' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/generate', async (req, res) => {
|
|
try {
|
|
const { theme, age, sections } = req.body;
|
|
console.log('Received generation request:', { theme, age, sections });
|
|
|
|
if (!process.env.GEMINI_API_KEY) {
|
|
console.warn('Missing GEMINI_API_KEY');
|
|
return res.status(500).json({ error: 'Server configuration error: Missing API Key' });
|
|
}
|
|
|
|
const model = genAI.getGenerativeModel({ model: "gemini-2.5-pro" });
|
|
|
|
// Get the activity-specific prompt for the first section
|
|
const activityType = sections[0] || 'coloring';
|
|
const prompt = getPromptForActivity(activityType, theme, age);
|
|
|
|
console.log(`Using specialized prompt for activity: ${activityType}`);
|
|
|
|
const result = await model.generateContent(prompt);
|
|
const response = await result.response;
|
|
const text = response.text();
|
|
|
|
// Cleanup markdown code blocks if present
|
|
const jsonStr = text.replace(/```json/g, '').replace(/```/g, '').trim();
|
|
const data = JSON.parse(jsonStr);
|
|
|
|
if (!data.title || !data.sections || !Array.isArray(data.sections)) {
|
|
throw new Error('Invalid generation format');
|
|
}
|
|
|
|
// Save to gallery
|
|
const galleryItem = {
|
|
...data,
|
|
age,
|
|
activityType
|
|
};
|
|
addToGallery(galleryItem);
|
|
console.log('Added to gallery, current size:', loadGallery().length);
|
|
|
|
res.json(data);
|
|
} catch (error) {
|
|
console.error('Generation error:', error);
|
|
res.status(500).json({ error: 'Failed to generate content' });
|
|
}
|
|
});
|
|
|
|
app.listen(port, () => {
|
|
console.log(`Server running at http://localhost:${port}`);
|
|
});
|