diff --git a/server/index.js b/server/index.js new file mode 100644 index 0000000..4efaba1 --- /dev/null +++ b/server/index.js @@ -0,0 +1,71 @@ +import Fastify from 'fastify' +import fastifyStatic from '@fastify/static' +import { readFile, writeFile, mkdir } from 'fs/promises' +import { join, dirname } from 'path' +import { fileURLToPath } from 'url' +import { existsSync } from 'fs' + +const __dirname = dirname(fileURLToPath(import.meta.url)) +const ROOT = join(__dirname, '..') +const DIST = join(ROOT, 'dist') +const SAVES_DIR = join(ROOT, 'data', 'saves') + +// Ensure saves directory exists +await mkdir(SAVES_DIR, { recursive: true }) + +const fastify = Fastify({ logger: false }) + +// Serve built frontend +await fastify.register(fastifyStatic, { + root: DIST, + prefix: '/', +}) + +// Load save state +fastify.get('/api/save/:token', async (req, reply) => { + const { token } = req.params + if (!/^[0-9a-f]{8}$/.test(token)) { + return reply.code(400).send({ error: 'Invalid token' }) + } + const path = join(SAVES_DIR, `${token}.json`) + if (!existsSync(path)) { + return reply.code(404).send({ error: 'Save not found' }) + } + try { + const raw = await readFile(path, 'utf8') + return reply.send(JSON.parse(raw)) + } catch { + return reply.code(500).send({ error: 'Failed to read save' }) + } +}) + +// Write save state +fastify.post('/api/save/:token', async (req, reply) => { + const { token } = req.params + if (!/^[0-9a-f]{8}$/.test(token)) { + return reply.code(400).send({ error: 'Invalid token' }) + } + if (!req.body || typeof req.body !== 'object') { + return reply.code(400).send({ error: 'Invalid body' }) + } + const path = join(SAVES_DIR, `${token}.json`) + try { + await writeFile(path, JSON.stringify(req.body, null, 2), 'utf8') + return reply.send({ ok: true }) + } catch { + return reply.code(500).send({ error: 'Failed to write save' }) + } +}) + +// Fallback — serve index.html for client-side routing +fastify.setNotFoundHandler((req, reply) => { + reply.sendFile('index.html') +}) + +try { + await fastify.listen({ port: 3000, host: '0.0.0.0' }) + console.log('OTIVM server running on port 3000') +} catch (err) { + console.error(err) + process.exit(1) +}