Character manager: asset uploads, per-ghost model/colour/opacity overrides, face+torso texture decals
This commit is contained in:
+28
-2
@@ -11,18 +11,40 @@ const { WebSocketServer } = require('ws');
|
||||
const state = require('./state');
|
||||
const auth = require('./auth');
|
||||
const Playlist = require('./playlist');
|
||||
const { Assets } = require('./uploads');
|
||||
|
||||
const PORT = process.env.PORT || 33044;
|
||||
const MODE = process.env.EXHIBIT_MODE || 'dev';
|
||||
|
||||
const app = express();
|
||||
app.use(express.json({ limit: '2mb' }));
|
||||
app.use(express.json({ limit: '20mb' })); // base64 asset uploads
|
||||
|
||||
// ---------- public static ----------
|
||||
app.use(express.static(path.join(__dirname, '..', 'public')));
|
||||
app.use('/data/ghosts.json', express.static(path.join(__dirname, '..', 'data', 'ghosts.json')));
|
||||
app.use('/data/gradients.json', express.static(path.join(__dirname, '..', 'data', 'gradients.json')));
|
||||
|
||||
// ---------- uploaded assets (models + textures) ----------
|
||||
const assets = new Assets(state.liveDir);
|
||||
app.use('/assets', express.static(path.join(state.liveDir, 'assets'), { maxAge: '1h' }));
|
||||
|
||||
app.get('/api/assets', (req, res) => res.json(assets.list()));
|
||||
app.post('/api/assets', guard, (req, res) => {
|
||||
try { res.json(assets.save(req.body)); }
|
||||
catch (e) { res.status(400).json({ error: String(e.message || e) }); }
|
||||
});
|
||||
app.delete('/api/assets/:id', guard, (req, res) => {
|
||||
try { res.json(assets.remove(req.params.id)); }
|
||||
catch (e) { res.status(400).json({ error: String(e.message || e) }); }
|
||||
});
|
||||
|
||||
// ---------- character overrides ----------
|
||||
app.get('/api/characters', (req, res) => res.json(state.getCharacters()));
|
||||
app.put('/api/characters', guard, (req, res) => {
|
||||
try { res.json(state.putCharacters(req.body)); }
|
||||
catch (e) { res.status(400).json({ error: String(e.message || e) }); }
|
||||
});
|
||||
|
||||
// ---------- runtime info ----------
|
||||
app.get('/api/info', (req, res) => {
|
||||
res.json({ mode: MODE, authRequired: MODE === 'final', version: 2 });
|
||||
@@ -91,6 +113,7 @@ playlist.start();
|
||||
|
||||
wss.on('connection', (ws) => {
|
||||
ws.send(JSON.stringify({ type: 'scene', scene: state.getScene() }));
|
||||
ws.send(JSON.stringify({ type: 'characters', characters: state.getCharacters() }));
|
||||
ws.send(JSON.stringify({ type: 'active', ghosts: playlist.activeSnapshot() }));
|
||||
ws.on('message', (raw) => {
|
||||
let m; try { m = JSON.parse(raw); } catch { return; }
|
||||
@@ -99,7 +122,10 @@ wss.on('connection', (ws) => {
|
||||
});
|
||||
|
||||
// push scene updates to viewers when admin saves
|
||||
state.onChange(() => broadcast({ type: 'scene', scene: state.getScene() }));
|
||||
state.onChange(() => {
|
||||
broadcast({ type: 'scene', scene: state.getScene() });
|
||||
broadcast({ type: 'characters', characters: state.getCharacters() });
|
||||
});
|
||||
|
||||
server.listen(PORT, () => {
|
||||
console.log(`Newbury Exhibit v2 [${MODE}] on :${PORT}`);
|
||||
|
||||
Reference in New Issue
Block a user