import express from "express"; import dotenv from "dotenv" import { randomUUID } from "crypto"; import DBAdapter, { getCurrentDate, setCurrentDate } from "./db/database.js"; import { DB_INTERNAL_ERROR, DB_USER_ERROR } from "./db/database.js"; // Switch to .env for test in local machine dotenv.config({ path: './server/docker.env' }); const { HOST, PORT, DATABASE, USER, PASSWORD } = process.env; const appPort = 3000; const app = express(); app.use(express.json()); app.use((req, res, next) => { res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE'); if (req.method === 'OPTIONS') { res.sendStatus(204); return; } next(); }); const adapter = new DBAdapter({ dbHost: HOST, dbPort: PORT, dbName: DATABASE, dbUserLogin: USER, dbUserPassword: PASSWORD }); function AddDays(dateString, days) { let date = new Date(dateString); date.setDate(date.getDate() + days); return date.toISOString().slice(0, 10); } app.get('/api/current-date', async (req, res) => { let currentDate = getCurrentDate(); res.json({ currentDate }); }); app.post('/api/current-date/advance', async (req, res) => { let currentDate = getCurrentDate(); try { const raw_ids = await adapter.getOrdersByDate(currentDate); if (raw_ids && raw_ids.length > 0) { const ids = raw_ids.map(item => item.id); await adapter.clearOrders(ids); } let nextDate = AddDays(currentDate, 1); await adapter.stock(); await setCurrentDate(nextDate); res.json({ currentDate: nextDate }); } catch (err){ res.statusCode = 500; res.message = "WHOOPS"; res.json({ timeStamp: new Date().toISOString(), statusCode: res.statusCode, error : `${err}` }) } }); app.get('/api/products', async (req, res) => { try { const db_products = await adapter.getProducts(); const products = db_products.map( ( {id, name, quantity} ) => ({ ID: id, pr_name: name, count: quantity }) ); res.statusCode = 200; res.message = "OK"; res.json(products); } catch (err){ res.statusCode = 500; res.message = "WHOOPS"; res.json({ timeStamp: new Date().toISOString(), statusCode: 500, error : `${err}` }) } }); app.get('/api/orders', async (req, res) => { try { const db_orders = await adapter.getOrders(); const orders = db_orders.map( ( {id, customer_name, order_date, status, total_amount, items} ) => ({ ID: id, customer: customer_name, date: order_date, status: status, total: total_amount, items: items ? items.map(item => ({ id: item.id, product_id: item.product_id, product_name: item.product_name || item.name, quantity: item.quantity, price: item.price })) : [] }) ); res.statusCode = 200; res.message = "OK"; res.json(orders); } catch (err) { res.statusCode = 500; res.message = "WHOOPS"; res.json({ timeStamp: new Date().toISOString(), statusCode: 500, error: `${err}` }); } }); app.post('/api/orders', async (req, res) => { let { customerName, orderDate } = req.body; let id = randomUUID(); try { await adapter.addOrder({ id, customer_name: customerName, orderDate }); res.status(201).json({ id, customerName, orderDate }); } catch (err) { console.log('Error in POST /api/orders:', err); switch(err.type) { case DB_INTERNAL_ERROR: res.status(500).json({ timeStamp: new Date().toISOString(), statusCode: 500, error: err.error?.message || err.message }); break; case DB_USER_ERROR: res.status(400).json({ timeStamp: new Date().toISOString(), statusCode: 400, error: err.error?.message || err.message }); break; default: res.status(500).json({ timeStamp: new Date().toISOString(), statusCode: 500, message: "Unknown error", error: err.message }); } } }); app.put('/api/orders/:id', async (req, res) => { const { customerName, orderDate } = req.body; const orderId = req.params.id; if (!customerName || !orderDate) { return res.status(400).json({ timeStamp: new Date().toISOString(), statusCode: 400, error: "Customer name and order date are required" }); } try { await adapter.changeOrderInfo({ id: orderId, customer_name: customerName, orderDate }); return res.json({ id: orderId, customer_name: customerName, order_date: orderDate }); } catch(err) { console.error('Error updating order:', err); const errorMessage = err.message || err.error?.message || "Unknown error occurred"; switch(err.type) { case DB_INTERNAL_ERROR: return res.status(500).json({ timeStamp: new Date().toISOString(), statusCode: 500, error: errorMessage, details: err.details || "Internal server error" }); case DB_USER_ERROR: return res.status(400).json({ timeStamp: new Date().toISOString(), statusCode: 400, error: errorMessage }); default: return res.status(500).json({ timeStamp: new Date().toISOString(), statusCode: 500, error: errorMessage }); } } }); app.delete('/api/orders/:id', async (req, res) => { try { await adapter.deleteOrderById(req.params.id); res.statusCode = 200; res.message = "OK"; res.json({message: "success"}); } catch (err){ res.statusCode = 500; res.message = "WHOOPS"; res.json({ timeStamp: new Date().toISOString(), statusCode: 500, error: `${err}` }); } }) app.post('/api/orders/:orderId/items', async (req, res) => { let orderId = req.params.orderId; let { productId, quantity } = req.body; if (!productId || !quantity || quantity <= 0) { return res.status(400).json({ timeStamp: new Date().toISOString(), statusCode: 400, error: "Invalid productId or quantity" }); } try { const itemId = await adapter.addOrderItem({ orderId, productId, quantity }); return res.status(201).json({ itemId: itemId.toString(), orderId, productId, quantity }); } catch (err) { console.error('Error adding order item:', err); if (err.type === DB_USER_ERROR) { return res.status(400).json({ timeStamp: new Date().toISOString(), statusCode: 400, error: err.error?.message || "Invalid request", details: err.details }); } else if (err.type === DB_INTERNAL_ERROR) { return res.status(500).json({ timeStamp: new Date().toISOString(), statusCode: 500, error: "Internal server error", details: err.error?.message }); } else { return res.status(500).json({ timeStamp: new Date().toISOString(), statusCode: 500, error: "Internal server error" }); } } }); app.put('/api/orders/:orderId/items/:itemId', async (req, res) => { let itemId = req.params.itemId; let quantity = req.body.quantity; try { await adapter.updateOrderItem({ itemId, quantity }); res.status(201).json({ itemId, quantity }); } catch (err) { res.json({ timeStamp: new Date().toISOString(), statusCode: 500, error: err.message }); } }); app.delete('/api/orders/:orderId/items/:itemId', async (req, res) => { try { await adapter.deleteOrderItem(req.params.itemId); return res.status(204).end(); } catch (err) { if (err.type === DB_INTERNAL_ERROR) { return res.status(500).json({ timeStamp: new Date().toISOString(), statusCode: 500, error: "Server error", details: err.error.message }); } else { return res.status(500).json({ timeStamp: new Date().toISOString(), statusCode: 500, error: "Internal server error", details: err.message }); } } }); app.post('/api/order-items/:itemId/move', async (req, res) => { let itemId = req.params.itemId; let targetOrderId = req.body.targetOrderId; try { await adapter.moveOrderItem({ itemId, targetOrderId, }); res.status(201).json({ itemId, targetOrderId, }); } catch (err) { // TODO res.status(500).json({ timeStamp: new Date().toISOString(), statusCode: 500, }); } }); app.use((req, res) => { res.status(404).json({ error: 'Invalid route' }); }); const server = app.listen(appPort, async() => { try { await adapter.connect(); console.log(`✅ Server running on port ${appPort}`); console.log(`📡 Local: http://localhost:${appPort}`); } catch(err){ console.log("Shutting down application..."); process.exit(100); } }); process.on('SIGTERM', () => { console.log("CLOSE APP"); server.close(async () => { await adapter.disconnect(); console.log("DB DISCONNECTED"); }); });