commit 3191680f41156288ad10ce95ac80001681f55d99 Author: ezkeel-admin Date: Thu Jun 11 18:49:05 2026 +0000 Initial template diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d11463c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM node:20-alpine + +WORKDIR /app + +COPY package.json package-lock.json* ./ +RUN npm install + +COPY server.js ./ +COPY public/ ./public/ + +EXPOSE 3000 + +CMD ["npm", "start"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..c56474f --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# todo-list + +Todo list app with Express backend, Postgres database, and single-page HTML frontend \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..d14fcfb --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "todo-list", + "version": "1.0.0", + "main": "server.js", + "scripts": { + "start": "node server.js" + }, + "dependencies": { + "express": "^4.18.2", + "pg": "^8.11.3", + "dotenv": "^16.3.1" + } +} diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..3582d1c --- /dev/null +++ b/public/index.html @@ -0,0 +1,216 @@ + + + + + + Todo List + + + +
+

✅ Todo List

+
+ + +
+ + +
+ + + + diff --git a/server.js b/server.js new file mode 100644 index 0000000..ed94a00 --- /dev/null +++ b/server.js @@ -0,0 +1,89 @@ +const express = require('express'); +const { Pool } = require('pg'); +const path = require('path'); + +const app = express(); +app.use(express.json()); +app.use(express.static(path.join(__dirname, 'public'))); + +const pool = new Pool({ + connectionString: process.env.DATABASE_URL, +}); + +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 NOT NULL DEFAULT false, + created_at TIMESTAMP NOT NULL DEFAULT NOW() + ) + `); + console.log('Database table ready'); + } finally { + client.release(); + } +} + +// GET /api/todos +app.get('/api/todos', async (req, res) => { + try { + const { rows } = await pool.query('SELECT * FROM todos ORDER BY created_at DESC'); + res.json(rows); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Failed to fetch todos' }); + } +}); + +// POST /api/todos +app.post('/api/todos', async (req, res) => { + const { text } = req.body; + if (!text || typeof text !== 'string' || !text.trim()) { + return res.status(400).json({ error: '"text" is required' }); + } + try { + const { rows } = await pool.query( + 'INSERT INTO todos (text) VALUES ($1) RETURNING *', + [text.trim()] + ); + res.status(201).json(rows[0]); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Failed to create todo' }); + } +}); + +// PUT /api/todos/:id/toggle +app.put('/api/todos/:id/toggle', async (req, res) => { + try { + const { rows } = await pool.query( + 'UPDATE todos SET done = NOT done WHERE id = $1 RETURNING *', + [req.params.id] + ); + if (rows.length === 0) return res.status(404).json({ error: 'Todo not found' }); + res.json(rows[0]); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Failed to toggle todo' }); + } +}); + +// DELETE /api/todos/:id +app.delete('/api/todos/:id', async (req, res) => { + try { + const { rowCount } = await pool.query('DELETE FROM todos WHERE id = $1', [req.params.id]); + if (rowCount === 0) return res.status(404).json({ error: 'Todo not found' }); + res.json({ message: 'Todo deleted' }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Failed to delete todo' }); + } +}); + +initDB().then(() => { + const PORT = process.env.PORT || 3000; + app.listen(PORT, () => console.log(`Server running on port ${PORT}`)); +});