const express = require('express'); const { Pool } = require('pg'); const path = require('path'); const fs = require('fs'); const app = express(); app.use(express.json()); const PORT = parseInt(process.env.PORT || '3070'); const BASE = (process.env.BASE_PATH || '/test').replace(/\/$/, ''); const DEFAULT_TEXT = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.'; // ── Postgres ────────────────────────────────────────────────────────────── const pgPass = process.env.PG_PASS || (fs.existsSync('/root/pg-appuser.env') ? fs.readFileSync('/root/pg-appuser.env', 'utf8').match(/APP_PASS=(.*)/)?.[1]?.trim() : null); const pool = new Pool({ host: '127.0.0.1', port: 5432, database: process.env.PG_DB || 'testapp', user: process.env.PG_USER || 'appuser', password: pgPass, }); async function initDb() { await pool.query(` CREATE TABLE IF NOT EXISTS card_text ( id INT PRIMARY KEY DEFAULT 1, content TEXT NOT NULL DEFAULT '', updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); INSERT INTO card_text (id, content) VALUES (1, $1) ON CONFLICT (id) DO NOTHING; `, [DEFAULT_TEXT]); } // ── CORS ────────────────────────────────────────────────────────────────── app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Headers', 'Content-Type'); res.header('Access-Control-Allow-Methods', 'GET, PUT, DELETE, OPTIONS'); if (req.method === 'OPTIONS') return res.sendStatus(200); next(); }); // ── GET /api/text ───────────────────────────────────────────────────────── app.get(['/api/text', BASE + '/api/text'], async (req, res) => { try { const { rows } = await pool.query('SELECT content, updated_at FROM card_text WHERE id = 1'); res.json(rows[0] || { content: DEFAULT_TEXT, updated_at: null }); } catch (e) { res.status(500).json({ error: e.message }); } }); // ── PUT /api/text — create / update ────────────────────────────────────── app.put(['/api/text', BASE + '/api/text'], async (req, res) => { const { content } = req.body; if (!content?.trim()) return res.status(400).json({ error: 'content required' }); try { const { rows } = await pool.query( `INSERT INTO card_text (id, content, updated_at) VALUES (1, $1, NOW()) ON CONFLICT (id) DO UPDATE SET content = $1, updated_at = NOW() RETURNING content, updated_at`, [content.trim()] ); res.json(rows[0]); } catch (e) { res.status(500).json({ error: e.message }); } }); // ── DELETE /api/text — reset to default ────────────────────────────────── app.delete(['/api/text', BASE + '/api/text'], async (req, res) => { try { const { rows } = await pool.query( `UPDATE card_text SET content = $1, updated_at = NOW() WHERE id = 1 RETURNING content, updated_at`, [DEFAULT_TEXT] ); res.json(rows[0] || { content: DEFAULT_TEXT }); } catch (e) { res.status(500).json({ error: e.message }); } }); // ── Serve index.html ────────────────────────────────────────────────────── app.get([BASE, BASE + '/', BASE + '/index.html'], (req, res) => { res.sendFile(path.join(__dirname, 'index.html')); }); // ── Boot ────────────────────────────────────────────────────────────────── initDb() .then(() => app.listen(PORT, '127.0.0.1', () => console.log(`test on :${PORT} at ${BASE}`))) .catch(err => { console.error('DB init failed:', err); process.exit(1); });