Split services into core and request
This commit is contained in:
2
TODO.md
2
TODO.md
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 });
|
||||||
|
|||||||
@@ -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 };
|
||||||
@@ -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
25
express/request/index.ts
Normal 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 };
|
||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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,
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user