feat: add sqlite catalog web app

This commit is contained in:
Keith Solomon
2026-05-17 14:05:25 -05:00
parent 140c16891f
commit fe0678fac2
22 changed files with 1452 additions and 12 deletions
+101
View File
@@ -0,0 +1,101 @@
import express from 'express';
import { CatalogDatabase } from '../database/store.js';
import { dashboard, page, table } from './views.js';
export function createWebApp(databasePath: string) {
const app = express();
app.get('/', (_req, res, next) => {
withDatabase(databasePath, (db) => res.send(dashboard(db.dashboardCounts()))).catch(next);
});
app.get('/links', (_req, res, next) => {
withDatabase(databasePath, (db) =>
res.send(
page(
'Links',
`<h1>Links</h1>${table(db.contentLinks(), [
['newsletter', 'Newsletter'],
['issueDate', 'Issue Date'],
['category', 'Category'],
['title', 'Title'],
['url', 'URL'],
['description', 'Description']
])}`
)
)
).catch(next);
});
app.get('/sponsors', (_req, res, next) => {
withDatabase(databasePath, (db) =>
res.send(
page(
'Sponsored Links',
`<h1>Sponsored Links</h1>${table(db.sponsoredLinks(), [
['newsletter', 'Newsletter'],
['sponsor', 'Sponsor'],
['description', 'Description']
])}`
)
)
).catch(next);
});
app.get('/dead-links', (_req, res, next) => {
withDatabase(databasePath, (db) =>
res.send(
page(
'Dead Links',
`<h1>Dead Links</h1>${table(db.deadLinks(), [
['url', 'URL'],
['status', 'Status'],
['source', 'Source'],
['date', 'Date']
])}`
)
)
).catch(next);
});
app.get('/runs', (_req, res, next) => {
withDatabase(databasePath, (db) =>
res.send(
page(
'Runs',
`<h1>Runs</h1>${table(db.runs(), [
['started_at', 'Started'],
['mode', 'Mode'],
['newsletters_processed', 'Newsletters'],
['links_extracted', 'Links'],
['sponsors', 'Sponsors'],
['dead_links', 'Dead Links'],
['errors', 'Errors']
])}`
)
)
).catch(next);
});
app.use(
(error: Error, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
console.error(error);
res.status(500).send(page('Error', '<h1>Error</h1><p>Something went wrong.</p>'));
}
);
return app;
}
async function withDatabase(
databasePath: string,
callback: (database: CatalogDatabase) => void
): Promise<void> {
const db = new CatalogDatabase(databasePath);
try {
db.migrate();
callback(db);
} finally {
db.close();
}
}