import express from 'express'; import { generate_id } from './lib/ids.mjs'; import { list_fields, get_field, set_field, delete_field, list_components, get_component, set_component, delete_component, list_inventory, get_inventory_entry, set_inventory_entry, delete_inventory_entry, } from './lib/storage.mjs'; const app = express(); app.use(express.json()); app.use(express.static(new URL('./public/', import.meta.url).pathname)); const PORT = process.env.PORT ?? 3020; function ok(res, data = {}) { res.json({ ok: true, ...data }); } function fail(res, msg, status = 400) { res.status(status).json({ ok: false, error: msg }); } // --------------------------------------------------------------------------- // Field definitions // --------------------------------------------------------------------------- app.get('/api/fields', (req, res) => { ok(res, { fields: list_fields() }); }); app.post('/api/fields', (req, res) => { const { name, unit = '', description = '' } = req.body; if (!name?.trim()) return fail(res, 'name is required'); const field = { id: generate_id(), name: name.trim(), unit: unit.trim(), description: description.trim(), created_at: Date.now(), }; set_field(field); ok(res, { field }); }); app.put('/api/fields/:id', (req, res) => { const existing = get_field(req.params.id); if (!existing) return fail(res, 'not found', 404); const { name, unit, description } = req.body; const updated = { ...existing }; if (name !== undefined) updated.name = name.trim(); if (unit !== undefined) updated.unit = unit.trim(); if (description !== undefined) updated.description = description.trim(); set_field(updated); ok(res, { field: updated }); }); app.delete('/api/fields/:id', (req, res) => { if (!delete_field(req.params.id)) return fail(res, 'not found', 404); ok(res); }); // --------------------------------------------------------------------------- // Components // --------------------------------------------------------------------------- app.get('/api/components', (req, res) => { ok(res, { components: list_components() }); }); app.post('/api/components', (req, res) => { const { name, description = '', fields = {} } = req.body; if (!name?.trim()) return fail(res, 'name is required'); const now = Date.now(); const component = { id: generate_id(), name: name.trim(), description: description.trim(), fields, created_at: now, updated_at: now, }; set_component(component); ok(res, { component }); }); app.get('/api/components/:id', (req, res) => { const component = get_component(req.params.id); if (!component) return fail(res, 'not found', 404); ok(res, { component }); }); app.put('/api/components/:id', (req, res) => { const existing = get_component(req.params.id); if (!existing) return fail(res, 'not found', 404); const { name, description, fields } = req.body; const updated = { ...existing, updated_at: Date.now() }; if (name !== undefined) updated.name = name.trim(); if (description !== undefined) updated.description = description.trim(); if (fields !== undefined) updated.fields = fields; set_component(updated); ok(res, { component: updated }); }); app.delete('/api/components/:id', (req, res) => { if (!delete_component(req.params.id)) return fail(res, 'not found', 404); ok(res); }); // --------------------------------------------------------------------------- // Inventory entries // --------------------------------------------------------------------------- app.get('/api/inventory', (req, res) => { ok(res, { entries: list_inventory() }); }); app.post('/api/inventory', (req, res) => { const { component_id, location_type, location_ref = '', quantity = '', notes = '' } = req.body; if (!component_id) return fail(res, 'component_id is required'); if (!location_type) return fail(res, 'location_type is required'); if (!get_component(component_id)) return fail(res, 'component not found', 404); const now = Date.now(); const entry = { id: generate_id(), component_id, location_type, location_ref: String(location_ref).trim(), quantity: String(quantity).trim(), notes: String(notes).trim(), created_at: now, updated_at: now, }; set_inventory_entry(entry); ok(res, { entry }); }); app.put('/api/inventory/:id', (req, res) => { const existing = get_inventory_entry(req.params.id); if (!existing) return fail(res, 'not found', 404); const { location_type, location_ref, quantity, notes } = req.body; const updated = { ...existing, updated_at: Date.now() }; if (location_type !== undefined) updated.location_type = location_type; if (location_ref !== undefined) updated.location_ref = String(location_ref).trim(); if (quantity !== undefined) updated.quantity = String(quantity).trim(); if (notes !== undefined) updated.notes = String(notes).trim(); set_inventory_entry(updated); ok(res, { entry: updated }); }); app.delete('/api/inventory/:id', (req, res) => { if (!delete_inventory_entry(req.params.id)) return fail(res, 'not found', 404); ok(res); }); app.listen(PORT, () => { console.log(`Electronics Inventory running on http://localhost:${PORT}`); });