142 lines
4.7 KiB
JavaScript
142 lines
4.7 KiB
JavaScript
// server.js
|
||
const express = require('express');
|
||
const bodyParser = require('body-parser');
|
||
const sqlite3 = require('sqlite3').verbose();
|
||
const path = require('path');
|
||
const http = require('http');
|
||
const { Server } = require('socket.io');
|
||
const fs = require('fs');
|
||
|
||
const app = express();
|
||
const server = http.createServer(app);
|
||
const io = new Server(server);
|
||
|
||
app.use(bodyParser.json());
|
||
app.use(express.static(path.join(__dirname, 'public')));
|
||
|
||
// open (or create) the SQLite database file
|
||
const db = new sqlite3.Database('positions.db', err => {
|
||
if (err) {
|
||
console.error('Could not open DB', err);
|
||
process.exit(1);
|
||
}
|
||
});
|
||
|
||
// create table if not exists
|
||
db.run(
|
||
`CREATE TABLE IF NOT EXISTS positions (
|
||
square_id TEXT PRIMARY KEY,
|
||
fruit TEXT
|
||
)`,
|
||
(err) => {
|
||
if (err) console.error('Could not ensure table', err);
|
||
}
|
||
);
|
||
|
||
// get all saved positions
|
||
app.get('/api/positions', (req, res) => {
|
||
db.all('SELECT square_id, fruit FROM positions', (err, rows) => {
|
||
if (err) return res.status(500).json({ error: err.message });
|
||
// convert to an object. should look like this: { "1": "Apple", "2": "Banana", … }
|
||
const mapping = {};
|
||
rows.forEach(r => (mapping[r.square_id] = r.fruit));
|
||
res.json(mapping);
|
||
});
|
||
});
|
||
|
||
// save (or update) a single square’s item
|
||
app.post('/api/positions', (req, res) => {
|
||
const { squareId, fruit } = req.body;
|
||
if (!squareId || typeof fruit !== 'string') {
|
||
return res.status(400).json({ error: 'squareId and fruit required' });
|
||
}
|
||
db.run(
|
||
`INSERT INTO positions (square_id, fruit)
|
||
VALUES (?, ?)
|
||
ON CONFLICT(square_id) DO UPDATE SET fruit=excluded.fruit`,
|
||
[squareId, fruit],
|
||
function (err) {
|
||
if (err) return res.status(500).json({ error: err.message });
|
||
// broadcast update via Socket.io
|
||
io.emit('update', { squareId, fruit });
|
||
res.json({ success: true });
|
||
}
|
||
);
|
||
});
|
||
|
||
// Serve categories and fruits from a JSON file
|
||
app.get('/api/categories', (req, res) => {
|
||
const categoriesPath = path.join(__dirname, 'categories.json');
|
||
fs.readFile(categoriesPath, 'utf8', (err, data) => {
|
||
if (err) return res.status(500).json({ error: 'Could not load categories' });
|
||
try {
|
||
const categories = JSON.parse(data);
|
||
res.json(categories);
|
||
} catch (e) {
|
||
res.status(500).json({ error: 'Invalid categories file' });
|
||
}
|
||
});
|
||
});
|
||
|
||
// Add a fruit to a category in categories.json
|
||
app.post('/api/add-fruit', (req, res) => {
|
||
const { category, fruit } = req.body;
|
||
if (!category || !fruit) {
|
||
return res.status(400).json({ error: 'category and fruit required' });
|
||
}
|
||
const categoriesPath = path.join(__dirname, 'categories.json');
|
||
fs.readFile(categoriesPath, 'utf8', (err, data) => {
|
||
if (err) return res.status(500).json({ error: 'Could not load categories' });
|
||
let categories;
|
||
try {
|
||
categories = JSON.parse(data);
|
||
} catch (e) {
|
||
return res.status(500).json({ error: 'Invalid categories file' });
|
||
}
|
||
const cat = categories.find(c => c.name === category);
|
||
if (!cat) return res.status(404).json({ error: 'Category not found' });
|
||
// Prevent duplicates
|
||
if (cat.fruits.includes(fruit)) {
|
||
return res.status(400).json({ error: 'Fruit already exists in category' });
|
||
}
|
||
cat.fruits.push(fruit);
|
||
fs.writeFile(categoriesPath, JSON.stringify(categories, null, 2), err2 => {
|
||
if (err2) return res.status(500).json({ error: 'Could not save categories' });
|
||
res.json({ success: true });
|
||
});
|
||
});
|
||
});
|
||
|
||
// Delete a fruit from a category in categories.json
|
||
app.post('/api/delete-fruit', (req, res) => {
|
||
const { category, fruit } = req.body;
|
||
if (!category || !fruit) {
|
||
return res.status(400).json({ error: 'category and fruit required' });
|
||
}
|
||
const categoriesPath = path.join(__dirname, 'categories.json');
|
||
fs.readFile(categoriesPath, 'utf8', (err, data) => {
|
||
if (err) return res.status(500).json({ error: 'Could not load categories' });
|
||
let categories;
|
||
try {
|
||
categories = JSON.parse(data);
|
||
} catch (e) {
|
||
return res.status(500).json({ error: 'Invalid categories file' });
|
||
}
|
||
const cat = categories.find(c => c.name === category);
|
||
if (!cat) return res.status(404).json({ error: 'Category not found' });
|
||
const idx = cat.fruits.indexOf(fruit);
|
||
if (idx === -1) return res.status(404).json({ error: 'Fruit not found in category' });
|
||
cat.fruits.splice(idx, 1);
|
||
fs.writeFile(categoriesPath, JSON.stringify(categories, null, 2), err2 => {
|
||
if (err2) return res.status(500).json({ error: 'Could not save categories' });
|
||
res.json({ success: true });
|
||
});
|
||
});
|
||
});
|
||
|
||
// start server
|
||
const PORT = process.env.PORT || 3085;
|
||
server.listen(PORT, () => {
|
||
console.log(`Listening on http://localhost:${PORT}`);
|
||
});
|