Split services into core and request

This commit is contained in:
2026-01-17 16:19:32 -06:00
parent e59bb35ac9
commit 8a7682e953
12 changed files with 76 additions and 73 deletions

View File

@@ -1,7 +1,5 @@
## high importance ## high importance
- [ ] nix services/ and split it up into core/ request/
- [ ] Add unit tests all over the place. - [ ] Add unit tests all over the place.
- ⚠️ Huge task - needs breakdown before starting - ⚠️ Huge task - needs breakdown before starting

View File

@@ -7,9 +7,11 @@ import { Session } from "./auth";
import { cli } from "./cli"; import { cli } from "./cli";
import { contentTypes } from "./content-types"; import { contentTypes } from "./content-types";
import { runWithContext } from "./context"; import { runWithContext } from "./context";
import { core } from "./core";
import { httpCodes } from "./http-codes"; import { httpCodes } from "./http-codes";
import { request } from "./request";
import { routes } from "./routes"; import { routes } from "./routes";
import { services } from "./services";
// import { URLPattern } from 'node:url'; // import { URLPattern } from 'node:url';
import { import {
AuthenticationRequired, AuthenticationRequired,
@@ -31,7 +33,7 @@ const app = express();
app.use(express.json()); app.use(express.json());
app.use(express.urlencoded({ extended: true })); app.use(express.urlencoded({ extended: true }));
services.logging.log({ source: "logging", text: ["1"] }); core.logging.log({ source: "logging", text: ["1"] });
const processedRoutes: { [K in Method]: ProcessedRoute[] } = { const processedRoutes: { [K in Method]: ProcessedRoute[] } = {
GET: [], GET: [],
POST: [], POST: [],
@@ -50,9 +52,9 @@ routes.forEach((route: Route, _idx: number, _allRoutes: Route[]) => {
const methodList = route.methods; const methodList = route.methods;
const handler: InternalHandler = async ( const handler: InternalHandler = async (
request: ExpressRequest, expressRequest: ExpressRequest,
): Promise<Result> => { ): Promise<Result> => {
const method = massageMethod(request.method); const method = massageMethod(expressRequest.method);
console.log("method", method); console.log("method", method);
@@ -60,17 +62,17 @@ routes.forEach((route: Route, _idx: number, _allRoutes: Route[]) => {
// XXX: Worth asserting this? // XXX: Worth asserting this?
} }
console.log("request.originalUrl", request.originalUrl); console.log("request.originalUrl", expressRequest.originalUrl);
// Authenticate the request // Authenticate the request
const auth = await services.auth.validateRequest(request); const auth = await request.auth.validateRequest(expressRequest);
const req: Call = { const req: Call = {
pattern: route.path, pattern: route.path,
path: request.originalUrl, path: expressRequest.originalUrl,
method, method,
parameters: { one: 1, two: 2 }, parameters: { one: 1, two: 2 },
request, request: expressRequest,
user: auth.user, user: auth.user,
session: new Session(auth.session, auth.user), session: new Session(auth.session, auth.user),
}; };

View File

@@ -5,7 +5,7 @@
import { z } from "zod"; import { z } from "zod";
import { contentTypes } from "../content-types"; import { contentTypes } from "../content-types";
import { httpCodes } from "../http-codes"; import { httpCodes } from "../http-codes";
import { services } from "../services"; import { request } from "../request";
import type { Call, Result, Route } from "../types"; import type { Call, Result, Route } from "../types";
import { import {
forgotPasswordInputParser, forgotPasswordInputParser,
@@ -39,7 +39,7 @@ const loginHandler = async (call: Call): Promise<Result> => {
const body = call.request.body; const body = call.request.body;
const { email, password } = loginInputParser.parse(body); const { email, password } = loginInputParser.parse(body);
const result = await services.auth.login(email, password, "cookie", { const result = await request.auth.login(email, password, "cookie", {
userAgent: call.request.get("User-Agent"), userAgent: call.request.get("User-Agent"),
ipAddress: call.request.ip, ipAddress: call.request.ip,
}); });
@@ -72,9 +72,9 @@ const loginHandler = async (call: Call): Promise<Result> => {
// POST /auth/logout // POST /auth/logout
const logoutHandler = async (call: Call): Promise<Result> => { const logoutHandler = async (call: Call): Promise<Result> => {
const token = services.auth.extractToken(call.request); const token = request.auth.extractToken(call.request);
if (token) { if (token) {
await services.auth.logout(token); await request.auth.logout(token);
} }
return jsonResponse(httpCodes.success.OK, { message: "Logged out" }); return jsonResponse(httpCodes.success.OK, { message: "Logged out" });
@@ -87,7 +87,7 @@ const registerHandler = async (call: Call): Promise<Result> => {
const { email, password, displayName } = const { email, password, displayName } =
registerInputParser.parse(body); registerInputParser.parse(body);
const result = await services.auth.register( const result = await request.auth.register(
email, email,
password, password,
displayName, displayName,
@@ -128,7 +128,7 @@ const forgotPasswordHandler = async (call: Call): Promise<Result> => {
const body = call.request.body; const body = call.request.body;
const { email } = forgotPasswordInputParser.parse(body); const { email } = forgotPasswordInputParser.parse(body);
const result = await services.auth.createPasswordResetToken(email); const result = await request.auth.createPasswordResetToken(email);
// Always return success (don't reveal if email exists) // Always return success (don't reveal if email exists)
if (result) { if (result) {
@@ -159,7 +159,7 @@ const resetPasswordHandler = async (call: Call): Promise<Result> => {
const body = call.request.body; const body = call.request.body;
const { token, password } = resetPasswordInputParser.parse(body); const { token, password } = resetPasswordInputParser.parse(body);
const result = await services.auth.resetPassword(token, password); const result = await request.auth.resetPassword(token, password);
if (!result.success) { if (!result.success) {
return errorResponse( return errorResponse(
@@ -195,7 +195,7 @@ const verifyEmailHandler = async (call: Call): Promise<Result> => {
); );
} }
const result = await services.auth.verifyEmail(token); const result = await request.auth.verifyEmail(token);
if (!result.success) { if (!result.success) {
return errorResponse(httpCodes.clientErrors.BadRequest, result.error); return errorResponse(httpCodes.clientErrors.BadRequest, result.error);

View File

@@ -1,8 +1,8 @@
import { SESSION_COOKIE_NAME } from "../auth/token"; import { SESSION_COOKIE_NAME } from "../auth/token";
import { tokenLifetimes } from "../auth/types"; import { tokenLifetimes } from "../auth/types";
import { services } from "../services"; import { request } from "../request";
import type { Call, Result, Route } from "../types";
import { html, redirect, render } from "../request/util"; import { html, redirect, render } from "../request/util";
import type { Call, Result, Route } from "../types";
const loginHandler = async (call: Call): Promise<Result> => { const loginHandler = async (call: Call): Promise<Result> => {
if (call.method === "GET") { if (call.method === "GET") {
@@ -21,7 +21,7 @@ const loginHandler = async (call: Call): Promise<Result> => {
return html(c); return html(c);
} }
const result = await services.auth.login(email, password, "cookie", { const result = await request.auth.login(email, password, "cookie", {
userAgent: call.request.get("User-Agent"), userAgent: call.request.get("User-Agent"),
ipAddress: call.request.ip, ipAddress: call.request.ip,
}); });

View File

@@ -1,13 +1,13 @@
import { SESSION_COOKIE_NAME } from "../auth/token"; import { SESSION_COOKIE_NAME } from "../auth/token";
import { services } from "../services"; import { request } from "../request";
import type { Call, Result, Route } from "../types";
import { redirect } from "../request/util"; import { redirect } from "../request/util";
import type { Call, Result, Route } from "../types";
const logoutHandler = async (call: Call): Promise<Result> => { const logoutHandler = async (call: Call): Promise<Result> => {
// Extract token from cookie and invalidate the session // Extract token from cookie and invalidate the session
const token = services.auth.extractToken(call.request); const token = request.auth.extractToken(call.request);
if (token) { if (token) {
await services.auth.logout(token); await request.auth.logout(token);
} }
// Clear the cookie and redirect to login // Clear the cookie and redirect to login

View File

@@ -1,7 +1,7 @@
import { DateTime } from "ts-luxon"; import { DateTime } from "ts-luxon";
import { services } from "../services"; import { request } from "../request";
import type { Call, Result, Route } from "../types";
import { html, render } from "../request/util"; import { html, render } from "../request/util";
import type { Call, Result, Route } from "../types";
import { loginRoute } from "./login"; import { loginRoute } from "./login";
import { logoutRoute } from "./logout"; import { logoutRoute } from "./logout";
@@ -20,8 +20,8 @@ const routes: Record<string, Route> = {
path: "/", path: "/",
methods: ["GET"], methods: ["GET"],
handler: async (_call: Call): Promise<Result> => { handler: async (_call: Call): Promise<Result> => {
const auth = services.auth; const _auth = request.auth;
const me = services.session.getUser(); const me = request.session.getUser();
const email = me.toString(); const email = me.toString();
const c = await render("basic/home", { email }); const c = await render("basic/home", { email });

View File

@@ -1,20 +1,16 @@
// services.ts import nunjucks from "nunjucks";
import { db, migrate, migrationStatus } from "../database";
import { AuthService } from "../auth";
import { getCurrentUser } from "../context";
import { db, migrate, migrationStatus, PostgresAuthStore } from "../database";
import { getLogs, log } from "../logging"; import { getLogs, log } from "../logging";
import type { MaybeUser } from "../user";
import nunjucks from 'nunjucks'
// FIXME: This doesn't belong here; move it somewhere else.
const conf = { const conf = {
templateEngine: () => { templateEngine: () => {
return { return {
renderTemplate: (template: string, context: object) => { renderTemplate: (template: string, context: object) => {
return nunjucks.renderString(template, context); return nunjucks.renderString(template, context);
}, },
} };
} },
}; };
const database = { const database = {
@@ -40,25 +36,13 @@ const misc = {
}, },
}; };
const session = {
getUser: (): MaybeUser => {
return getCurrentUser();
},
};
// Initialize auth with PostgreSQL store
const authStore = new PostgresAuthStore();
const auth = new AuthService(authStore);
// Keep this asciibetically sorted // Keep this asciibetically sorted
const services = { const core = {
auth,
conf, conf,
database, database,
logging, logging,
misc, misc,
random, random,
session,
}; };
export { services }; export { core };

View File

@@ -1,11 +1,11 @@
import { contentTypes } from "./content-types"; import { contentTypes } from "./content-types";
import { core } from "./core";
import { httpCodes } from "./http-codes"; import { httpCodes } from "./http-codes";
import { services } from "./services";
import type { Call, Handler, Result } from "./types"; import type { Call, Handler, Result } from "./types";
const multiHandler: Handler = async (call: Call): Promise<Result> => { const multiHandler: Handler = async (call: Call): Promise<Result> => {
const code = httpCodes.success.OK; const code = httpCodes.success.OK;
const rn = services.random.randomNumber(); const rn = core.random.randomNumber();
const retval: Result = { const retval: Result = {
code, code,

25
express/request/index.ts Normal file
View File

@@ -0,0 +1,25 @@
import { AuthService } from "../auth";
import { getCurrentUser } from "../context";
import { PostgresAuthStore } from "../database";
import type { MaybeUser } from "../user";
import { html, redirect, render } from "./util";
const util = { html, redirect, render };
const session = {
getUser: (): MaybeUser => {
return getCurrentUser();
},
};
// Initialize auth with PostgreSQL store
const authStore = new PostgresAuthStore();
const auth = new AuthService(authStore);
const request = {
auth,
session,
util,
};
export { request };

View File

@@ -1,24 +1,23 @@
import { contentTypes } from "../content-types"; import { contentTypes } from "../content-types";
import { core } from "../core";
import { executionContext } from "../execution-context"; import { executionContext } from "../execution-context";
import { httpCodes } from "../http-codes"; import { httpCodes } from "../http-codes";
import type { RedirectResult, Result } from "../types"; import type { RedirectResult, Result } from "../types";
import {services} from '../services' import { loadFile } from "../util";
import { request } from "./index";
import{loadFile}from'../util'
type NoUser = { type NoUser = {
[key: string]: unknown; [key: string]: unknown;
} & { } & {
user?: never; user?: never;
} };
const render = async (path: string, ctx?: NoUser): Promise<string> => { const render = async (path: string, ctx?: NoUser): Promise<string> => {
const fullPath = `${executionContext.diachron_root}/templates/${path}.html.njk`; const fullPath = `${executionContext.diachron_root}/templates/${path}.html.njk`;
const template = await loadFile(fullPath); const template = await loadFile(fullPath);
const user = services.session.getUser(); const user = request.session.getUser();
const context = {user, ...ctx} const context = { user, ...ctx };
const engine = services.conf.templateEngine() const engine = core.conf.templateEngine();
const retval = engine.renderTemplate(template, context); const retval = engine.renderTemplate(template, context);
return retval; return retval;
@@ -43,4 +42,4 @@ const redirect = (location: string): RedirectResult => {
}; };
}; };
export { render, html, redirect }; export { html, redirect, render };

View File

@@ -5,9 +5,9 @@ import { DateTime } from "ts-luxon";
import { authRoutes } from "./auth/routes"; import { authRoutes } from "./auth/routes";
import { routes as basicRoutes } from "./basic/routes"; import { routes as basicRoutes } from "./basic/routes";
import { contentTypes } from "./content-types"; import { contentTypes } from "./content-types";
import { core } from "./core";
import { multiHandler } from "./handlers"; import { multiHandler } from "./handlers";
import { httpCodes } from "./http-codes"; import { httpCodes } from "./http-codes";
import { services } from "./services";
import type { Call, Result, Route } from "./types"; import type { Call, Result, Route } from "./types";
// FIXME: Obviously put this somewhere else // FIXME: Obviously put this somewhere else
@@ -35,7 +35,7 @@ const routes: Route[] = [
handler: async (_call: Call): Promise<Result> => { handler: async (_call: Call): Promise<Result> => {
console.log("starting slow request"); console.log("starting slow request");
await services.misc.sleep(2); await core.misc.sleep(2);
console.log("finishing slow request"); console.log("finishing slow request");
const retval = okText("that was slow"); const retval = okText("that was slow");

View File

@@ -1,5 +1,4 @@
import { readFile } from "node:fs/promises"; import { readFile } from "node:fs/promises";
import nunjucks from "nunjucks";
// FIXME: Handle the error here // FIXME: Handle the error here
const loadFile = async (path: string): Promise<string> => { const loadFile = async (path: string): Promise<string> => {
@@ -9,8 +8,4 @@ const loadFile = async (path: string): Promise<string> => {
return data; return data;
}; };
export { loadFile };
export {
loadFile,
}