113 lines
3.8 KiB
JavaScript
113 lines
3.8 KiB
JavaScript
|
import { dirname, isAbsolute, join } from 'node:path';
|
||
|
import { fileURLToPath } from 'node:url';
|
||
|
import { App } from '@tinyhttp/app';
|
||
|
import { cors } from '@tinyhttp/cors';
|
||
|
import { Eta } from 'eta';
|
||
|
import { json } from 'milliparsec';
|
||
|
import sirv from 'sirv';
|
||
|
import { isItem, Service } from './service.js';
|
||
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||
|
const isProduction = process.env['NODE_ENV'] === 'production';
|
||
|
const eta = new Eta({
|
||
|
views: join(__dirname, '../views'),
|
||
|
cache: isProduction,
|
||
|
});
|
||
|
export function createApp(db, options = {}) {
|
||
|
// Create service
|
||
|
const service = new Service(db);
|
||
|
// Create app
|
||
|
const app = new App();
|
||
|
// Static files
|
||
|
app.use(sirv('public', { dev: !isProduction }));
|
||
|
options.static
|
||
|
?.map((path) => (isAbsolute(path) ? path : join(process.cwd(), path)))
|
||
|
.forEach((dir) => app.use(sirv(dir, { dev: !isProduction })));
|
||
|
// CORS
|
||
|
app
|
||
|
.use((req, res, next) => {
|
||
|
return cors({
|
||
|
allowedHeaders: req.headers['access-control-request-headers']
|
||
|
?.split(',')
|
||
|
.map((h) => h.trim()),
|
||
|
})(req, res, next);
|
||
|
})
|
||
|
.options('*', cors());
|
||
|
// Body parser
|
||
|
// @ts-expect-error expected
|
||
|
app.use(json());
|
||
|
app.get('/', (_req, res) => res.send(eta.render('index.html', { data: db.data })));
|
||
|
app.get('/:name', (req, res, next) => {
|
||
|
const { name = '' } = req.params;
|
||
|
const query = Object.fromEntries(Object.entries(req.query)
|
||
|
.map(([key, value]) => {
|
||
|
if (['_start', '_end', '_limit', '_page', '_per_page'].includes(key) &&
|
||
|
typeof value === 'string') {
|
||
|
return [key, parseInt(value)];
|
||
|
}
|
||
|
else {
|
||
|
return [key, value];
|
||
|
}
|
||
|
})
|
||
|
.filter(([, value]) => !Number.isNaN(value)));
|
||
|
res.locals['data'] = service.find(name, query);
|
||
|
next?.();
|
||
|
});
|
||
|
app.get('/:name/:id', (req, res, next) => {
|
||
|
const { name = '', id = '' } = req.params;
|
||
|
res.locals['data'] = service.findById(name, id, req.query);
|
||
|
next?.();
|
||
|
});
|
||
|
app.post('/:name', async (req, res, next) => {
|
||
|
const { name = '' } = req.params;
|
||
|
if (isItem(req.body)) {
|
||
|
res.locals['data'] = await service.create(name, req.body);
|
||
|
}
|
||
|
next?.();
|
||
|
});
|
||
|
app.put('/:name', async (req, res, next) => {
|
||
|
const { name = '' } = req.params;
|
||
|
if (isItem(req.body)) {
|
||
|
res.locals['data'] = await service.update(name, req.body);
|
||
|
}
|
||
|
next?.();
|
||
|
});
|
||
|
app.put('/:name/:id', async (req, res, next) => {
|
||
|
const { name = '', id = '' } = req.params;
|
||
|
if (isItem(req.body)) {
|
||
|
res.locals['data'] = await service.updateById(name, id, req.body);
|
||
|
}
|
||
|
next?.();
|
||
|
});
|
||
|
app.patch('/:name', async (req, res, next) => {
|
||
|
const { name = '' } = req.params;
|
||
|
if (isItem(req.body)) {
|
||
|
res.locals['data'] = await service.patch(name, req.body);
|
||
|
}
|
||
|
next?.();
|
||
|
});
|
||
|
app.patch('/:name/:id', async (req, res, next) => {
|
||
|
const { name = '', id = '' } = req.params;
|
||
|
if (isItem(req.body)) {
|
||
|
res.locals['data'] = await service.patchById(name, id, req.body);
|
||
|
}
|
||
|
next?.();
|
||
|
});
|
||
|
app.delete('/:name/:id', async (req, res, next) => {
|
||
|
const { name = '', id = '' } = req.params;
|
||
|
res.locals['data'] = await service.destroyById(name, id, req.query['_dependent']);
|
||
|
next?.();
|
||
|
});
|
||
|
app.use('/:name', (req, res) => {
|
||
|
const { data } = res.locals;
|
||
|
if (data === undefined) {
|
||
|
res.sendStatus(404);
|
||
|
}
|
||
|
else {
|
||
|
if (req.method === 'POST')
|
||
|
res.status(201);
|
||
|
res.json(data);
|
||
|
}
|
||
|
});
|
||
|
return app;
|
||
|
}
|