${escapeHtml(entry.message)}
The guestbook
Leave a note for whoever arrives next.
${entries.length} ${entries.length === 1 ? 'entry' : 'entries'}
Running since ${escapeHtml(bootedAt.toISOString())}
const express = require('express');
const fs = require('node:fs');
const path = require('node:path');
const { DatabaseSync } = require('node:sqlite');
const app = express();
const bootedAt = new Date();
const dataDir = process.env.TOKAY_DATA_DIR || path.join(__dirname, 'data');
fs.mkdirSync(dataDir, { recursive: true });
const database = new DatabaseSync(path.join(dataDir, 'guestbook.sqlite'));
database.exec(`
CREATE TABLE IF NOT EXISTS entries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
message TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
)
`);
const entryCount = database.prepare('SELECT COUNT(*) AS count FROM entries').get().count;
if (entryCount === 0) {
database.prepare('INSERT INTO entries (name, message) VALUES (?, ?)')
.run('Your host', 'Welcome. Sign below.');
}
app.use(express.urlencoded({ extended: false }));
const escapeHtml = (value) => String(value)
.replaceAll('&', '&')
.replaceAll('<', '<')
.replaceAll('>', '>')
.replaceAll('"', '"')
.replaceAll("'", ''');
const page = ({ error = '', draft = {} } = {}) => {
const entries = database.prepare(`
SELECT id, name, message, created_at
FROM entries
ORDER BY id DESC
`).all();
const cards = entries.map((entry) => `
${escapeHtml(entry.message)}
Leave a note for whoever arrives next.
${entries.length} ${entries.length === 1 ? 'entry' : 'entries'}
Running since ${escapeHtml(bootedAt.toISOString())}