- Notes: per-story rich-text notes panel (right drawer) with TipTap editor, image support, autosave, and full CRUD API - Font picker: 15 Google Fonts selectable from a floating Aa button, persisted to localStorage via --font-body CSS variable - Sticky toolbar: pulled formatting bar out of overflow:hidden wrapper so it sticks below the topbar while scrolling - Prompts: 100 additional built-in prompts (120 total) in a shuffled no-repeat queue; pre-fetch on page load so the AI has time to respond; timeout raised to 45s; error logging + /api/prompts/test debug endpoint; source badge shows whether prompt came from AI or built-in list Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
58 lines
2.0 KiB
JavaScript
58 lines
2.0 KiB
JavaScript
import Database from 'better-sqlite3'
|
|
import { mkdirSync } from 'fs'
|
|
import path from 'path'
|
|
import { fileURLToPath } from 'url'
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
const dataDir = path.join(__dirname, 'data')
|
|
mkdirSync(dataDir, { recursive: true })
|
|
|
|
const db = new Database(path.join(dataDir, 'stories.db'))
|
|
db.pragma('journal_mode = WAL')
|
|
db.pragma('foreign_keys = ON')
|
|
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL,
|
|
username TEXT UNIQUE NOT NULL COLLATE NOCASE,
|
|
password TEXT NOT NULL,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS stories (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
title TEXT NOT NULL DEFAULT 'Untitled Story',
|
|
content TEXT DEFAULT '{}',
|
|
cover_image TEXT,
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS images (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
filename TEXT NOT NULL,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS notes (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
story_id INTEGER NOT NULL REFERENCES stories(id) ON DELETE CASCADE,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
title TEXT NOT NULL DEFAULT 'Untitled Note',
|
|
content TEXT DEFAULT '{}',
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`)
|
|
|
|
// Migrate: rename email -> username for existing databases
|
|
const userCols = db.pragma('table_info(users)').map(c => c.name)
|
|
if (userCols.includes('email') && !userCols.includes('username')) {
|
|
db.exec('ALTER TABLE users RENAME COLUMN email TO username')
|
|
}
|
|
|
|
export default db
|