From 7088e5e879613a624c826f897b786949b1981364 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 11 Aug 2026 19:32:55 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20TECHNO-TODO=20v1.0:=2090s=20cyberpu?= =?UTF-8?q?nk=20todo=20app=20with=20PostgreSQL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 12 ++ package.json | 14 +++ server.js | 78 +++++++++++++ views/index.ejs | 301 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 405 insertions(+) create mode 100644 Dockerfile create mode 100644 package.json create mode 100644 server.js create mode 100644 views/index.ejs diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..02f3f11 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM node:22-alpine + +WORKDIR /app + +COPY package*.json ./ +RUN npm install --production + +COPY . . + +EXPOSE 3000 + +CMD ["node", "server.js"] diff --git a/package.json b/package.json new file mode 100644 index 0000000..7350273 --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "name": "techno-todo", + "version": "1.0.0", + "description": "⚡ 90s Techno-Style Todo App", + "main": "server.js", + "scripts": { + "start": "node server.js" + }, + "dependencies": { + "express": "^4.21.0", + "pg": "^8.13.0", + "ejs": "^3.1.10" + } +} diff --git a/server.js b/server.js new file mode 100644 index 0000000..9964897 --- /dev/null +++ b/server.js @@ -0,0 +1,78 @@ +const express = require('express'); +const { Pool } = require('pg'); +const path = require('path'); + +const app = express(); +const PORT = process.env.PORT || 3000; + +// PostgreSQL connection +const pool = new Pool({ + connectionString: process.env.DATABASE_URL, + ssl: process.env.DB_SSL === 'true' ? { rejectUnauthorized: false } : false, +}); + +// Initialize DB table +async function initDB() { + const client = await pool.connect(); + try { + await client.query(` + CREATE TABLE IF NOT EXISTS todos ( + id SERIAL PRIMARY KEY, + text TEXT NOT NULL, + done BOOLEAN DEFAULT FALSE, + created_at TIMESTAMP DEFAULT NOW() + ) + `); + console.log('⚡ DATABASE ONLINE ⚡'); + } finally { + client.release(); + } +} + +app.use(express.urlencoded({ extended: true })); +app.use(express.static(path.join(__dirname, 'public'))); +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Routes +app.get('/', async (req, res) => { + try { + const result = await pool.query('SELECT * FROM todos ORDER BY created_at DESC'); + res.render('index', { todos: result.rows, error: null }); + } catch (err) { + res.render('index', { todos: [], error: 'DATABASE CONNECTION LOST ⚠' }); + } +}); + +app.post('/add', async (req, res) => { + const { text } = req.body; + if (text && text.trim()) { + await pool.query('INSERT INTO todos (text) VALUES ($1)', [text.trim()]); + } + res.redirect('/'); +}); + +app.post('/toggle/:id', async (req, res) => { + await pool.query('UPDATE todos SET done = NOT done WHERE id = $1', [req.params.id]); + res.redirect('/'); +}); + +app.post('/delete/:id', async (req, res) => { + await pool.query('DELETE FROM todos WHERE id = $1', [req.params.id]); + res.redirect('/'); +}); + +// Health check +app.get('/health', (req, res) => res.json({ status: 'ONLINE', db: 'CONNECTED' })); + +initDB().then(() => { + app.listen(PORT, () => { + console.log(`╔══════════════════════════════════════╗`); + console.log(`║ ⚡ TECHNO-TODO v1.0 ONLINE ⚡ ║`); + console.log(`║ PORT: ${PORT} ║`); + console.log(`╚══════════════════════════════════════╝`); + }); +}).catch(err => { + console.error('Failed to init DB:', err); + process.exit(1); +}); diff --git a/views/index.ejs b/views/index.ejs new file mode 100644 index 0000000..feb9935 --- /dev/null +++ b/views/index.ejs @@ -0,0 +1,301 @@ + + + + + + ⚡ TECHNO-TODO v1.0 + + + +
+
+

⚡ TECHNO-TODO

+
// version 1.0-beta :: neon edition
█ SYSTEM ACTIVE █
+
+ + <% if (error) { %> +
⚠ <%= error %> ⚠
+ <% } %> + +
+ + +
+ + <% if (todos.length === 0) { %> +
// NO TASKS IN MEMORY
> READY FOR INPUT_
+ <% } else { %> + + <% } %> + +
+ > TOTAL: <%= todos.length %> | DONE: <%= todos.filter(t => t.done).length %> | PENDING: <%= todos.filter(t => !t.done).length %> +
+ + +
+ +