style(auth): auto-correction of the linter

- using pnpm eslint --fix ./src
This commit is contained in:
Raphael 2025-09-28 19:03:59 +02:00
parent b56906b557
commit 38013b77d3
No known key found for this signature in database
13 changed files with 207 additions and 223 deletions

View file

@ -1,107 +1,104 @@
const headers = { const headers = {
'Accept': 'application/json', 'Accept': 'application/json',
'Content-Type': 'application/json' 'Content-Type': 'application/json',
}; };
const tUsername = document.querySelector("#t-username") const tUsername = document.querySelector('#t-username');
const iUsername = document.querySelector("#i-username"); const iUsername = document.querySelector('#i-username');
const iPassword = document.querySelector("#i-password"); const iPassword = document.querySelector('#i-password');
const iOtp = document.querySelector("#i-otp"); const iOtp = document.querySelector('#i-otp');
const bOtpSend = document.querySelector("#b-otpSend"); const bOtpSend = document.querySelector('#b-otpSend');
const bLogin = document.querySelector("#b-login"); const bLogin = document.querySelector('#b-login');
const bLogout = document.querySelector("#b-logout"); const bLogout = document.querySelector('#b-logout');
const bSignin = document.querySelector("#b-signin"); const bSignin = document.querySelector('#b-signin');
const bWhoami = document.querySelector("#b-whoami"); const bWhoami = document.querySelector('#b-whoami');
const bOtpStatus = document.querySelector("#b-otpStatus"); const bOtpStatus = document.querySelector('#b-otpStatus');
const bOtpEnable = document.querySelector("#b-otpEnable"); const bOtpEnable = document.querySelector('#b-otpEnable');
const bOtpDisable = document.querySelector("#b-otpDisable"); const bOtpDisable = document.querySelector('#b-otpDisable');
const dResponse = document.querySelector("#d-response"); const dResponse = document.querySelector('#d-response');
function setResponse(obj) { function setResponse(obj) {
let obj_str = JSON.stringify(obj, null, 4); const obj_str = JSON.stringify(obj, null, 4);
dResponse.innerText = obj_str; dResponse.innerText = obj_str;
} }
let otpToken = null; let otpToken = null;
bOtpSend.addEventListener("click", async () => { bOtpSend.addEventListener('click', async () => {
let res = await fetch("/api/auth/otp", { method: "POST", body: JSON.stringify({ code: iOtp.value, token: otpToken }), headers }); const res = await fetch('/api/auth/otp', { method: 'POST', body: JSON.stringify({ code: iOtp.value, token: otpToken }), headers });
const json = await res.json(); const json = await res.json();
setResponse(json); setResponse(json);
if (json.kind === "success") { if (json.kind === 'success') {
if (json?.payload?.token) if (json?.payload?.token) {document.cookie = `token=${json?.payload?.token}`;}
document.cookie = `token=${json?.payload?.token}`;
} }
}); });
bOtpStatus.addEventListener("click", async () => { bOtpStatus.addEventListener('click', async () => {
let res = await fetch("/api/auth/statusOtp"); const res = await fetch('/api/auth/statusOtp');
const json = await res.json(); const json = await res.json();
setResponse(json); setResponse(json);
}); });
bOtpEnable.addEventListener("click", async () => { bOtpEnable.addEventListener('click', async () => {
let res = await fetch("/api/auth/enableOtp", { method: "PUT" }); const res = await fetch('/api/auth/enableOtp', { method: 'PUT' });
const json = await res.json(); const json = await res.json();
setResponse(json); setResponse(json);
}); });
bOtpDisable.addEventListener("click", async () => { bOtpDisable.addEventListener('click', async () => {
let res = await fetch("/api/auth/disableOtp", { method: "PUT" }); const res = await fetch('/api/auth/disableOtp', { method: 'PUT' });
const json = await res.json(); const json = await res.json();
setResponse(json); setResponse(json);
}); });
bWhoami.addEventListener("click", async () => { bWhoami.addEventListener('click', async () => {
let username = ""; let username = '';
try { try {
let res = await fetch("/api/auth/whoami"); const res = await fetch('/api/auth/whoami');
const json = await res.json(); const json = await res.json();
setResponse(json); setResponse(json);
if (json?.kind === "success") if (json?.kind === 'success') {username = json?.payload?.name;}
username = json?.payload?.name; else {username = `<not logged in:${json.msg}>`;}
else }
username = `<not logged in:${json.msg}>` catch {
} catch { username = '<not logged in: threw>';
username = `<not logged in: threw>`
} }
tUsername.innerText = username; tUsername.innerText = username;
}); });
bLogin.addEventListener("click", async () => { bLogin.addEventListener('click', async () => {
const name = iUsername.value; const name = iUsername.value;
const password = iPassword.value; const password = iPassword.value;
let res = await fetch("/api/auth/login", { method: "POST", body: JSON.stringify({ name, password }), headers }); const res = await fetch('/api/auth/login', { method: 'POST', body: JSON.stringify({ name, password }), headers });
let json = await res.json(); const json = await res.json();
if (json?.kind === "otpRequired") { if (json?.kind === 'otpRequired') {
otpToken = json?.payload?.token; otpToken = json?.payload?.token;
} else if (json?.kind === "success") { }
if (json?.payload?.token) else if (json?.kind === 'success') {
document.cookie = `token=${json?.payload?.token}`; if (json?.payload?.token) {document.cookie = `token=${json?.payload?.token}`;}
} }
setResponse(json); setResponse(json);
}) });
bLogout.addEventListener("click", async () => { bLogout.addEventListener('click', async () => {
let res = await fetch("/api/auth/logout", { method: "POST" }); const res = await fetch('/api/auth/logout', { method: 'POST' });
setResponse(await res.json()); setResponse(await res.json());
}) });
bSignin.addEventListener("click", async () => { bSignin.addEventListener('click', async () => {
const name = iUsername.value; const name = iUsername.value;
const password = iPassword.value; const password = iPassword.value;
let res = await fetch("/api/auth/signin", { method: "POST", body: JSON.stringify({ name, password }), headers }); const res = await fetch('/api/auth/signin', { method: 'POST', body: JSON.stringify({ name, password }), headers });
let json = await res.json(); const json = await res.json();
if (json?.payload?.token) if (json?.payload?.token) {document.cookie = `token=${json?.payload?.token};`;}
document.cookie = `token=${json?.payload?.token};`;
setResponse(json); setResponse(json);
}) });

