Updated server.mjs with 224 additions
--- a/server.mjs
+++ b/server.mjs
@@ -1,0 +1,224 @@
+import express from 'express';
+import session from 'express-session';
+import SQLiteStore from 'connect-sqlite3';
+import bcrypt from 'bcryptjs';
+import Database from 'better-sqlite3';
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+const app = express();
+const PORT = process.env.PORT || 3000;
+const SQLiteStoreSession = SQLiteStore(session);
+
+// Database setup
+const db = new Database('database.db');
+db.pragma('journal_mode = WAL');
+
+db.exec(`
+  CREATE TABLE IF NOT EXISTS users (
+    id INTEGER PRIMARY KEY AUTOINCREMENT,
+    username TEXT UNIQUE NOT NULL,
+    password TEXT NOT NULL
+  );
+  CREATE TABLE IF NOT EXISTS tasks (
+    id INTEGER PRIMARY KEY AUTOINCREMENT,
+    userId INTEGER NOT NULL,
+    title TEXT NOT NULL,
+    completed BOOLEAN DEFAULT 0,
+    FOREIGN KEY (userId) REFERENCES users(id)
+  );
+`);
+
+// Middleware
+app.use(express.json());
+app.use(express.static(path.join(__dirname, 'public')));
+app.use(session({
+  store: new SQLiteStoreSession({ db: 'sessions.db', dir: './' }),
+  secret: 'supersecretkey',
+  resave: false,
+  saveUninitialized: false,
+  cookie: { maxAge: 7 * 24 * 60 * 60 * 1000 } // 1 week
+}));
+
+// Auth middleware
+const isAuthenticated = (req, res, next) => {
+  if (req.session.userId) {
+    next();
+  } else {
+    res.status(401).json({ message: 'Unauthorized' });
+  }
+};
+
+// Utility for input validation
+const validateInput = (data, fields) => {
+  for (const field of fields) {
+    if (!data[field] || typeof data[field] !== 'string' || data[field].trim() === '') {
+      return `Missing or invalid field: ${field}`;
+    }
+  }
+  return null;
+};
+
+// Routes
+
+// Health check endpoint
+app.get('/health', (req, res) => {
+  res.status(200).json({ status: 'ok' });
+});
+
+// Register
+app.post('/register', async (req, res) => {
+  const { username, password } = req.body;
+  const validationError = validateInput(req.body, ['username', 'password']);
+  if (validationError) {
+    return res.status(400).json({ message: validationError });
+  }
+
+  try {
+    const hashedPassword = await bcrypt.hash(password, 10);
+    const stmt = db.prepare('INSERT INTO users (username, password) VALUES (?, ?)');
+    stmt.run(username, hashedPassword);
+    res.status(201).json({ message: 'User registered successfully' });
+  } catch (error) {
+    if (error.code === 'SQLITE_CONSTRAINT_UNIQUE') {
+      return res.status(409).json({ message: 'Username already exists' });
+    }
+    console.error('Registration error:', error);
+    res.status(500).json({ message: 'Internal server error' });
+  }
+});
+
+// Login
+app.post('/login', async (req, res) => {
+  const { username, password } = req.body;
+  const validationError = validateInput(req.body, ['username', 'password']);
+  if (validationError) {
+    return res.status(400).json({ message: validationError });
+  }
+
+  try {
+    const stmt = db.prepare('SELECT id, password FROM users WHERE username = ?');
+    const user = stmt.get(username);
+
+    if (user && await bcrypt.compare(password, user.password)) {
+      req.session.userId = user.id;
+      res.status(200).json({ message: 'Logged in successfully' });
+    } else {
+      res.status(401).json({ message: 'Invalid credentials' });
+    }
+  } catch (error) {
+    console.error('Login error:', error);
+    res.status(500).json({ message: 'Internal server error' });
+  }
+});
+
+// Logout
+app.post('/logout', isAuthenticated, (req, res) => {
+  req.session.destroy(err => {
+    if (err) {
+      return res.status(500).json({ message: 'Could not log out' });
+    }
+    res.clearCookie('connect.sid');
+    res.status(200).json({ message: 'Logged out successfully' });
+  });
+});
+
+// Get user session status
+app.get('/session', (req, res) => {
+  if (req.session.userId) {
+    res.status(200).json({ authenticated: true, userId: req.session.userId });
+  } else {
+    res.status(200).json({ authenticated: false });
+  }
+});
+
+// Task CRUD
+
+// Create task
+app.post('/tasks', isAuthenticated, (req, res) => {
+  const { title } = req.body;
+  const validationError = validateInput(req.body, ['title']);
+  if (validationError) {
+    return res.status(400).json({ message: validationError });
+  }
+
+  try {
+    const stmt = db.prepare('INSERT INTO tasks (userId, title) VALUES (?, ?)');
+    const info = stmt.run(req.session.userId, title);
+    res.status(201).json({ id: info.lastInsertRowid, title, completed: 0 });
+  } catch (error) {
+    console.error('Create task error:', error);
+    res.status(500).json({ message: 'Internal server error' });
+  }
+});
+
+// Get tasks
+app.get('/tasks', isAuthenticated, (req, res) => {
+  try {
+    const stmt = db.prepare('SELECT id, title, completed FROM tasks WHERE userId = ?');
+    const tasks = stmt.all(req.session.userId);
+    res.status(200).json(tasks);
+  } catch (error) {
+    console.error('Get tasks error:', error);
+    res.status(500).json({ message: 'Internal server error' });
+  }
+});
+
+// Update task
+app.put('/tasks/:id', isAuthenticated, (req, res) => {
+  const { id } = req.params;
+  const { title, completed } = req.body;
+
+  if (typeof title !== 'string' || title.trim() === '') {
+    return res.status(400).json({ message: 'Invalid title' });
+  }
+  if (typeof completed !== 'boolean') {
+    return res.status(400).json({ message: 'Invalid completed status' });
+  }
+
+  try {
+    const stmt = db.prepare('UPDATE tasks SET title = ?, completed = ? WHERE id = ? AND userId = ?');
+    const info = stmt.run(title, completed ? 1 : 0, id, req.session.userId);
+
+    if (info.changes === 0) {
+      return res.status(404).json({ message: 'Task not found or not authorized' });
+    }
+    res.status(200).json({ message: 'Task updated successfully' });
+  } catch (error) {
+    console.error('Update task error:', error);
+    res.status(500).json({ message: 'Internal server error' });
+  }
+});
+
+// Delete task
+app.delete('/tasks/:id', isAuthenticated, (req, res) => {
+  const { id } = req.params;
+
+  try {
+    const stmt = db.prepare('DELETE FROM tasks WHERE id = ? AND userId = ?');
+    const info = stmt.run(id, req.session.userId);
+
+    if (info.changes === 0) {
+      return res.status(404).json({ message: 'Task not found or not authorized' });
+    }
+    res.status(200).json({ message: 'Task deleted successfully' });
+  } catch (error) {
+    console.error('Delete task error:', error);
+    res.status(500).json({ message: 'Internal server error' });
+  }
+});
+
+// Error handling middleware
+app.use((err, req, res, next) => {
+  console.error(err.stack);
+  res.status(500).send('Something broke!');
+});
+
+// Start server
+app.listen(PORT, () => {
+  console.log(`Server running on http://localhost:${PORT}`);
+});
+