- React + TipTap editor with formatting toolbar (bold, italic, underline, strikethrough, alignment, highlight, scene breaks) - Custom image node view with resize and alignment controls; server-side WebP conversion via sharp - Express + SQLite backend with JWT auth and admin user management - Export to PDF, EPUB, and ODT - Five themes (Midnight, Gothic Night, Enchanted Forest, Aged Manuscript, Neon Noir); Lora body font for readability - Writing streak, daily word goal, milestones, and Ollama writing prompts - Docker Compose setup for self-hosted deployment behind NPMplus Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
57 lines
3.3 KiB
JavaScript
57 lines
3.3 KiB
JavaScript
import { Router } from 'express'
|
||
import { auth } from '../middleware/auth.js'
|
||
|
||
const OLLAMA_URL = process.env.OLLAMA_URL || 'http://localhost:11434'
|
||
const OLLAMA_MODEL = process.env.OLLAMA_MODEL || 'llama3.2'
|
||
|
||
const FALLBACK = [
|
||
"A letter arrives addressed to you — dated ten years in the future. It says only: 'Don't go to the old lighthouse on Friday.'",
|
||
"Your new neighbour asks you to water one plant while they're away. On day two, it whispers your name.",
|
||
"Every mirror in the city goes dark overnight. Yours still shows a reflection — but it isn't yours.",
|
||
"You find your grandmother's diary from when she was your age. The last entry describes something happening to you. Today.",
|
||
"A bookshop appears on your street that wasn't there yesterday. Inside are books about your life — including things that haven't happened yet.",
|
||
"The last dragon in the world lands in your garden. It is very small and very scared.",
|
||
"You win a competition you never entered. The prize is one hour of flying.",
|
||
"Everyone in your town wakes up speaking a different language. Everyone except you.",
|
||
"There is a trapdoor under your bed that opens onto an upside-down forest.",
|
||
"Every story you write comes true — but always with one small, wrong detail.",
|
||
"The lighthouse has been dark for fifty years. The night you finally climb to the top, you find the lamp still warm.",
|
||
"Two kingdoms at war agree to peace. Each must send their most precious thing as a gift. Both kingdoms send the same person.",
|
||
"Your shadow starts leaving notes under your pillow.",
|
||
"The old tree at the centre of the village is cut down. Inside is a room. Inside the room is a child who has never seen the sky.",
|
||
"You are the only one who can hear the sea talking. It is furious about something.",
|
||
"A train arrives at your station that isn't on any timetable. The conductor says you have been expected.",
|
||
"You find a photograph of your house — taken fifty years before it was built.",
|
||
"The stars rearrange themselves every night. Last night you finally worked out what language they're writing in.",
|
||
"A girl discovers she can rewind the last ten seconds of any conversation — but only once per person.",
|
||
"The town clockmaker stops every clock at midnight. By morning, she is gone. So is yesterday.",
|
||
]
|
||
|
||
const router = Router()
|
||
router.use(auth)
|
||
|
||
router.get('/', async (req, res) => {
|
||
try {
|
||
const r = await fetch(`${OLLAMA_URL}/api/generate`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
model: OLLAMA_MODEL,
|
||
prompt: 'Write one creative writing prompt for a young writer aged 10–14. Make it imaginative, specific, and intriguing — a little mysterious or adventurous. Write only the prompt itself: no introduction, no explanation, no quotation marks. Maximum two sentences.',
|
||
stream: false,
|
||
}),
|
||
signal: AbortSignal.timeout(8000),
|
||
})
|
||
if (!r.ok) throw new Error('bad status')
|
||
const data = await r.json()
|
||
const prompt = data.response?.trim()
|
||
if (!prompt) throw new Error('empty')
|
||
res.json({ prompt, source: 'ollama', model: OLLAMA_MODEL })
|
||
} catch {
|
||
const prompt = FALLBACK[Math.floor(Math.random() * FALLBACK.length)]
|
||
res.json({ prompt, source: 'local' })
|
||
}
|
||
})
|
||
|
||
export default router
|