View file

@ -1,10 +1,10 @@
import { FastifyPluginAsync } from 'fastify' import { FastifyPluginAsync } from 'fastify';
import fastifyFormBody from '@fastify/formbody' import fastifyFormBody from '@fastify/formbody';
import fastifyMultipart from '@fastify/multipart' import fastifyMultipart from '@fastify/multipart';
import { mkdir } from 'node:fs/promises' import { mkdir } from 'node:fs/promises';
import fp from 'fastify-plugin' import fp from 'fastify-plugin';
import * as db from '@shared/database' import * as db from '@shared/database';
import * as auth from '@shared/auth' import * as auth from '@shared/auth';
// @ts-ignore: import.meta.glob is a vite thing. Typescript doesn't know this... // @ts-ignore: import.meta.glob is a vite thing. Typescript doesn't know this...
const plugins = import.meta.glob('./plugins/**/*.ts', { eager: true }); const plugins = import.meta.glob('./plugins/**/*.ts', { eager: true });
@ -21,11 +21,11 @@ declare module 'fastify' {
const app: FastifyPluginAsync = async ( const app: FastifyPluginAsync = async (
fastify, fastify,
opts opts,
): Promise<void> => { ): Promise<void> => {
await fastify.register(db.useDatabase as any, {}) await fastify.register(db.useDatabase as any, {});
await fastify.register(auth.jwtPlugin as any, {}) await fastify.register(auth.jwtPlugin as any, {});
await fastify.register(auth.authPlugin as any, {}) await fastify.register(auth.authPlugin as any, {});
// Place here your custom code! // Place here your custom code!
for (const plugin of Object.values(plugins)) { for (const plugin of Object.values(plugins)) {
@ -35,9 +35,9 @@ const app: FastifyPluginAsync = async (
void fastify.register(route as any, {}); void fastify.register(route as any, {});
} }
void fastify.register(fastifyFormBody, {}) void fastify.register(fastifyFormBody, {});
void fastify.register(fastifyMultipart, {}) void fastify.register(fastifyMultipart, {});
} };
export default app export default app;
export { app } export { app };

View file

@ -1,5 +1,5 @@
import fp from 'fastify-plugin' import fp from 'fastify-plugin';
import sensible, { FastifySensibleOptions } from '@fastify/sensible' import sensible, { FastifySensibleOptions } from '@fastify/sensible';
/** /**
* This plugins adds some utilities to handle http errors * This plugins adds some utilities to handle http errors
@ -7,5 +7,5 @@ import sensible, { FastifySensibleOptions } from '@fastify/sensible'
* @see https://github.com/fastify/fastify-sensible * @see https://github.com/fastify/fastify-sensible
*/ */
export default fp<FastifySensibleOptions>(async (fastify) => { export default fp<FastifySensibleOptions>(async (fastify) => {
fastify.register(sensible) fastify.register(sensible);
}) });

View file

@ -1,25 +1,24 @@
import { FastifyPluginAsync } from "fastify"; import { FastifyPluginAsync } from 'fastify';
import { Static, Type } from "@sinclair/typebox"; import { Static, Type } from '@sinclair/typebox';
import { makeResponse, typeResponse, isNullish } from "@shared/utils" import { makeResponse, typeResponse, isNullish } from '@shared/utils';
export const WhoAmIRes = Type.Union([ export const WhoAmIRes = Type.Union([
typeResponse("success", "disableOtp.success"), typeResponse('success', 'disableOtp.success'),
typeResponse("failure", "disableOtp.failure.generic") typeResponse('failure', 'disableOtp.failure.generic'),
]); ]);
export type WhoAmIRes = Static<typeof WhoAmIRes>; export type WhoAmIRes = Static<typeof WhoAmIRes>;
const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => { const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => {
fastify.put( fastify.put(
"/api/auth/disableOtp", '/api/auth/disableOtp',
{ schema: { response: { "2xx": WhoAmIRes } }, config: { requireAuth: true } }, { schema: { response: { '2xx': WhoAmIRes } }, config: { requireAuth: true } },
async function(req, _res) { async function(req, _res) {
if (isNullish(req.authUser)) if (isNullish(req.authUser)) {return makeResponse('failure', 'disableOtp.failure.generic');}
return makeResponse("failure", "disableOtp.failure.generic");
this.db.deleteUserOtpSecret(req.authUser.id); this.db.deleteUserOtpSecret(req.authUser.id);
return makeResponse("success", "disableOtp.success"); return makeResponse('success', 'disableOtp.success');
}, },
); );
}; };

View file

@ -1,29 +1,27 @@
import { FastifyPluginAsync } from "fastify"; import { FastifyPluginAsync } from 'fastify';
import { Static, Type } from "@sinclair/typebox"; import { Static, Type } from '@sinclair/typebox';
import { isNullish, makeResponse, typeResponse } from "@shared/utils" import { isNullish, makeResponse, typeResponse } from '@shared/utils';
import { Otp } from "@shared/auth"; import { Otp } from '@shared/auth';
export const WhoAmIRes = Type.Union([ export const WhoAmIRes = Type.Union([
typeResponse("success", "enableOtp.success", { url: Type.String({ description: "The otp url to feed into a 2fa app" }) }), typeResponse('success', 'enableOtp.success', { url: Type.String({ description: 'The otp url to feed into a 2fa app' }) }),
typeResponse("failure", ["enableOtp.failure.noUser", "enableOtp.failure.noSecret"]) typeResponse('failure', ['enableOtp.failure.noUser', 'enableOtp.failure.noSecret']),
]); ]);
export type WhoAmIRes = Static<typeof WhoAmIRes>; export type WhoAmIRes = Static<typeof WhoAmIRes>;
const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => { const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => {
fastify.put( fastify.put(
"/api/auth/enableOtp", '/api/auth/enableOtp',
{ schema: { response: { "2xx": WhoAmIRes } }, config: { requireAuth: true } }, { schema: { response: { '2xx': WhoAmIRes } }, config: { requireAuth: true } },
async function(req, _res) { async function(req, _res) {
if (isNullish(req.authUser)) if (isNullish(req.authUser)) {return makeResponse('failure', 'enableOtp.failure.noUser');}
return makeResponse("failure", "enableOtp.failure.noUser"); const otpSecret = this.db.ensureUserOtpSecret(req.authUser!.id);
let otpSecret = this.db.ensureUserOtpSecret(req.authUser!.id); if (isNullish(otpSecret)) {return makeResponse('failure', 'enableOtp.failure.noSecret');}
if (isNullish(otpSecret)) const otp = new Otp({ secret: otpSecret });
return makeResponse("failure", "enableOtp.failure.noSecret"); return makeResponse('success', 'enableOtp.success', { url: otp.totpURL });
let otp = new Otp({ secret: otpSecret });
return makeResponse("success", "enableOtp.success", { url: otp.totpURL });
}, },
); );
}; };

View file

@ -1,8 +1,8 @@
import { FastifyPluginAsync } from "fastify"; import { FastifyPluginAsync } from 'fastify';
import { Static, Type } from "@sinclair/typebox"; import { Static, Type } from '@sinclair/typebox';
import { typeResponse, makeResponse, isNullish } from "@shared/utils" import { typeResponse, makeResponse, isNullish } from '@shared/utils';
import { verifyUserPassword } from "@shared/database/mixin/user"; import { verifyUserPassword } from '@shared/database/mixin/user';
export const LoginReq = Type.Object({ export const LoginReq = Type.Object({
name: Type.String(), name: Type.String(),
@ -12,9 +12,9 @@ export const LoginReq = Type.Object({
export type LoginReq = Static<typeof LoginReq>; export type LoginReq = Static<typeof LoginReq>;
export const LoginRes = Type.Union([ export const LoginRes = Type.Union([
typeResponse("failed", ["login.failed.generic", "login.failed.invalid"]), typeResponse('failed', ['login.failed.generic', 'login.failed.invalid']),
typeResponse("otpRequired", "login.otpRequired", { token: Type.String({ description: "JWT to send with the OTP to finish login" }) }), typeResponse('otpRequired', 'login.otpRequired', { token: Type.String({ description: 'JWT to send with the OTP to finish login' }) }),
typeResponse("success", "login.success", { token: Type.String({ description: "JWT that represent a logged in user" }) }), typeResponse('success', 'login.success', { token: Type.String({ description: 'JWT that represent a logged in user' }) }),
]); ]);
@ -22,34 +22,32 @@ export type LoginRes = Static<typeof LoginRes>;
const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => { const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => {
fastify.post<{ Body: LoginReq; Response: LoginRes }>( fastify.post<{ Body: LoginReq; Response: LoginRes }>(
"/api/auth/login", '/api/auth/login',
{ schema: { body: LoginReq, response: { "2xx": LoginRes } }, }, { schema: { body: LoginReq, response: { '2xx': LoginRes } } },
async function(req, _res) { async function(req, _res) {
try { try {
let { name, password } = req.body; const { name, password } = req.body;
let user = this.db.getUserFromName(name); const user = this.db.getUserFromName(name);
// does the user exist // does the user exist
// does it have a password setup ? // does it have a password setup ?
if (isNullish(user?.password)) if (isNullish(user?.password)) {return makeResponse('failed', 'login.failed.invalid');}
return makeResponse("failed", "login.failed.invalid");
// does the password he provided match the one we have // does the password he provided match the one we have
if (!(await verifyUserPassword(user, password))) if (!(await verifyUserPassword(user, password))) {return makeResponse('failed', 'login.failed.invalid');}
return makeResponse("failed", "login.failed.invalid");
// does the user has 2FA up ? // does the user has 2FA up ?
if (!isNullish(user.otp)) { if (!isNullish(user.otp)) {
// yes -> we ask them to fill it, // yes -> we ask them to fill it,
// send them somehting to verify that they indeed passed throught the user+password phase // send them somehting to verify that they indeed passed throught the user+password phase
return makeResponse("otpRequired", "login.otpRequired", { token: this.signJwt("otp", user.name) }); return makeResponse('otpRequired', 'login.otpRequired', { token: this.signJwt('otp', user.name) });
} }
// every check has been passed, they are now logged in, using this token to say who they are... // every check has been passed, they are now logged in, using this token to say who they are...
return makeResponse("success", "login.success", { token: this.signJwt("auth", user.name) }); return makeResponse('success', 'login.success', { token: this.signJwt('auth', user.name) });
} }
catch { catch {
return makeResponse("failed", "login.failed.generic"); return makeResponse('failed', 'login.failed.generic');
} }
}, },
); );

View file

@ -1,10 +1,10 @@
import { FastifyPluginAsync } from "fastify"; import { FastifyPluginAsync } from 'fastify';
const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => { const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => {
fastify.post( fastify.post(
"/api/auth/logout", '/api/auth/logout',
async function(_req, res) { async function(_req, res) {
return res.clearCookie("token").send("{}") return res.clearCookie('token').send('{}');
}, },
); );
}; };

View file

@ -1,19 +1,19 @@
import { FastifyPluginAsync } from "fastify"; import { FastifyPluginAsync } from 'fastify';
import { Static, Type } from "@sinclair/typebox"; import { Static, Type } from '@sinclair/typebox';
import { JwtType, Otp } from "@shared/auth"; import { JwtType, Otp } from '@shared/auth';
import { typeResponse, makeResponse, isNullish } from "@shared/utils"; import { typeResponse, makeResponse, isNullish } from '@shared/utils';
const OtpReq = Type.Object({ const OtpReq = Type.Object({
token: Type.String({ description: "The token given at the login phase" }), token: Type.String({ description: 'The token given at the login phase' }),
code: Type.String({ description: "The OTP given by the user" }), code: Type.String({ description: 'The OTP given by the user' }),
}); });
type OtpReq = Static<typeof OtpReq>; type OtpReq = Static<typeof OtpReq>;
const OtpRes = Type.Union([ const OtpRes = Type.Union([
typeResponse("failed", ["otp.failed.generic", "otp.failed.invalid", "otp.failed.timeout", "otp.failed.noSecret"]), typeResponse('failed', ['otp.failed.generic', 'otp.failed.invalid', 'otp.failed.timeout', 'otp.failed.noSecret']),
typeResponse("success", "otp.success", { token: Type.String({ description: "the JWT Token" }) }), typeResponse('success', 'otp.success', { token: Type.String({ description: 'the JWT Token' }) }),
]); ]);
type OtpRes = Static<typeof OtpRes>; type OtpRes = Static<typeof OtpRes>;
@ -22,34 +22,34 @@ const OTP_TOKEN_TIMEOUT_SEC = 120;
const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => { const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => {
fastify.post<{ Body: OtpReq }>( fastify.post<{ Body: OtpReq }>(
"/api/auth/otp", '/api/auth/otp',
{ schema: { body: OtpReq, response: { "2xx": OtpRes } } }, { schema: { body: OtpReq, response: { '2xx': OtpRes } } },
async function(req, _res) { async function(req, _res) {
try { try {
const { token, code } = req.body; const { token, code } = req.body;
// lets try to decode+verify the jwt // lets try to decode+verify the jwt
let dJwt = this.jwt.verify<JwtType>(token); const dJwt = this.jwt.verify<JwtType>(token);
// is the jwt a valid `otp` jwt ? // is the jwt a valid `otp` jwt ?
if (dJwt.kind != "otp") if (dJwt.kind != 'otp')
// no ? fuck off then // no ? fuck off then
return makeResponse("failed", "otp.failed.invalid"); {return makeResponse('failed', 'otp.failed.invalid');}
// is it too old ? // is it too old ?
if (dJwt.createdAt + OTP_TOKEN_TIMEOUT_SEC * 1000 < Date.now()) if (dJwt.createdAt + OTP_TOKEN_TIMEOUT_SEC * 1000 < Date.now())
// yes ? fuck off then, redo the password // yes ? fuck off then, redo the password
return makeResponse("failed", "otp.failed.timeout"); {return makeResponse('failed', 'otp.failed.timeout');}
// get the Otp sercret from the db // get the Otp sercret from the db
let user = this.db.getUserFromName(dJwt.who); const user = this.db.getUserFromName(dJwt.who);
if (isNullish(user?.otp)) if (isNullish(user?.otp))
// oops, either no user, or user without otpSecret // oops, either no user, or user without otpSecret
// fuck off // fuck off
return makeResponse("failed", "otp.failed.noSecret"); {return makeResponse('failed', 'otp.failed.noSecret');}
// good lets now verify the token you gave us is the correct one... // good lets now verify the token you gave us is the correct one...
let otpHandle = new Otp({ secret: user.otp }); const otpHandle = new Otp({ secret: user.otp });
let now = Date.now(); const now = Date.now();
const tokens = [ const tokens = [
// we also get the last code, to mitiage the delay between client<->server roundtrip... // we also get the last code, to mitiage the delay between client<->server roundtrip...
otpHandle.totp(now - 30 * 1000), otpHandle.totp(now - 30 * 1000),
@ -61,11 +61,12 @@ const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => {
if (tokens.some((c) => c === code)) if (tokens.some((c) => c === code))
// they do ! // they do !
// gg you are now logged in ! // gg you are now logged in !
return makeResponse("success", "otp.success", { token: this.signJwt("auth", dJwt.who) }); {return makeResponse('success', 'otp.success', { token: this.signJwt('auth', dJwt.who) });}
} catch {
return makeResponse("failed", "otp.failed.generic");
} }
return makeResponse("failed", "otp.failed.generic"); catch {
return makeResponse('failed', 'otp.failed.generic');
}
return makeResponse('failed', 'otp.failed.generic');
}, },
); );
}; };

View file

@ -1,7 +1,7 @@
import { FastifyPluginAsync } from "fastify"; import { FastifyPluginAsync } from 'fastify';
import { Static, Type } from "@sinclair/typebox"; import { Static, Type } from '@sinclair/typebox';
import { typeResponse, makeResponse, isNullish } from "@shared/utils"; import { typeResponse, makeResponse, isNullish } from '@shared/utils';
const USERNAME_CHECK: RegExp = /^[a-zA-Z\_0-9]+$/; const USERNAME_CHECK: RegExp = /^[a-zA-Z\_0-9]+$/;
@ -13,51 +13,44 @@ const SignInReq = Type.Object({
type SignInReq = Static<typeof SignInReq>; type SignInReq = Static<typeof SignInReq>;
const SignInRes = Type.Union([ const SignInRes = Type.Union([
typeResponse("failed", [ typeResponse('failed', [
"signin.failed.generic", 'signin.failed.generic',
"signin.failed.username.existing", 'signin.failed.username.existing',
"signin.failed.username.toolong", 'signin.failed.username.toolong',
"signin.failed.username.tooshort", 'signin.failed.username.tooshort',
"signin.failed.username.invalid", 'signin.failed.username.invalid',
"signin.failed.password.toolong", 'signin.failed.password.toolong',
"signin.failed.password.tooshort", 'signin.failed.password.tooshort',
"signin.failed.password.invalid", 'signin.failed.password.invalid',
]), ]),
typeResponse("success", "signin.success", { token: Type.String({ description: "the JWT token" }) }), typeResponse('success', 'signin.success', { token: Type.String({ description: 'the JWT token' }) }),
]) ]);
type SignInRes = Static<typeof SignInRes>; type SignInRes = Static<typeof SignInRes>;
const route: FastifyPluginAsync = async (fastify, opts): Promise<void> => { const route: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
fastify.post<{ Body: SignInReq }>( fastify.post<{ Body: SignInReq }>(
"/api/auth/signin", '/api/auth/signin',
{ schema: { body: SignInReq, response: { "200": SignInRes, "5xx": Type.Object({}) } }, }, { schema: { body: SignInReq, response: { '200': SignInRes, '5xx': Type.Object({}) } } },
async function(req, res) { async function(req, res) {
const { name, password } = req.body; const { name, password } = req.body;
if (name.length < 4) if (name.length < 4) {return makeResponse('failed', 'signin.failed.username.tooshort');}
return makeResponse("failed", "signin.failed.username.tooshort"); if (name.length > 32) {return makeResponse('failed', 'signin.failed.username.toolong');}
if (name.length > 32) if (!USERNAME_CHECK.test(name)) {return makeResponse('failed', 'signin.failed.username.invalid');}
return makeResponse("failed", "signin.failed.username.toolong");
if (!USERNAME_CHECK.test(name))
return makeResponse("failed", "signin.failed.username.invalid");
// username if good now :) // username if good now :)
if (password.length < 8) if (password.length < 8) {return makeResponse('failed', 'signin.failed.password.tooshort');}
return makeResponse("failed", "signin.failed.password.tooshort"); if (password.length > 64) {return makeResponse('failed', 'signin.failed.password.toolong');}
if (password.length > 64)
return makeResponse("failed", "signin.failed.password.toolong");
// password is good too ! // password is good too !
if (this.db.getUserFromName(name) !== undefined) if (this.db.getUserFromName(name) !== undefined) {return makeResponse('failed', 'signin.failed.username.existing');}
return makeResponse("failed", "signin.failed.username.existing"); const u = await this.db.createUser(name, password);
let u = await this.db.createUser(name, password); if (isNullish(u)) {return makeResponse('failed', 'signin.failed.generic');}
if (isNullish(u))
return makeResponse("failed", "signin.failed.generic");
// every check has been passed, they are now logged in, using this token to say who they are... // every check has been passed, they are now logged in, using this token to say who they are...
let userToken = this.signJwt('auth', u.name); const userToken = this.signJwt('auth', u.name);
return makeResponse("success", "signin.success", { token: userToken }); return makeResponse('success', 'signin.success', { token: userToken });
}, },
); );
}; };

View file

@ -1,30 +1,28 @@
import { FastifyPluginAsync } from "fastify"; import { FastifyPluginAsync } from 'fastify';
import { Static, Type } from "@sinclair/typebox"; import { Static, Type } from '@sinclair/typebox';
import { isNullish, makeResponse, typeResponse } from "@shared/utils" import { isNullish, makeResponse, typeResponse } from '@shared/utils';
import { Otp } from "@shared/auth"; import { Otp } from '@shared/auth';
export const StatusOtpRes = Type.Union([ export const StatusOtpRes = Type.Union([
typeResponse("success", "statusOtp.success.enabled", { url: Type.String({ description: "The otp url to feed into a 2fa app" }) }), typeResponse('success', 'statusOtp.success.enabled', { url: Type.String({ description: 'The otp url to feed into a 2fa app' }) }),
typeResponse("success", "statusOtp.success.disabled"), typeResponse('success', 'statusOtp.success.disabled'),
typeResponse("failure", "statusOtp.failure.generic") typeResponse('failure', 'statusOtp.failure.generic'),
]); ]);
export type StatusOtpRes = Static<typeof StatusOtpRes>; export type StatusOtpRes = Static<typeof StatusOtpRes>;
const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => { const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => {
fastify.get( fastify.get(
"/api/auth/statusOtp", '/api/auth/statusOtp',
{ schema: { response: { "2xx": StatusOtpRes } }, config: { requireAuth: true } }, { schema: { response: { '2xx': StatusOtpRes } }, config: { requireAuth: true } },
async function(req, _res) { async function(req, _res) {
if (isNullish(req.authUser)) if (isNullish(req.authUser)) {return makeResponse('failure', 'statusOtp.failure.generic');}
return makeResponse("failure", "statusOtp.failure.generic"); const otpSecret = this.db.getUserOtpSecret(req.authUser.id);
let otpSecret = this.db.getUserOtpSecret(req.authUser.id); if (isNullish(otpSecret)) {return makeResponse('success', 'statusOtp.success.disabled');}
if (isNullish(otpSecret)) const otp = new Otp({ secret: otpSecret });
return makeResponse("success", "statusOtp.success.disabled"); return makeResponse('success', 'statusOtp.success.enabled', { url: otp.totpURL });
let otp = new Otp({ secret: otpSecret })
return makeResponse("success", "statusOtp.success.enabled", { url: otp.totpURL });
}, },
); );
}; };

View file

@ -1,24 +1,23 @@
import { FastifyPluginAsync } from "fastify"; import { FastifyPluginAsync } from 'fastify';
import { Static, Type } from "@sinclair/typebox"; import { Static, Type } from '@sinclair/typebox';
import { isNullish, makeResponse, typeResponse } from "@shared/utils" import { isNullish, makeResponse, typeResponse } from '@shared/utils';
export const WhoAmIRes = Type.Union([ export const WhoAmIRes = Type.Union([
typeResponse("success", "whoami.success", { name: Type.String() }), typeResponse('success', 'whoami.success', { name: Type.String() }),
typeResponse("failure", "whoami.failure.generic") typeResponse('failure', 'whoami.failure.generic'),
]); ]);
export type WhoAmIRes = Static<typeof WhoAmIRes>; export type WhoAmIRes = Static<typeof WhoAmIRes>;
const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => { const route: FastifyPluginAsync = async (fastify, _opts): Promise<void> => {
fastify.get( fastify.get(
"/api/auth/whoami", '/api/auth/whoami',
{ schema: { response: { "2xx": WhoAmIRes } }, config: { requireAuth: true } }, { schema: { response: { '2xx': WhoAmIRes } }, config: { requireAuth: true } },
async function(req, _res) { async function(req, _res) {
if (isNullish(req.authUser)) if (isNullish(req.authUser)) {return makeResponse('failure', 'whoami.failure.generic');}
return makeResponse("failure", "whoami.failure.generic") return makeResponse('success', 'whoami.success', { name: req.authUser.name });
return makeResponse("success", "whoami.success", { name: req.authUser.name })
}, },
); );
}; };

View file

@ -1,7 +1,7 @@
// this sould only be used by the docker file ! // this sould only be used by the docker file !
import fastify, { FastifyInstance } from "fastify"; import fastify, { FastifyInstance } from 'fastify';
import app from "./app" import app from './app';
const start = async () => { const start = async () => {
const envToLogger = { const envToLogger = {
@ -16,15 +16,16 @@ const start = async () => {
}, },
production: true, production: true,
test: false, test: false,
} };
const f: FastifyInstance = fastify({ logger: envToLogger.development }); const f: FastifyInstance = fastify({ logger: envToLogger.development });
try { try {
await f.register(app, {}); await f.register(app, {});
await f.listen({ port: 80, host: '0.0.0.0' }) await f.listen({ port: 80, host: '0.0.0.0' });
} catch (err) {
f.log.error(err)
process.exit(1)
} }
} catch (err) {
start() f.log.error(err);
process.exit(1);
}
};
start();

View file

@ -1,8 +1,8 @@
import { defineConfig } from 'vite' import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths' import tsconfigPaths from 'vite-tsconfig-paths';
import nodeExternals from 'rollup-plugin-node-externals' import nodeExternals from 'rollup-plugin-node-externals';
import path from 'node:path' import path from 'node:path';
import fs from 'node:fs' import fs from 'node:fs';
function collectDeps(...pkgJsonPaths) { function collectDeps(...pkgJsonPaths) {
const allDeps = new Set(); const allDeps = new Set();
@ -20,7 +20,7 @@ function collectDeps(...pkgJsonPaths) {
const externals = collectDeps( const externals = collectDeps(
'./package.json', './package.json',
'../@shared/package.json' '../@shared/package.json',
); );
@ -42,5 +42,5 @@ export default defineConfig({
target: 'node22', // or whatever Node version you use target: 'node22', // or whatever Node version you use
sourcemap: true, sourcemap: true,
minify: false, // for easier debugging minify: false, // for easier debugging
} },
}) });