feat: implement logging functionality and improve API endpoint handling in backend; refactor frontend to use dynamic API base
All checks were successful
Build and publish Docker image / build-and-push (release) Successful in 14s

This commit is contained in:
2025-11-12 00:55:44 +01:00
parent 04f0cc6dbf
commit f3d7408e14
5 changed files with 134 additions and 34 deletions

View File

@@ -7,6 +7,32 @@ const fs = require('fs');
const app = express();
// lightweight level logger
const LOG_LEVEL = process.env.LOG_LEVEL || 'info';
const LOG_ORDER = { error: 0, warn: 1, info: 2, debug: 3 };
function log(level, ...args) {
if (LOG_ORDER[level] <= LOG_ORDER[LOG_LEVEL]) {
const ts = new Date().toISOString();
const out = `[${ts}] [${level.toUpperCase()}]`;
if (level === 'error') console.error(out, ...args);
else if (level === 'warn') console.warn(out, ...args);
else if (level === 'debug' && console.debug) console.debug(out, ...args);
else console.log(out, ...args);
}
}
// optional request logger (only when debug)
if (LOG_LEVEL === 'debug') {
app.use((req, res, next) => {
const start = Date.now();
res.once('finish', () => {
const ms = Date.now() - start;
log('debug', `${req.method} ${req.originalUrl} ${res.statusCode} ${ms}ms`);
});
next();
});
}
// Use DATA_DIR env var if set, otherwise use local "data" folder
const DATA_DIR = process.env.DATA_DIR || path.join(__dirname, 'data');
@@ -15,7 +41,7 @@ const db = new sqlite3.Database(dbPath);
const upload = multer({ dest: path.join(DATA_DIR, 'uploads/') });
app.use(cors());
app.use(express.json());
app.use('/uploads', express.static(path.join(DATA_DIR, 'uploads')));
app.use('/uploads', express.static(path.join(DATA_DIR, 'uploads'), { maxAge: '1d' }));
// Create uploads directory if it doesn't exist
fs.mkdirSync(path.join(DATA_DIR, 'uploads'), { recursive: true });
@@ -40,6 +66,8 @@ app.get('/api/counters', (req, res) => {
id: Number(row.id),
value: Number(row.value)
}));
// log names at debug level (won't clutter info logs)
log('debug', `Fetched ${counters.length} counters:`, counters.map(c => c.name));
res.json(counters);
});
});
@@ -49,7 +77,11 @@ app.post('/api/counters', upload.single('image'), (req, res) => {
const { name = 'Counter', value = 0 } = req.body;
const image = req.file ? `/uploads/${req.file.filename}` : null;
db.run('INSERT INTO counters (name, value, image) VALUES (?, ?, ?)', [name, value, image], function (err) {
if (err) return res.status(500).json({ error: err.message });
if (err) {
log('error', 'Create counter failed:', err.message);
return res.status(500).json({ error: err.message });
}
log('info', `Created counter "${name}" (id: ${this.lastID})`);
res.json({ id: Number(this.lastID), name, value: Number(value), image });
});
});
@@ -60,6 +92,9 @@ app.put('/api/counters/:id', upload.single('image'), (req, res) => {
const id = req.params.id;
let image = null;
// Log incoming request at debug level only
log('debug', `PUT /api/counters/${id} body:`, req.body, 'file:', !!req.file);
// If a new image is uploaded, get its path
if (req.file) {
image = `/uploads/${req.file.filename}`;
@@ -83,17 +118,46 @@ app.put('/api/counters/:id', upload.single('image'), (req, res) => {
params.push(id);
const sql = `UPDATE counters SET ${fields.join(', ')} WHERE id = ?`;
// Log SQL and params at debug level
log('debug', `Updating counter ${id}`, sql, params);
db.run(sql, params, function (err) {
if (err) return res.status(500).json({ error: err.message });
res.json({ updated: this.changes });
if (err) {
log('error', `DB error updating counter ${id}:`, err.message);
return res.status(500).json({ error: err.message });
}
// Fetch the updated row to log the current name and value
db.get('SELECT name, value FROM counters WHERE id = ?', [id], (err2, row2) => {
if (err2) {
log('error', `Failed to fetch updated counter ${id}:`, err2.message);
} else if (row2) {
log('info', `Counter "${row2.name}" (id: ${id}) updated to value ${Number(row2.value)}`);
} else {
log('info', `Counter ${id} updated, changes: ${this.changes}`);
}
res.json({ updated: this.changes });
});
});
});
// Delete a counter
// Delete a counter (fetch name first so we can log it)
app.delete('/api/counters/:id', (req, res) => {
db.run('DELETE FROM counters WHERE id = ?', [req.params.id], function (err) {
if (err) return res.status(500).json({ error: err.message });
res.json({ deleted: this.changes });
const id = req.params.id;
db.get('SELECT name FROM counters WHERE id = ?', [id], (err, row) => {
if (err) {
log('error', `Lookup before delete failed for id ${id}:`, err.message);
return res.status(500).json({ error: err.message });
}
const name = row && row.name ? row.name : null;
db.run('DELETE FROM counters WHERE id = ?', [id], function (err) {
if (err) {
log('error', `Delete counter ${name} with ${id} failed:`, err.message);
return res.status(500).json({ error: err.message });
}
log('info', `Deleted counter${name ? ` "${name}"` : ''} (id: ${id})`);
res.json({ deleted: this.changes });
});
});
});
@@ -109,7 +173,7 @@ app.get('/*path', (req, res) => {
const PORT = 3000;
app.listen(PORT, () => {
console.log(`API server running on http://localhost:${PORT}`);
console.log(`API server listening on port: ${PORT}`);
});
process.on('SIGTERM', () => {