Initial commit: Character sandbox with React+Express frontend/backend, SvelteKit foundation
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import { Router } from 'express';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import db from '../db.js';
|
||||
import { authenticateToken } from '../middleware/auth.js';
|
||||
import { processTrainingPrompt, generateCharacterSuggestion, DEFAULT_KNOWLEDGE } from '../ai/trainer.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post('/suggest', authenticateToken, async (req, res) => {
|
||||
const { prompt } = req.body;
|
||||
if (!prompt) return res.status(400).json({ error: 'Prompt is required' });
|
||||
|
||||
const suggestion = await processTrainingPrompt(prompt, req.userId);
|
||||
res.json({ suggestion });
|
||||
});
|
||||
|
||||
router.post('/generate-name', authenticateToken, (req, res) => {
|
||||
const { style } = req.body;
|
||||
const name = generateCharacterSuggestion('generate name', req.userId).name;
|
||||
res.json({ name });
|
||||
});
|
||||
|
||||
router.post('/train', authenticateToken, (req, res) => {
|
||||
const { prompt, response, category } = req.body;
|
||||
if (!prompt || !response) return res.status(400).json({ error: 'Prompt and response are required' });
|
||||
|
||||
const id = uuidv4();
|
||||
db.prepare('INSERT INTO ai_training_data (id, user_id, prompt, response, category) VALUES (?, ?, ?, ?, ?)')
|
||||
.run(id, req.userId, prompt, response, category || 'general');
|
||||
|
||||
res.status(201).json({ message: 'Training data added', id });
|
||||
});
|
||||
|
||||
router.get('/training-data', authenticateToken, (req, res) => {
|
||||
const data = db.prepare('SELECT id, prompt, response, category, created_at FROM ai_training_data WHERE user_id = ? ORDER BY created_at DESC').all(req.userId);
|
||||
res.json({ training_data: data });
|
||||
});
|
||||
|
||||
router.delete('/training-data/:id', authenticateToken, (req, res) => {
|
||||
const existing = db.prepare('SELECT * FROM ai_training_data WHERE id = ? AND user_id = ?').get(req.params.id, req.userId);
|
||||
if (!existing) return res.status(404).json({ error: 'Training data not found' });
|
||||
db.prepare('DELETE FROM ai_training_data WHERE id = ?').run(req.params.id);
|
||||
res.json({ message: 'Training data deleted' });
|
||||
});
|
||||
|
||||
router.get('/knowledge', authenticateToken, (req, res) => {
|
||||
const archetypes = DEFAULT_KNOWLEDGE.character_archetypes.map(a => ({
|
||||
name: a.name,
|
||||
traits: a.traits,
|
||||
needs_priority: a.needs_priority
|
||||
}));
|
||||
res.json({ archetypes, name_styles: Object.keys(DEFAULT_KNOWLEDGE.name_generators) });
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,49 @@
|
||||
import { Router } from 'express';
|
||||
import bcrypt from 'bcrypt';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import db from '../db.js';
|
||||
import { generateToken, authenticateToken } from '../middleware/auth.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post('/register', (req, res) => {
|
||||
const { username, email, password } = req.body;
|
||||
if (!username || !email || !password) {
|
||||
return res.status(400).json({ error: 'Username, email, and password are required' });
|
||||
}
|
||||
|
||||
const existing = db.prepare('SELECT id FROM users WHERE username = ? OR email = ?').get(username, email);
|
||||
if (existing) {
|
||||
return res.status(409).json({ error: 'Username or email already exists' });
|
||||
}
|
||||
|
||||
const id = uuidv4();
|
||||
const passwordHash = bcrypt.hashSync(password, 10);
|
||||
db.prepare('INSERT INTO users (id, username, email, password_hash) VALUES (?, ?, ?, ?)').run(id, username, email, passwordHash);
|
||||
|
||||
const token = generateToken(id);
|
||||
res.status(201).json({ token, user: { id, username, email } });
|
||||
});
|
||||
|
||||
router.post('/login', (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
if (!username || !password) {
|
||||
return res.status(400).json({ error: 'Username and password are required' });
|
||||
}
|
||||
|
||||
const user = db.prepare('SELECT * FROM users WHERE username = ?').get(username);
|
||||
if (!user || !bcrypt.compareSync(password, user.password_hash)) {
|
||||
return res.status(401).json({ error: 'Invalid credentials' });
|
||||
}
|
||||
|
||||
const token = generateToken(user.id);
|
||||
res.json({ token, user: { id: user.id, username: user.username, email: user.email } });
|
||||
});
|
||||
|
||||
router.get('/me', authenticateToken, (req, res) => {
|
||||
const user = db.prepare('SELECT id, username, email, created_at FROM users WHERE id = ?').get(req.userId);
|
||||
if (!user) return res.status(404).json({ error: 'User not found' });
|
||||
res.json({ user });
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,238 @@
|
||||
import { Router } from 'express';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import db from '../db.js';
|
||||
import { authenticateToken } from '../middleware/auth.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/', authenticateToken, (req, res) => {
|
||||
const characters = db.prepare('SELECT * FROM characters WHERE user_id = ? ORDER BY updated_at DESC').all(req.userId);
|
||||
res.json({ characters });
|
||||
});
|
||||
|
||||
router.post('/', authenticateToken, (req, res) => {
|
||||
const { name, description, personality_traits, backstory } = req.body;
|
||||
if (!name) return res.status(400).json({ error: 'Name is required' });
|
||||
|
||||
const id = uuidv4();
|
||||
const traits = JSON.stringify(personality_traits || []);
|
||||
db.prepare('INSERT INTO characters (id, user_id, name, description, personality_traits, backstory) VALUES (?, ?, ?, ?, ?, ?)')
|
||||
.run(id, req.userId, name, description || '', traits, backstory || '');
|
||||
|
||||
const character = db.prepare('SELECT * FROM characters WHERE id = ?').get(id);
|
||||
res.status(201).json({ character });
|
||||
});
|
||||
|
||||
router.get('/:id', authenticateToken, (req, res) => {
|
||||
const character = db.prepare('SELECT * FROM characters WHERE id = ? AND user_id = ?').get(req.params.id, req.userId);
|
||||
if (!character) return res.status(404).json({ error: 'Character not found' });
|
||||
|
||||
const needs = db.prepare('SELECT * FROM character_needs WHERE character_id = ?').all(req.params.id);
|
||||
const uiElements = db.prepare('SELECT * FROM character_ui_elements WHERE character_id = ?').all(req.params.id);
|
||||
const brainRules = db.prepare('SELECT * FROM character_brain_rules WHERE character_id = ?').all(req.params.id);
|
||||
|
||||
res.json({ character, needs, ui_elements: uiElements, brain_rules: brainRules });
|
||||
});
|
||||
|
||||
router.put('/:id', authenticateToken, (req, res) => {
|
||||
const { name, description, personality_traits, backstory, avatar_url } = req.body;
|
||||
const existing = db.prepare('SELECT * FROM characters WHERE id = ? AND user_id = ?').get(req.params.id, req.userId);
|
||||
if (!existing) return res.status(404).json({ error: 'Character not found' });
|
||||
|
||||
db.prepare('UPDATE characters SET name = ?, description = ?, personality_traits = ?, backstory = ?, avatar_url = ?, updated_at = datetime(\'now\') WHERE id = ?')
|
||||
.run(
|
||||
name || existing.name,
|
||||
description !== undefined ? description : existing.description,
|
||||
personality_traits ? JSON.stringify(personality_traits) : existing.personality_traits,
|
||||
backstory !== undefined ? backstory : existing.backstory,
|
||||
avatar_url !== undefined ? avatar_url : existing.avatar_url,
|
||||
req.params.id
|
||||
);
|
||||
|
||||
const character = db.prepare('SELECT * FROM characters WHERE id = ?').get(req.params.id);
|
||||
res.json({ character });
|
||||
});
|
||||
|
||||
router.delete('/:id', authenticateToken, (req, res) => {
|
||||
const existing = db.prepare('SELECT * FROM characters WHERE id = ? AND user_id = ?').get(req.params.id, req.userId);
|
||||
if (!existing) return res.status(404).json({ error: 'Character not found' });
|
||||
|
||||
db.prepare('DELETE FROM characters WHERE id = ?').run(req.params.id);
|
||||
res.json({ message: 'Character deleted' });
|
||||
});
|
||||
|
||||
// --- Needs ---
|
||||
|
||||
router.get('/:id/needs', authenticateToken, (req, res) => {
|
||||
const needs = db.prepare('SELECT * FROM character_needs WHERE character_id = ?').all(req.params.id);
|
||||
res.json({ needs });
|
||||
});
|
||||
|
||||
router.post('/:id/needs', authenticateToken, (req, res) => {
|
||||
const { name, enabled, initial_value, min_value, max_value, decay_rate, priority } = req.body;
|
||||
if (!name) return res.status(400).json({ error: 'Need name is required' });
|
||||
|
||||
const needId = uuidv4();
|
||||
db.prepare('INSERT INTO character_needs (id, character_id, name, enabled, initial_value, min_value, max_value, decay_rate, priority) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)')
|
||||
.run(needId, req.params.id, name, enabled !== false ? 1 : 0, initial_value || 50, min_value || 0, max_value || 100, decay_rate || 1, priority || 0);
|
||||
|
||||
const need = db.prepare('SELECT * FROM character_needs WHERE id = ?').get(needId);
|
||||
res.status(201).json({ need });
|
||||
});
|
||||
|
||||
router.put('/:id/needs/:needId', authenticateToken, (req, res) => {
|
||||
const { name, enabled, initial_value, min_value, max_value, decay_rate, priority } = req.body;
|
||||
const existing = db.prepare('SELECT * FROM character_needs WHERE id = ? AND character_id = ?').get(req.params.needId, req.params.id);
|
||||
if (!existing) return res.status(404).json({ error: 'Need not found' });
|
||||
|
||||
db.prepare('UPDATE character_needs SET name = ?, enabled = ?, initial_value = ?, min_value = ?, max_value = ?, decay_rate = ?, priority = ? WHERE id = ?')
|
||||
.run(name || existing.name, enabled !== undefined ? (enabled ? 1 : 0) : existing.enabled, initial_value ?? existing.initial_value, min_value ?? existing.min_value, max_value ?? existing.max_value, decay_rate ?? existing.decay_rate, priority ?? existing.priority, req.params.needId);
|
||||
|
||||
const need = db.prepare('SELECT * FROM character_needs WHERE id = ?').get(req.params.needId);
|
||||
res.json({ need });
|
||||
});
|
||||
|
||||
router.delete('/:id/needs/:needId', authenticateToken, (req, res) => {
|
||||
const existing = db.prepare('SELECT * FROM character_needs WHERE id = ? AND character_id = ?').get(req.params.needId, req.params.id);
|
||||
if (!existing) return res.status(404).json({ error: 'Need not found' });
|
||||
db.prepare('DELETE FROM character_needs WHERE id = ?').run(req.params.needId);
|
||||
res.json({ message: 'Need deleted' });
|
||||
});
|
||||
|
||||
// --- UI Elements ---
|
||||
|
||||
router.get('/:id/ui-elements', authenticateToken, (req, res) => {
|
||||
const elements = db.prepare('SELECT * FROM character_ui_elements WHERE character_id = ?').all(req.params.id);
|
||||
res.json({ ui_elements: elements });
|
||||
});
|
||||
|
||||
router.post('/:id/ui-elements', authenticateToken, (req, res) => {
|
||||
const { need_id, element_type, config } = req.body;
|
||||
if (!element_type) return res.status(400).json({ error: 'Element type is required' });
|
||||
|
||||
const elementId = uuidv4();
|
||||
db.prepare('INSERT INTO character_ui_elements (id, character_id, need_id, element_type, config) VALUES (?, ?, ?, ?, ?)')
|
||||
.run(elementId, req.params.id, need_id || null, element_type, JSON.stringify(config || {}));
|
||||
|
||||
const element = db.prepare('SELECT * FROM character_ui_elements WHERE id = ?').get(elementId);
|
||||
res.status(201).json({ ui_element: element });
|
||||
});
|
||||
|
||||
router.put('/:id/ui-elements/:elementId', authenticateToken, (req, res) => {
|
||||
const { need_id, element_type, config } = req.body;
|
||||
const existing = db.prepare('SELECT * FROM character_ui_elements WHERE id = ? AND character_id = ?').get(req.params.elementId, req.params.id);
|
||||
if (!existing) return res.status(404).json({ error: 'UI element not found' });
|
||||
|
||||
db.prepare('UPDATE character_ui_elements SET need_id = ?, element_type = ?, config = ? WHERE id = ?')
|
||||
.run(need_id !== undefined ? need_id : existing.need_id, element_type || existing.element_type, config ? JSON.stringify(config) : existing.config, req.params.elementId);
|
||||
|
||||
const element = db.prepare('SELECT * FROM character_ui_elements WHERE id = ?').get(req.params.elementId);
|
||||
res.json({ ui_element: element });
|
||||
});
|
||||
|
||||
router.delete('/:id/ui-elements/:elementId', authenticateToken, (req, res) => {
|
||||
const existing = db.prepare('SELECT * FROM character_ui_elements WHERE id = ? AND character_id = ?').get(req.params.elementId, req.params.id);
|
||||
if (!existing) return res.status(404).json({ error: 'UI element not found' });
|
||||
db.prepare('DELETE FROM character_ui_elements WHERE id = ?').run(req.params.elementId);
|
||||
res.json({ message: 'UI element deleted' });
|
||||
});
|
||||
|
||||
// --- Brain Rules ---
|
||||
|
||||
router.get('/:id/brain-rules', authenticateToken, (req, res) => {
|
||||
const rules = db.prepare('SELECT * FROM character_brain_rules WHERE character_id = ?').all(req.params.id);
|
||||
res.json({ brain_rules: rules });
|
||||
});
|
||||
|
||||
router.post('/:id/brain-rules', authenticateToken, (req, res) => {
|
||||
const { condition, action, priority, enabled } = req.body;
|
||||
if (!condition || !action) return res.status(400).json({ error: 'Condition and action are required' });
|
||||
|
||||
const ruleId = uuidv4();
|
||||
db.prepare('INSERT INTO character_brain_rules (id, character_id, condition, action, priority, enabled) VALUES (?, ?, ?, ?, ?, ?)')
|
||||
.run(ruleId, req.params.id, JSON.stringify(condition), JSON.stringify(action), priority || 0, enabled !== false ? 1 : 0);
|
||||
|
||||
const rule = db.prepare('SELECT * FROM character_brain_rules WHERE id = ?').get(ruleId);
|
||||
res.status(201).json({ brain_rule: rule });
|
||||
});
|
||||
|
||||
router.put('/:id/brain-rules/:ruleId', authenticateToken, (req, res) => {
|
||||
const { condition, action, priority, enabled } = req.body;
|
||||
const existing = db.prepare('SELECT * FROM character_brain_rules WHERE id = ? AND character_id = ?').get(req.params.ruleId, req.params.id);
|
||||
if (!existing) return res.status(404).json({ error: 'Brain rule not found' });
|
||||
|
||||
db.prepare('UPDATE character_brain_rules SET condition = ?, action = ?, priority = ?, enabled = ? WHERE id = ?')
|
||||
.run(condition ? JSON.stringify(condition) : existing.condition, action ? JSON.stringify(action) : existing.action, priority ?? existing.priority, enabled !== undefined ? (enabled ? 1 : 0) : existing.enabled, req.params.ruleId);
|
||||
|
||||
const rule = db.prepare('SELECT * FROM character_brain_rules WHERE id = ?').get(req.params.ruleId);
|
||||
res.json({ brain_rule: rule });
|
||||
});
|
||||
|
||||
router.delete('/:id/brain-rules/:ruleId', authenticateToken, (req, res) => {
|
||||
const existing = db.prepare('SELECT * FROM character_brain_rules WHERE id = ? AND character_id = ?').get(req.params.ruleId, req.params.id);
|
||||
if (!existing) return res.status(404).json({ error: 'Brain rule not found' });
|
||||
db.prepare('DELETE FROM character_brain_rules WHERE id = ?').run(req.params.ruleId);
|
||||
res.json({ message: 'Brain rule deleted' });
|
||||
});
|
||||
|
||||
// --- Simulation ---
|
||||
|
||||
router.post('/:id/simulate', authenticateToken, (req, res) => {
|
||||
const { steps = 10, events = [] } = req.body;
|
||||
const needs = db.prepare('SELECT * FROM character_needs WHERE character_id = ? AND enabled = 1').all(req.params.id);
|
||||
const rules = db.prepare('SELECT * FROM character_brain_rules WHERE character_id = ? AND enabled = 1 ORDER BY priority DESC').all(req.params.id);
|
||||
|
||||
const simulation = [];
|
||||
let currentValues = {};
|
||||
needs.forEach(n => { currentValues[n.name] = { ...n }; });
|
||||
|
||||
for (let step = 0; step < steps; step++) {
|
||||
for (const need of needs) {
|
||||
if (currentValues[need.name]) {
|
||||
let newVal = currentValues[need.name].current_value || need.initial_value;
|
||||
newVal -= need.decay_rate;
|
||||
newVal = Math.max(need.min_value, Math.min(need.max_value, newVal));
|
||||
currentValues[need.name].current_value = newVal;
|
||||
}
|
||||
}
|
||||
|
||||
for (const event of events) {
|
||||
for (const need of needs) {
|
||||
if (event[need.name]) {
|
||||
let val = currentValues[need.name].current_value || need.initial_value;
|
||||
val += event[need.name];
|
||||
val = Math.max(need.min_value, Math.min(need.max_value, val));
|
||||
currentValues[need.name].current_value = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const triggeredRules = [];
|
||||
for (const rule of rules) {
|
||||
const cond = JSON.parse(rule.condition);
|
||||
const needVal = currentValues[cond.need]?.current_value ?? need.initial_value;
|
||||
if (cond.operator === 'lt' && needVal < cond.value) {
|
||||
triggeredRules.push(rule);
|
||||
} else if (cond.operator === 'gt' && needVal > cond.value) {
|
||||
triggeredRules.push(rule);
|
||||
} else if (cond.operator === 'eq' && needVal === cond.value) {
|
||||
triggeredRules.push(rule);
|
||||
} else if (cond.operator === 'lte' && needVal <= cond.value) {
|
||||
triggeredRules.push(rule);
|
||||
} else if (cond.operator === 'gte' && needVal >= cond.value) {
|
||||
triggeredRules.push(rule);
|
||||
}
|
||||
}
|
||||
|
||||
const snapshot = {};
|
||||
for (const need of needs) {
|
||||
snapshot[need.name] = Math.round((currentValues[need.name].current_value || need.initial_value) * 100) / 100;
|
||||
}
|
||||
|
||||
simulation.push({ step, values: snapshot, triggered_rules: triggeredRules.map(r => ({ id: r.id, action: JSON.parse(r.action) })) });
|
||||
}
|
||||
|
||||
res.json({ simulation, final_values: simulation[simulation.length - 1]?.values });
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,84 @@
|
||||
import { Router } from 'express';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import db from '../db.js';
|
||||
import { authenticateToken } from '../middleware/auth.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/search', authenticateToken, (req, res) => {
|
||||
const { q, tag } = req.query;
|
||||
let query = `SELECT f.*, l.name as lorebook_name FROM fragments f JOIN lorebooks l ON f.lorebook_id = l.id WHERE l.user_id = ?`;
|
||||
const params = [req.userId];
|
||||
|
||||
if (q) {
|
||||
query += ` AND (f.title LIKE ? OR f.content LIKE ?)`;
|
||||
params.push(`%${q}%`, `%${q}%`);
|
||||
}
|
||||
if (tag) {
|
||||
query += ` AND f.tags LIKE ?`;
|
||||
params.push(`%"${tag}"%`);
|
||||
}
|
||||
|
||||
query += ` ORDER BY f.updated_at DESC LIMIT 50`;
|
||||
const fragments = db.prepare(query).all(...params);
|
||||
res.json({ fragments });
|
||||
});
|
||||
|
||||
router.get('/:id', authenticateToken, (req, res) => {
|
||||
const fragment = db.prepare(`
|
||||
SELECT f.*, l.name as lorebook_name, l.user_id
|
||||
FROM fragments f JOIN lorebooks l ON f.lorebook_id = l.id
|
||||
WHERE f.id = ? AND l.user_id = ?
|
||||
`).get(req.params.id, req.userId);
|
||||
if (!fragment) return res.status(404).json({ error: 'Fragment not found' });
|
||||
res.json({ fragment });
|
||||
});
|
||||
|
||||
router.post('/lorebook/:lorebookId', authenticateToken, (req, res) => {
|
||||
const { title, content, tags, linked_characters } = req.body;
|
||||
if (!title) return res.status(400).json({ error: 'Title is required' });
|
||||
|
||||
const lorebook = db.prepare('SELECT * FROM lorebooks WHERE id = ? AND user_id = ?').get(req.params.lorebookId, req.userId);
|
||||
if (!lorebook) return res.status(404).json({ error: 'Lorebook not found' });
|
||||
|
||||
const id = uuidv4();
|
||||
db.prepare('INSERT INTO fragments (id, lorebook_id, title, content, tags, linked_characters) VALUES (?, ?, ?, ?, ?, ?)')
|
||||
.run(id, req.params.lorebookId, title, content || '', JSON.stringify(tags || []), JSON.stringify(linked_characters || []));
|
||||
|
||||
const fragment = db.prepare('SELECT * FROM fragments WHERE id = ?').get(id);
|
||||
res.status(201).json({ fragment });
|
||||
});
|
||||
|
||||
router.put('/:id', authenticateToken, (req, res) => {
|
||||
const { title, content, tags, linked_characters } = req.body;
|
||||
const fragment = db.prepare(`
|
||||
SELECT f.* FROM fragments f JOIN lorebooks l ON f.lorebook_id = l.id
|
||||
WHERE f.id = ? AND l.user_id = ?
|
||||
`).get(req.params.id, req.userId);
|
||||
if (!fragment) return res.status(404).json({ error: 'Fragment not found' });
|
||||
|
||||
db.prepare('UPDATE fragments SET title = ?, content = ?, tags = ?, linked_characters = ?, updated_at = datetime(\'now\') WHERE id = ?')
|
||||
.run(
|
||||
title || fragment.title,
|
||||
content !== undefined ? content : fragment.content,
|
||||
tags ? JSON.stringify(tags) : fragment.tags,
|
||||
linked_characters ? JSON.stringify(linked_characters) : fragment.linked_characters,
|
||||
req.params.id
|
||||
);
|
||||
|
||||
const updated = db.prepare('SELECT * FROM fragments WHERE id = ?').get(req.params.id);
|
||||
res.json({ fragment: updated });
|
||||
});
|
||||
|
||||
router.delete('/:id', authenticateToken, (req, res) => {
|
||||
const fragment = db.prepare(`
|
||||
SELECT f.* FROM fragments f JOIN lorebooks l ON f.lorebook_id = l.id
|
||||
WHERE f.id = ? AND l.user_id = ?
|
||||
`).get(req.params.id, req.userId);
|
||||
if (!fragment) return res.status(404).json({ error: 'Fragment not found' });
|
||||
|
||||
db.prepare('DELETE FROM fragments WHERE id = ?').run(req.params.id);
|
||||
res.json({ message: 'Fragment deleted' });
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Router } from 'express';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import db from '../db.js';
|
||||
import { authenticateToken } from '../middleware/auth.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/', authenticateToken, (req, res) => {
|
||||
const lorebooks = db.prepare('SELECT * FROM lorebooks WHERE user_id = ? ORDER BY updated_at DESC').all(req.userId);
|
||||
res.json({ lorebooks });
|
||||
});
|
||||
|
||||
router.post('/', authenticateToken, (req, res) => {
|
||||
const { name, description } = req.body;
|
||||
if (!name) return res.status(400).json({ error: 'Name is required' });
|
||||
|
||||
const id = uuidv4();
|
||||
db.prepare('INSERT INTO lorebooks (id, user_id, name, description) VALUES (?, ?, ?, ?)').run(id, req.userId, name, description || '');
|
||||
const lorebook = db.prepare('SELECT * FROM lorebooks WHERE id = ?').get(id);
|
||||
res.status(201).json({ lorebook });
|
||||
});
|
||||
|
||||
router.get('/:id', authenticateToken, (req, res) => {
|
||||
const lorebook = db.prepare('SELECT * FROM lorebooks WHERE id = ? AND user_id = ?').get(req.params.id, req.userId);
|
||||
if (!lorebook) return res.status(404).json({ error: 'Lorebook not found' });
|
||||
|
||||
const fragments = db.prepare('SELECT * FROM fragments WHERE lorebook_id = ? ORDER BY created_at DESC').all(req.params.id);
|
||||
res.json({ lorebook, fragments });
|
||||
});
|
||||
|
||||
router.put('/:id', authenticateToken, (req, res) => {
|
||||
const { name, description } = req.body;
|
||||
const existing = db.prepare('SELECT * FROM lorebooks WHERE id = ? AND user_id = ?').get(req.params.id, req.userId);
|
||||
if (!existing) return res.status(404).json({ error: 'Lorebook not found' });
|
||||
|
||||
db.prepare('UPDATE lorebooks SET name = ?, description = ?, updated_at = datetime(\'now\') WHERE id = ?')
|
||||
.run(name || existing.name, description !== undefined ? description : existing.description, req.params.id);
|
||||
|
||||
const lorebook = db.prepare('SELECT * FROM lorebooks WHERE id = ?').get(req.params.id);
|
||||
res.json({ lorebook });
|
||||
});
|
||||
|
||||
router.delete('/:id', authenticateToken, (req, res) => {
|
||||
const existing = db.prepare('SELECT * FROM lorebooks WHERE id = ? AND user_id = ?').get(req.params.id, req.userId);
|
||||
if (!existing) return res.status(404).json({ error: 'Lorebook not found' });
|
||||
|
||||
db.prepare('DELETE FROM lorebooks WHERE id = ?').run(req.params.id);
|
||||
res.json({ message: 'Lorebook deleted' });
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user