Adding the lab project
This commit is contained in:
Vendored
+235
@@ -0,0 +1,235 @@
|
||||
import express from 'express';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { createCaEngine, createDeck, createInitialConditionGenerator, createPresetNode, createPresetTree, createScene, deleteDeck, deletePresetNode, deletePresetTree, deleteScene, getCaEngine, getDeck, getInitialConditionGenerator, getPresetNode, getScene, listCaEngines, listCaEnginePresetUsage, listDecks, listInitialConditionGenerators, listPresetNodeUsage, listPresetNodes, listPresetTrees, listScenes, resolveDeck, resolvePresetNode, resolveScene, updateCaEngine, updateDeck, updateInitialConditionGenerator, updatePresetNode, updateScene } from './caStudioRepository.js';
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const adminPublicPath = resolve(__dirname, '../public/admin');
|
||||
function asyncRoute(handler) {
|
||||
return (request, response, next) => {
|
||||
handler(request, response).catch(next);
|
||||
};
|
||||
}
|
||||
function routeParam(request, key) {
|
||||
const value = request.params[key];
|
||||
if (typeof value !== 'string') {
|
||||
throw new Error(`Missing route parameter: ${key}`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function queryString(value) {
|
||||
return typeof value === 'string' ? value : undefined;
|
||||
}
|
||||
function parseStates(value) {
|
||||
const trimmed = value.trim();
|
||||
if (/^\d+$/.test(trimmed))
|
||||
return Number(trimmed);
|
||||
if (trimmed.startsWith('['))
|
||||
return JSON.parse(trimmed);
|
||||
return trimmed.split(',').map((state) => state.trim()).filter(Boolean);
|
||||
}
|
||||
function caClassFromQuery(query) {
|
||||
const dimensions = queryString(query.dimensions);
|
||||
const states = queryString(query.states);
|
||||
if (!dimensions && !states)
|
||||
return undefined;
|
||||
if (!dimensions || !states) {
|
||||
throw new Error('Filtering initial condition generators requires dimensions and states');
|
||||
}
|
||||
return {
|
||||
dimensions: Number(dimensions),
|
||||
states: parseStates(states)
|
||||
};
|
||||
}
|
||||
export function createCaStudioApi(db) {
|
||||
const app = express();
|
||||
app.use(express.json({ limit: '1mb' }));
|
||||
app.use('/admin', express.static(adminPublicPath));
|
||||
app.get('/', (_request, response) => {
|
||||
response.redirect('/admin');
|
||||
});
|
||||
app.get(/^\/(?:admin|view)(?:\/.*)?$/, (_request, response) => {
|
||||
response.sendFile(resolve(adminPublicPath, 'index.html'));
|
||||
});
|
||||
app.post('/api/ca/preset-trees', asyncRoute(async (request, response) => {
|
||||
const tree = await createPresetTree(db, request.body);
|
||||
response.status(201).json({ ...tree, node_count: 0 });
|
||||
}));
|
||||
app.get('/api/ca/preset-trees', asyncRoute(async (_request, response) => {
|
||||
response.json(await listPresetTrees(db));
|
||||
}));
|
||||
app.delete('/api/ca/preset-trees/:treeId', asyncRoute(async (request, response) => {
|
||||
const deleted = await deletePresetTree(db, routeParam(request, 'treeId'));
|
||||
response.sendStatus(deleted ? 204 : 404);
|
||||
}));
|
||||
app.get('/api/ca/preset-trees/:treeId/nodes', asyncRoute(async (request, response) => {
|
||||
response.json(await listPresetNodes(db, routeParam(request, 'treeId')));
|
||||
}));
|
||||
app.post('/api/ca/preset-trees/:treeId/nodes', asyncRoute(async (request, response) => {
|
||||
const node = await createPresetNode(db, {
|
||||
...request.body,
|
||||
treeId: routeParam(request, 'treeId')
|
||||
});
|
||||
response.status(201).json(node);
|
||||
}));
|
||||
app.get('/api/ca/preset-trees/:treeId/nodes/:nodeId/resolved', asyncRoute(async (request, response) => {
|
||||
const resolved = await resolvePresetNode(db, routeParam(request, 'treeId'), routeParam(request, 'nodeId'));
|
||||
if (!resolved) {
|
||||
response.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
response.json(resolved);
|
||||
}));
|
||||
app.get('/api/ca/preset-trees/:treeId/nodes/:nodeId', asyncRoute(async (request, response) => {
|
||||
const node = await getPresetNode(db, routeParam(request, 'treeId'), routeParam(request, 'nodeId'));
|
||||
if (!node) {
|
||||
response.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
response.json(node);
|
||||
}));
|
||||
app.get('/api/ca/preset-trees/:treeId/node-usage', asyncRoute(async (request, response) => {
|
||||
response.json(await listPresetNodeUsage(db, routeParam(request, 'treeId')));
|
||||
}));
|
||||
app.patch('/api/ca/preset-trees/:treeId/nodes/:nodeId', asyncRoute(async (request, response) => {
|
||||
const node = await updatePresetNode(db, routeParam(request, 'treeId'), routeParam(request, 'nodeId'), request.body);
|
||||
if (!node) {
|
||||
response.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
response.json(node);
|
||||
}));
|
||||
app.delete('/api/ca/preset-trees/:treeId/nodes/:nodeId', asyncRoute(async (request, response) => {
|
||||
const deleted = await deletePresetNode(db, routeParam(request, 'treeId'), routeParam(request, 'nodeId'));
|
||||
response.sendStatus(deleted ? 204 : 404);
|
||||
}));
|
||||
app.post('/api/ca/initial-condition-generators', asyncRoute(async (request, response) => {
|
||||
response.status(201).json(await createInitialConditionGenerator(db, request.body));
|
||||
}));
|
||||
app.get('/api/ca/initial-condition-generators', asyncRoute(async (request, response) => {
|
||||
response.json(await listInitialConditionGenerators(db, { caClass: caClassFromQuery(request.query) }));
|
||||
}));
|
||||
app.get('/api/ca/initial-condition-generators/:generatorId', asyncRoute(async (request, response) => {
|
||||
const generator = await getInitialConditionGenerator(db, routeParam(request, 'generatorId'));
|
||||
if (!generator) {
|
||||
response.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
response.json(generator);
|
||||
}));
|
||||
app.patch('/api/ca/initial-condition-generators/:generatorId', asyncRoute(async (request, response) => {
|
||||
const generator = await updateInitialConditionGenerator(db, routeParam(request, 'generatorId'), request.body);
|
||||
if (!generator) {
|
||||
response.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
response.json(generator);
|
||||
}));
|
||||
app.post('/api/ca/engines', asyncRoute(async (request, response) => {
|
||||
response.status(201).json(await createCaEngine(db, request.body));
|
||||
}));
|
||||
app.get('/api/ca/engines', asyncRoute(async (request, response) => {
|
||||
response.json(await listCaEngines(db, { caClass: caClassFromQuery(request.query) }));
|
||||
}));
|
||||
app.get('/api/ca/engines/:engineId/usage', asyncRoute(async (request, response) => {
|
||||
const usage = await listCaEnginePresetUsage(db, routeParam(request, 'engineId'));
|
||||
if (!usage) {
|
||||
response.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
response.json(usage);
|
||||
}));
|
||||
app.get('/api/ca/engines/:engineId', asyncRoute(async (request, response) => {
|
||||
const engine = await getCaEngine(db, routeParam(request, 'engineId'));
|
||||
if (!engine) {
|
||||
response.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
response.json(engine);
|
||||
}));
|
||||
app.patch('/api/ca/engines/:engineId', asyncRoute(async (request, response) => {
|
||||
const engine = await updateCaEngine(db, routeParam(request, 'engineId'), request.body);
|
||||
if (!engine) {
|
||||
response.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
response.json(engine);
|
||||
}));
|
||||
app.post('/api/ca/decks', asyncRoute(async (request, response) => {
|
||||
response.status(201).json(await createDeck(db, request.body));
|
||||
}));
|
||||
app.get('/api/ca/decks', asyncRoute(async (_request, response) => {
|
||||
response.json(await listDecks(db));
|
||||
}));
|
||||
app.get('/api/ca/decks/:deckId', asyncRoute(async (request, response) => {
|
||||
const deck = await getDeck(db, routeParam(request, 'deckId'));
|
||||
if (!deck) {
|
||||
response.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
response.json(deck);
|
||||
}));
|
||||
app.patch('/api/ca/decks/:deckId', asyncRoute(async (request, response) => {
|
||||
const deck = await updateDeck(db, routeParam(request, 'deckId'), request.body);
|
||||
if (!deck) {
|
||||
response.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
response.json(deck);
|
||||
}));
|
||||
app.delete('/api/ca/decks/:deckId', asyncRoute(async (request, response) => {
|
||||
const deleted = await deleteDeck(db, routeParam(request, 'deckId'));
|
||||
response.sendStatus(deleted ? 204 : 404);
|
||||
}));
|
||||
app.get('/api/ca/decks/:deckId/resolved', asyncRoute(async (request, response) => {
|
||||
const resolved = await resolveDeck(db, routeParam(request, 'deckId'));
|
||||
if (!resolved) {
|
||||
response.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
response.json(resolved);
|
||||
}));
|
||||
app.get('/api/ca/decks/:deckId/scenes', asyncRoute(async (request, response) => {
|
||||
response.json(await listScenes(db, routeParam(request, 'deckId')));
|
||||
}));
|
||||
app.post('/api/ca/decks/:deckId/scenes', asyncRoute(async (request, response) => {
|
||||
const scene = await createScene(db, {
|
||||
...request.body,
|
||||
deckId: routeParam(request, 'deckId')
|
||||
});
|
||||
response.status(201).json(scene);
|
||||
}));
|
||||
app.get('/api/ca/scenes/:sceneId', asyncRoute(async (request, response) => {
|
||||
const scene = await getScene(db, routeParam(request, 'sceneId'));
|
||||
if (!scene) {
|
||||
response.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
response.json(scene);
|
||||
}));
|
||||
app.patch('/api/ca/scenes/:sceneId', asyncRoute(async (request, response) => {
|
||||
const scene = await updateScene(db, routeParam(request, 'sceneId'), request.body);
|
||||
if (!scene) {
|
||||
response.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
response.json(scene);
|
||||
}));
|
||||
app.delete('/api/ca/scenes/:sceneId', asyncRoute(async (request, response) => {
|
||||
const deleted = await deleteScene(db, routeParam(request, 'sceneId'));
|
||||
response.sendStatus(deleted ? 204 : 404);
|
||||
}));
|
||||
app.get('/api/ca/scenes/:sceneId/resolved', asyncRoute(async (request, response) => {
|
||||
const resolved = await resolveScene(db, routeParam(request, 'sceneId'));
|
||||
if (!resolved) {
|
||||
response.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
response.json(resolved);
|
||||
}));
|
||||
app.use((error, _request, response, _next) => {
|
||||
const message = error instanceof Error ? error.message : 'Unknown error';
|
||||
response.status(400).json({ error: message });
|
||||
});
|
||||
return app;
|
||||
}
|
||||
//# sourceMappingURL=caStudioApi.js.map
|
||||
Reference in New Issue
Block a user