style(shared): auto-correction of the linter

- using pnpm eslint --fix ./src
This commit is contained in:
Raphael 2025-09-28 19:03:51 +02:00
parent 77324f6d89
commit b56906b557
No known key found for this signature in database
6 changed files with 126 additions and 125 deletions

View file

@ -1,23 +1,23 @@
import OTP from "otp"; import OTP from 'otp';
import cookie from "@fastify/cookie"; import cookie from '@fastify/cookie';
import fastifyJwt from "@fastify/jwt"; import fastifyJwt from '@fastify/jwt';
import fp from "fastify-plugin"; import fp from 'fastify-plugin';
import { FastifyPluginAsync, preValidationAsyncHookHandler } from "fastify"; import { FastifyPluginAsync, preValidationAsyncHookHandler } from 'fastify';
import { Static, Type } from "@sinclair/typebox"; import { Static, Type } from '@sinclair/typebox';
import { UserId } from "@shared/database/mixin/user"; import { UserId } from '@shared/database/mixin/user';
import { useDatabase } from "@shared/database"; import { useDatabase } from '@shared/database';
import { isNullish, makeResponse } from "@shared/utils"; import { isNullish, makeResponse } from '@shared/utils';
const kRouteAuthDone = Symbol("shared-route-auth-done"); const kRouteAuthDone = Symbol('shared-route-auth-done');
type AuthedUser = { type AuthedUser = {
id: UserId; id: UserId;
name: string; name: string;
}; };
declare module "fastify" { declare module 'fastify' {
export interface FastifyInstance { export interface FastifyInstance {
signJwt: (kind: "auth" | "otp", who: string) => string; signJwt: (kind: 'auth' | 'otp', who: string) => string;
[s: symbol]: boolean; [s: symbol]: boolean;
} }
export interface FastifyRequest { export interface FastifyRequest {
@ -33,10 +33,10 @@ let jwtAdded = false;
export const jwtPlugin = fp<FastifyPluginAsync>(async (fastify, _opts) => { export const jwtPlugin = fp<FastifyPluginAsync>(async (fastify, _opts) => {
if (jwtAdded) return; if (jwtAdded) return;
jwtAdded = true; jwtAdded = true;
let env = process.env.JWT_SECRET; const env = process.env.JWT_SECRET;
if (isNullish(env)) throw "JWT_SECRET is not defined"; if (isNullish(env)) throw 'JWT_SECRET is not defined';
if (!fastify.hasDecorator("signJwt")) { if (!fastify.hasDecorator('signJwt')) {
void fastify.decorate("signJwt", (kind, who) => void fastify.decorate('signJwt', (kind, who) =>
fastify.jwt.sign({ kind, who, createdAt: Date.now() }), fastify.jwt.sign({ kind, who, createdAt: Date.now() }),
); );
void fastify.register(fastifyJwt, { void fastify.register(fastifyJwt, {
@ -48,16 +48,16 @@ export const jwtPlugin = fp<FastifyPluginAsync>(async (fastify, _opts) => {
export const JwtType = Type.Object({ export const JwtType = Type.Object({
kind: Type.Union([ kind: Type.Union([
Type.Const("otp", { Type.Const('otp', {
description: "the token is only valid for otp call", description: 'the token is only valid for otp call',
}), }),
Type.Const("auth", { Type.Const('auth', {
description: "the token is valid for authentication", description: 'the token is valid for authentication',
}), }),
]), ]),
who: Type.String({ description: "the login of the user" }), who: Type.String({ description: 'the login of the user' }),
createdAt: Type.Integer({ createdAt: Type.Integer({
description: "Unix timestamp of when the token as been created at", description: 'Unix timestamp of when the token as been created at',
}), }),
}); });
@ -65,52 +65,57 @@ export type JwtType = Static<typeof JwtType>;
let authAdded = false; let authAdded = false;
export const authPlugin = fp<FastifyPluginAsync>(async (fastify, _opts) => { export const authPlugin = fp<FastifyPluginAsync>(async (fastify, _opts) => {
if (authAdded) return void console.log("skipping"); if (authAdded) return void console.log('skipping');
authAdded = true; authAdded = true;
await fastify.register(useDatabase as any, {}); await fastify.register(useDatabase as any, {});
await fastify.register(jwtPlugin as any, {}); await fastify.register(jwtPlugin as any, {});
await fastify.register(cookie); await fastify.register(cookie);
if (!fastify.hasRequestDecorator("authUser")) if (!fastify.hasRequestDecorator('authUser')) {fastify.decorateRequest('authUser', undefined);}
fastify.decorateRequest("authUser", undefined); fastify.addHook('onRoute', (routeOpts) => {
fastify.addHook("onRoute", (routeOpts) => {
if ( if (
routeOpts.config?.requireAuth && routeOpts.config?.requireAuth &&
!(routeOpts as any)[kRouteAuthDone] !(routeOpts as any)[kRouteAuthDone]
) { ) {
let f: preValidationAsyncHookHandler = async function(req, res) { const f: preValidationAsyncHookHandler = async function(req, res) {
try { try {
if (isNullish(req.cookies.token)) if (isNullish(req.cookies.token)) {
return res return res
.clearCookie("token") .clearCookie('token')
.send( .send(
JSON.stringify(makeResponse("notLoggedIn", "auth.noCookie")), JSON.stringify(makeResponse('notLoggedIn', 'auth.noCookie')),
); );
let tok = this.jwt.verify<JwtType>(req.cookies.token); }
if (tok.kind != "auth") const tok = this.jwt.verify<JwtType>(req.cookies.token);
if (tok.kind != 'auth') {
return res return res
.clearCookie("token") .clearCookie('token')
.send( .send(
JSON.stringify(makeResponse("notLoggedIn", "auth.invalidKind")), JSON.stringify(makeResponse('notLoggedIn', 'auth.invalidKind')),
); );
let user = this.db.getUserFromName(tok.who); }
if (isNullish(user)) const user = this.db.getUserFromName(tok.who);
if (isNullish(user)) {
return res return res
.clearCookie("token") .clearCookie('token')
.send( .send(
JSON.stringify(makeResponse("notLoggedIn", "auth.noUser")), JSON.stringify(makeResponse('notLoggedIn', 'auth.noUser')),
); );
}
req.authUser = { id: user.id, name: tok.who }; req.authUser = { id: user.id, name: tok.who };
} catch { }
catch {
return res return res
.clearCookie("token") .clearCookie('token')
.send(JSON.stringify(makeResponse("notLoggedIn", "auth.invalid"))); .send(JSON.stringify(makeResponse('notLoggedIn', 'auth.invalid')));
} }
}; };
if (!routeOpts.preValidation) { if (!routeOpts.preValidation) {
routeOpts.preValidation = [f]; routeOpts.preValidation = [f];
} else if (Array.isArray(routeOpts.preValidation)) { }
else if (Array.isArray(routeOpts.preValidation)) {
routeOpts.preValidation.push(f); routeOpts.preValidation.push(f);
} else { }
else {
routeOpts.preValidation = [routeOpts.preValidation, f]; routeOpts.preValidation = [routeOpts.preValidation, f];
} }

View file

@ -1,8 +1,8 @@
import fp from 'fastify-plugin' import fp from 'fastify-plugin';
import { FastifyInstance, FastifyPluginAsync } from 'fastify' import { FastifyInstance, FastifyPluginAsync } from 'fastify';
import { Database as DbImpl } from "./mixin/_base"; import { Database as DbImpl } from './mixin/_base';
import { UserImpl, IUserDb } from "./mixin/user"; import { UserImpl, IUserDb } from './mixin/user';
import { isNullish } from '@shared/utils'; import { isNullish } from '@shared/utils';
@ -22,16 +22,13 @@ let dbAdded = false;
export const useDatabase = fp<FastifyPluginAsync>(async function( export const useDatabase = fp<FastifyPluginAsync>(async function(
f: FastifyInstance, f: FastifyInstance,
_options: {}) { _options: {}) {
if (dbAdded) if (dbAdded) {return;}
return;
dbAdded = true; dbAdded = true;
let path = process.env.DATABASE_DIR; const path = process.env.DATABASE_DIR;
if (isNullish(path)) if (isNullish(path)) {throw 'env `DATABASE_DIR` not defined';}
throw "env `DATABASE_DIR` not defined"; f.log.info(`Opening database with path: ${path}/database.db`);
f.log.info(`Opening database with path: ${path}/database.db`) const db: Database = new DbImpl(`${path}/database.db`) as Database;
let db: Database = new DbImpl(`${path}/database.db`) as Database; if (!f.hasDecorator('db')) {f.decorate('db', db);}
if (!f.hasDecorator("db"))
f.decorate('db', db);
}); });
export default useDatabase; export default useDatabase;

View file

@ -1,7 +1,7 @@
import sqlite from "better-sqlite3"; import sqlite from 'better-sqlite3';
// @ts-ignore: this file is included using vite, typescript doesn't know how to include this... // @ts-ignore: this file is included using vite, typescript doesn't know how to include this...
import initSql from "../init.sql?raw" import initSql from '../init.sql?raw';
export type SqliteReturn = object | undefined; export type SqliteReturn = object | undefined;
@ -12,31 +12,31 @@ export type SqliteReturn = object | undefined;
// To create a new query function, go open another file, create a class that inherit from this class // To create a new query function, go open another file, create a class that inherit from this class
// in the `index.ts` file, import the new class, and make the `Database` class inherit it // in the `index.ts` file, import the new class, and make the `Database` class inherit it
export class Database { export class Database {
private db: sqlite.Database; private db: sqlite.Database;
private st: Map<string, sqlite.Statement> = new Map(); private st: Map<string, sqlite.Statement> = new Map();
/** /**
* Create a new instance of the database, and init it to a known state * Create a new instance of the database, and init it to a known state
* the file ./init.sql will be ran onto the database, creating any table that might be missing * the file ./init.sql will be ran onto the database, creating any table that might be missing
*/ */
constructor(db_path: string) { constructor(db_path: string) {
this.db = sqlite(db_path, {}); this.db = sqlite(db_path, {});
this.db.pragma('journal_mode = WAL'); this.db.pragma('journal_mode = WAL');
this.db.transaction(() => this.db.exec(initSql))(); this.db.transaction(() => this.db.exec(initSql))();
} }
/** /**
* close the database * close the database
*/ */
public destroy(): void { public destroy(): void {
// remove any statement from the cache // remove any statement from the cache
this.st.clear(); this.st.clear();
// close the database // close the database
this.db?.close(); this.db?.close();
} }
/** /**
* use this to create queries. This will create statements (kinda expensive) and cache them * use this to create queries. This will create statements (kinda expensive) and cache them
* since they will be cached, this means that they are only created once, * since they will be cached, this means that they are only created once,
* otherwise they'll be just spat out from the cache * otherwise they'll be just spat out from the cache
@ -46,12 +46,12 @@ export class Database {
* @example this.prepare('SELECT * FROM users WHERE id = ?') * @example this.prepare('SELECT * FROM users WHERE id = ?')
* @example this.prepare('SELECT * FROM users LIMIT 100 OFFSET ?') * @example this.prepare('SELECT * FROM users LIMIT 100 OFFSET ?')
*/ */
protected prepare(query: string): sqlite.Statement { protected prepare(query: string): sqlite.Statement {
let st = this.st.get(query); let st = this.st.get(query);
if (st !== undefined) return st; if (st !== undefined) return st;
st = this.db.prepare(query); st = this.db.prepare(query);
this.st.set(query, st); this.st.set(query, st);
return st; return st;
} }
} }

View file

@ -1,4 +1,4 @@
import type { Database } from "./_base"; import type { Database } from './_base';
// never use this directly // never use this directly
@ -9,28 +9,28 @@ export interface ITemplateDb extends Database {
}; };
export const UserImpl: Omit<ITemplateDb, keyof Database> = { export const UserImpl: Omit<ITemplateDb, keyof Database> = {
/** /**
* whole function description * whole function description
* *
* @param id the argument description * @param id the argument description
* *
* @returns what does the function return ? * @returns what does the function return ?
*/ */
normalFunction(this: ITemplateDb, id: TemplateId): TemplateData | undefined { normalFunction(this: ITemplateDb, id: TemplateId): TemplateData | undefined {
void id; void id;
return undefined; return undefined;
}, },
/** /**
* whole function description * whole function description
* *
* @param id the argument description * @param id the argument description
* *
* @returns what does the function return ? * @returns what does the function return ?
*/ */
async asyncFunction(this: ITemplateDb, id: TemplateId): Promise<TemplateData | undefined> { async asyncFunction(this: ITemplateDb, id: TemplateId): Promise<TemplateData | undefined> {
void id; void id;
return undefined; return undefined;
}, },
}; };
export type TemplateId = number & { readonly __brand: unique symbol }; export type TemplateId = number & { readonly __brand: unique symbol };
@ -43,13 +43,13 @@ export type TemplateData = {
// this function will be able to be called from everywhere // this function will be able to be called from everywhere
export async function freeFloatingExportedFunction(): Promise<boolean> { export async function freeFloatingExportedFunction(): Promise<boolean> {
return false; return false;
} }
// this function will never be able to be called outside of this module // this function will never be able to be called outside of this module
async function privateFunction(): Promise<string | undefined> { async function privateFunction(): Promise<string | undefined> {
return undefined return undefined;
} }
//silence warnings // silence warnings
void privateFunction; void privateFunction;

View file

@ -1,7 +1,7 @@
import type { Database, SqliteReturn } from "./_base"; import type { Database, SqliteReturn } from './_base';
import { Otp } from "@shared/auth"; import { Otp } from '@shared/auth';
import { isNullish } from "@shared/utils"; import { isNullish } from '@shared/utils';
import * as bcrypt from "bcrypt"; import * as bcrypt from 'bcrypt';
// never use this directly // never use this directly
@ -38,7 +38,7 @@ export const UserImpl: Omit<IUserDb, keyof Database> = {
getUserFromName(this: IUserDb, name: string): User | undefined { getUserFromName(this: IUserDb, name: string): User | undefined {
return userFromRow( return userFromRow(
this.prepare( this.prepare(
"SELECT * FROM user WHERE name = @name LIMIT 1", 'SELECT * FROM user WHERE name = @name LIMIT 1',
).get({ name }), ).get({ name }),
); );
}, },
@ -52,7 +52,7 @@ export const UserImpl: Omit<IUserDb, keyof Database> = {
*/ */
getUserFromRawId(this: IUserDb, id: number): User | undefined { getUserFromRawId(this: IUserDb, id: number): User | undefined {
return userFromRow( return userFromRow(
this.prepare("SELECT * FROM user WHERE id = @id LIMIT 1").get({ this.prepare('SELECT * FROM user WHERE id = @id LIMIT 1').get({
id, id,
}) as SqliteReturn, }) as SqliteReturn,
); );
@ -70,7 +70,7 @@ export const UserImpl: Omit<IUserDb, keyof Database> = {
password = await hashPassword(password); password = await hashPassword(password);
return userFromRow( return userFromRow(
this.prepare( this.prepare(
"INSERT OR FAIL INTO user (name, password) VALUES (@name, @password) RETURNING *", 'INSERT OR FAIL INTO user (name, password) VALUES (@name, @password) RETURNING *',
).get({ name, password }), ).get({ name, password }),
); );
}, },
@ -88,30 +88,29 @@ export const UserImpl: Omit<IUserDb, keyof Database> = {
password = await hashPassword(password); password = await hashPassword(password);
return userFromRow( return userFromRow(
this.prepare( this.prepare(
"UPDATE OR FAIL user SET password = @password WHERE id = @id RETURNING *", 'UPDATE OR FAIL user SET password = @password WHERE id = @id RETURNING *',
).get({ password, id }) as SqliteReturn, ).get({ password, id }) as SqliteReturn,
); );
}, },
getUserOtpSecret(this: IUserDb, id: UserId): string | undefined { getUserOtpSecret(this: IUserDb, id: UserId): string | undefined {
let otp: any = this.prepare("SELECT otp FROM user WHERE id = @id LIMIT 1").get({ id }) as SqliteReturn; const otp: any = this.prepare('SELECT otp FROM user WHERE id = @id LIMIT 1').get({ id }) as SqliteReturn;
if (isNullish(otp?.otp)) return undefined; if (isNullish(otp?.otp)) return undefined;
return otp.otp; return otp.otp;
}, },
ensureUserOtpSecret(this: IUserDb, id: UserId): string | undefined { ensureUserOtpSecret(this: IUserDb, id: UserId): string | undefined {
let otp = this.getUserOtpSecret(id); const otp = this.getUserOtpSecret(id);
if (!isNullish(otp)) if (!isNullish(otp)) {return otp;}
return otp; const otpGen = new Otp();
let otpGen = new Otp(); const res: any = this.prepare('UPDATE OR IGNORE user SET otp = @otp WHERE id = @id RETURNING otp')
const res: any = this.prepare("UPDATE OR IGNORE user SET otp = @otp WHERE id = @id RETURNING otp")
.get({ id, otp: otpGen.secret }); .get({ id, otp: otpGen.secret });
return res?.otp; return res?.otp;
}, },
deleteUserOtpSecret(this: IUserDb, id: UserId): void { deleteUserOtpSecret(this: IUserDb, id: UserId): void {
this.prepare("UPDATE OR IGNORE user SET otp = NULL WHERE id = @id").run({ id }); this.prepare('UPDATE OR IGNORE user SET otp = NULL WHERE id = @id').run({ id });
} },
}; };
export type UserId = number & { readonly __brand: unique symbol }; export type UserId = number & { readonly __brand: unique symbol };

View file

@ -1,4 +1,4 @@
import { Type } from "@sinclair/typebox"; import { Type } from '@sinclair/typebox';
/** /**
* @description Represent a message key * @description Represent a message key
@ -27,8 +27,8 @@ export type ResponseBase<T = {}> = {
* @example makeResponse("success", "login.success", { token: "supersecrettoken" }) * @example makeResponse("success", "login.success", { token: "supersecrettoken" })
*/ */
export function makeResponse<T = {}>(kind: string, key: MessageKey, payload?: T): ResponseBase<T> { export function makeResponse<T = {}>(kind: string, key: MessageKey, payload?: T): ResponseBase<T> {
console.log(`making response {kind: ${JSON.stringify(kind)}; key: ${JSON.stringify(key)}}`) console.log(`making response {kind: ${JSON.stringify(kind)}; key: ${JSON.stringify(key)}}`);
return { kind, msg: key, payload } return { kind, msg: key, payload };
} }
@ -43,7 +43,8 @@ export function typeResponse(kind: string, key: MessageKey | MessageKey[], paylo
let tKey; let tKey;
if (key instanceof Array) { if (key instanceof Array) {
tKey = Type.Union(key.map(l => Type.Const(l))); tKey = Type.Union(key.map(l => Type.Const(l)));
} else { }
else {
tKey = Type.Const(key); tKey = Type.Const(key);
} }
@ -51,10 +52,9 @@ export function typeResponse(kind: string, key: MessageKey | MessageKey[], paylo
kind: Type.Const(kind), kind: Type.Const(kind),
msg: tKey, msg: tKey,
}; };
if (payload !== undefined) if (payload !== undefined) {Object.assign(Ty, { payload: Type.Object(payload) });}
Object.assign(Ty, { payload: Type.Object(payload) })
return Type.Object(Ty) return Type.Object(Ty);
} }
/** /**
@ -69,5 +69,5 @@ export function typeResponse(kind: string, key: MessageKey | MessageKey[], paylo
* @example assert_equal(isNullish(false), false); * @example assert_equal(isNullish(false), false);
*/ */
export function isNullish<T>(v: T | undefined | null): v is (null | undefined) { export function isNullish<T>(v: T | undefined | null): v is (null | undefined) {
return v === null || v === undefined return v === null || v === undefined;
} }