Embed base images into binary

Include resource directory
Ran `deno fmt` with 4 space indent, that changed every file (!!!!!)
various changes
This commit is contained in:
2025-03-24 19:11:36 -04:00
parent 2207e389c9
commit 49c481aa0e
61 changed files with 2369 additions and 2031 deletions

View File

@@ -4,27 +4,29 @@ import Logging from "@proxnet/undead-logging";
import { decode } from "@gz/jwt";
import { Config } from "./config.ts";
import { AuthType, User, UserTokenFormat } from "./data/users.ts";
import Profile, { ProfileTokenFormat } from "./data/profiles.ts";
const config = Config.getConfig();
const log = new Logging('APIUtils');
const log = new Logging("APIUtils");
type AppRouter = {
path: string,
router: express.Router
}
path: string;
router: express.Router;
};
export function createRouter(path: string) {
const router: AppRouter = {
path: path,
router: express.Router()
}
router: express.Router(),
};
return router;
}
export function generateRandomString(length: number) {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let randomString = '';
const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let randomString = "";
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * characters.length);
@@ -35,11 +37,20 @@ export function generateRandomString(length: number) {
}
export function checkQueryTypes<T>(typeDef: T) {
return (rq: express.Request, rs: express.Response, nxt: express.NextFunction) => {
return (
rq: express.Request,
rs: express.Response,
nxt: express.NextFunction,
) => {
for (const key in typeDef) {
if (typeof rq.query[key] !== typeof (typeDef)[key]) {
if (typeof rq.query[key] !== typeof typeDef[key]) {
rs.statusCode = 400;
rs.json(genericResponseFormat(true, "One or more query parameters were invalid or not found."));
rs.json(
genericResponseFormat(
true,
"One or more query parameters were invalid or not found.",
),
);
return;
}
}
@@ -47,12 +58,21 @@ export function checkQueryTypes<T>(typeDef: T) {
};
}
export function checkBodyTypes<T>(typeDef: T) {
return (rq: express.Request, rs: express.Response, nxt: express.NextFunction) => {
for (const key in typeDef) {
if (typeof rq.body[key] !== typeof (typeDef)[key]) {
return (
rq: express.Request,
rs: express.Response,
nxt: express.NextFunction,
) => {
for (const key in typeDef) {
if (typeof rq.body[key] !== typeof typeDef[key]) {
log.e(`Body check for key '${key}' failed.`);
rs.statusCode = 400;
rs.json(genericResponseFormat(true, "One or more body values were invalid or not found."));
rs.json(
genericResponseFormat(
true,
"One or more body values were invalid or not found.",
),
);
return;
}
}
@@ -60,28 +80,40 @@ export function checkBodyTypes<T>(typeDef: T) {
};
}
export function genericResponseFormat(failure: boolean, msg: string | null = null, data: object | null = null) {
export function genericResponseFormat(
failure: boolean,
msg: string | null = null,
data: object | null = null,
) {
return { failed: failure, message: msg, data: data };
}
export function genericResponse(failure: boolean, msg: string | null = null, data: object | null = null) {
export function genericResponse(
failure: boolean,
msg: string | null = null,
data: object | null = null,
) {
return (_rq: express.Request, rs: express.Response) => {
rs.json({ failed: failure, message: msg, data: data });
};
}
type RecNetResponse = {
Success: boolean,
Message: string
Success: boolean;
Message: string;
};
export function RecNetResponse(success: boolean, message: string) {
const msg: RecNetResponse = { Success: success, Message: message };
return (_rq: express.Request, rs: express.Response) => {
rs.json(msg);
}
};
}
export function logBody(rq: express.Request, _rs: express.Response, nxt: express.NextFunction) {
nxt();
export function logBody(
rq: express.Request,
_rs: express.Response,
nxt: express.NextFunction,
) {
log.d(`Request body: ${JSON.stringify(rq.body)}`);
nxt();
}
export function emptyArrayResponse(_rq: express.Request, rs: express.Response) {
@@ -89,27 +121,26 @@ export function emptyArrayResponse(_rq: express.Request, rs: express.Response) {
}
export function getSrcIpDefault(rq: express.Request) {
const cfIp = rq.header('cf-connecting-ip');
const cfIp = rq.header("cf-connecting-ip");
if (cfIp !== undefined) return cfIp;
const xrIp = rq.header('x-real-ip');
const xrIp = rq.header("x-real-ip");
if (xrIp !== undefined) return xrIp;
const ip = typeof rq.ip === 'undefined' ? '(unknown source)' : rq.ip;
const ip = typeof rq.ip === "undefined" ? "(unknown source)" : rq.ip;
return ip;
}
export function statusResponse(code: number) {
return (_rq: express.Request, rs: express.Response) => {
rs.sendStatus(code);
}
};
}
export class RateLimiter {
#intervalId: number;
#intervalId: number
#hitLimit: number
#hitLimit: number;
#addressHits: Map<string, number> = new Map();
@@ -118,17 +149,15 @@ export class RateLimiter {
* @param limit Number of hits (inclusive) before requests are blocked
*/
constructor(interval: number, limit: number) {
this.#hitLimit = limit;
this.#intervalId = setInterval(() => {
this.#addressHits.clear();
}, interval * 1000);
Deno.addSignalListener('SIGINT', () => {
Deno.addSignalListener("SIGINT", () => {
this.#close();
});
}
#addressIncrement(address: string) {
@@ -147,66 +176,89 @@ export class RateLimiter {
}
middle() {
return (rq: express.Request, rs: express.Response, nxt: express.NextFunction) => {
return (
rq: express.Request,
rs: express.Response,
nxt: express.NextFunction,
) => {
const address = getSrcIpDefault(rq);
this.#addressIncrement(address);
const hits = this.#getAddressHits(address);
if (hits && hits > this.#hitLimit) {
rs.statusCode = 429;
rs.json(genericResponseFormat(true, `Rate limit for address ${address} reached. Try again in a moment.`));
rs.json(
genericResponseFormat(
true,
`Rate limit for address ${address} reached. Try again in a moment.`,
),
);
return;
} else nxt();
}
};
}
#close() {
clearInterval(this.#intervalId);
}
}
export async function UserAuthentication(rq: express.Request, rs: express.Response, nxt: express.NextFunction) {
export interface TokenBaseFormat {
typ: AuthType;
iss: string;
nbf: number;
exp: number;
iat: number;
}
export type TokenFormat = UserTokenFormat | ProfileTokenFormat;
export async function Authentication(
rq: express.Request,
rs: express.Response,
nxt: express.NextFunction,
) {
function returnUnauthorized() {
rs.statusCode = 401;
rs.json(genericResponseFormat(true, 'Authorization required.'));
rs.json(genericResponseFormat(true, "Authorization required."));
}
const token: string | undefined = rq.header('GalvanicAuth');
if (typeof token == 'undefined') {
const token: string | undefined = rq.header("GalvanicAuth");
if (typeof token == "undefined") {
returnUnauthorized();
return;
}
try {
const decodedToken = await decode<UserTokenFormat>(token, config.auth.secret, { algorithm: "HS512" });
const decodedToken = await decode<TokenFormat>(
token,
config.auth.secret,
{
algorithm: "HS512",
},
);
const valid = ![
decodedToken.iss == config.web.publichost,
decodedToken.nbf < Math.round(Date.now() / 1000),
decodedToken.exp > Math.round(Date.now() / 1000),
decodedToken.typ == AuthType.Web
].includes(false);
if (valid) {
rs.locals.user = new User(decodedToken.sub);
if (decodedToken.typ == AuthType.Web) {
rs.locals.user = new User(decodedToken.sub);
} else if (decodedToken.typ == AuthType.Game) {
rs.locals.profile = new Profile(decodedToken.sub);
}
nxt();
}
else {
} else {
returnUnauthorized();
return;
}
} catch (err) {
returnUnauthorized();
log.w(`User Authentication failed: ${err}`);
}
}
export type NoBody = Record<string | number | symbol, never>
export type NoBody = Record<string | number | symbol, never>;
export * as APIUtils from "./apiutils.ts"
export * as APIUtils from "./apiutils.ts";