Bug fixes and app settings

This commit is contained in:
Alice Gaudon 2020-06-27 17:11:31 +02:00
parent 10257b8294
commit b043513fe8
9 changed files with 51 additions and 30 deletions

View File

@ -1,4 +1,8 @@
export default { export default {
app: {
name: 'Example App',
contact_email: 'noreply@example.net'
},
log_level: "DEV", log_level: "DEV",
db_log_level: "ERROR", db_log_level: "ERROR",
public_url: "http://localhost:4899", public_url: "http://localhost:4899",

View File

@ -1,6 +1,6 @@
{ {
"name": "wms-core", "name": "wms-core",
"version": "0.10.2", "version": "0.10.16",
"description": "Node web framework", "description": "Node web framework",
"repository": "git@gitlab.com:ArisuOngaku/wms-core.git", "repository": "git@gitlab.com:ArisuOngaku/wms-core.git",
"author": "Alice Gaudon <alice@gaudon.pro>", "author": "Alice Gaudon <alice@gaudon.pro>",

View File

@ -11,6 +11,7 @@ import {Type} from "./Utils";
import LogRequestsComponent from "./components/LogRequestsComponent"; import LogRequestsComponent from "./components/LogRequestsComponent";
import {ValidationBag} from "./db/Validator"; import {ValidationBag} from "./db/Validator";
import TemplateError = lib.TemplateError; import TemplateError = lib.TemplateError;
import config from "config";
export default abstract class Application { export default abstract class Application {
private readonly version: string; private readonly version: string;
@ -42,7 +43,7 @@ export default abstract class Application {
} }
public async start(): Promise<void> { public async start(): Promise<void> {
Logger.info(`${this.constructor.name} v${this.version} - hi`); Logger.info(`${config.get('app.name')} v${this.version} - hi`);
process.once('SIGINT', () => { process.once('SIGINT', () => {
this.stop().catch(console.error); this.stop().catch(console.error);
}); });

View File

@ -11,13 +11,33 @@ import geoip from "geoip-lite";
export default abstract class MagicLinkAuthController extends Controller { export default abstract class MagicLinkAuthController extends Controller {
public static async checkAndAuth(req: Request, magicLink: MagicLink): Promise<void> { public static async checkAndAuth(req: Request, res: Response, magicLink: MagicLink): Promise<void> {
if (magicLink.getSessionID() !== req.sessionID!) throw new BadOwnerMagicLink(); if (magicLink.getSessionID() !== req.sessionID!) throw new BadOwnerMagicLink();
if (!await magicLink.isAuthorized()) throw new UnauthorizedMagicLink(); if (!await magicLink.isAuthorized()) throw new UnauthorizedMagicLink();
if (!await magicLink.isValid()) throw new InvalidMagicLink(); if (!await magicLink.isValid()) throw new InvalidMagicLink();
// Auth // Auth
try {
await req.authGuard.authenticateOrRegister(req.session!, magicLink); await req.authGuard.authenticateOrRegister(req.session!, magicLink);
} catch (e) {
if (e instanceof PendingApprovalAuthError) {
res.format({
json: () => {
res.json({
'status': 'warning',
'message': `Your account is pending review. You'll receive an email once you're approved.`
});
},
html: () => {
req.flash('warning', `Your account is pending review. You'll receive an email once you're approved.`);
res.redirectBack('/');
}
});
return;
} else {
throw e;
}
}
} }
protected readonly loginMagicLinkActionType: string = 'Login'; protected readonly loginMagicLinkActionType: string = 'Login';
@ -114,27 +134,7 @@ export default abstract class MagicLinkAuthController extends Controller {
return; return;
} }
try { await MagicLinkAuthController.checkAndAuth(req, res, magicLink);
await MagicLinkAuthController.checkAndAuth(req, magicLink);
} catch (e) {
if (e instanceof PendingApprovalAuthError) {
res.format({
json: () => {
res.json({
'status': 'warning',
'message': `Your account is pending review. You'll receive an email once you're approved.`
});
},
default: () => {
req.flash('warning', `Your account is pending review. You'll receive an email once you're approved.`);
res.redirectBack('/');
}
});
return;
} else {
throw e;
}
}
// Auth success // Auth success
const username = req.models.user?.name; const username = req.models.user?.name;

View File

@ -12,8 +12,8 @@ export default class User extends Model {
} }
public name?: string; public name?: string;
public approved: boolean = false; public approved!: boolean;
public is_admin: boolean = false; public is_admin!: boolean;
public created_at?: Date; public created_at?: Date;
public updated_at?: Date; public updated_at?: Date;
@ -24,6 +24,12 @@ export default class User extends Model {
public readonly mainEmail = this.emails.cloneReduceToOne().constraint(q => q.where('main', true)); public readonly mainEmail = this.emails.cloneReduceToOne().constraint(q => q.where('main', true));
constructor(data: any) {
super(data);
if (this.approved === undefined) this.approved = false;
if (this.is_admin === undefined) this.is_admin = false;
}
protected init(): void { protected init(): void {
this.addProperty<string>('name', new Validator().acceptUndefined().between(3, 64)); this.addProperty<string>('name', new Validator().acceptUndefined().between(3, 64));
if (User.isApprovalMode()) this.addProperty<boolean>('approved', new Validator().defined()); if (User.isApprovalMode()) this.addProperty<boolean>('approved', new Validator().defined());

View File

@ -29,6 +29,9 @@ export default class NunjucksComponent extends ApplicationComponent<void> {
req.env = env; req.env = env;
res.locals.url = req.url; res.locals.url = req.url;
res.locals.params = () => req.params; res.locals.params = () => req.params;
res.locals.app = config.get('app');
next(); next();
}); });
} }

View File

@ -224,7 +224,7 @@ export default abstract class Model {
for (const indexField of this.getPrimaryKeyFields()) { for (const indexField of this.getPrimaryKeyFields()) {
query = query.where(indexField, this[indexField]); query = query.where(indexField, this[indexField]);
} }
return typeof (await query.first()) !== 'undefined'; return (await query.limit(1).execute()).results.length > 0;
} }
public async delete(): Promise<void> { public async delete(): Promise<void> {

View File

@ -190,9 +190,11 @@ export default class ModelQuery<M extends Model> {
// Eager loading execute // Eager loading execute
for (const relationName of this.relations) { for (const relationName of this.relations) {
const relations = relationMap[relationName]; const relations = relationMap[relationName];
if (relations.length > 0) {
const allModels = await relations[0].eagerLoad(relations); const allModels = await relations[0].eagerLoad(relations);
await Promise.all(relations.map(r => r.populate(allModels))); await Promise.all(relations.map(r => r.populate(allModels)));
} }
}
return models; return models;
} }

View File

@ -31,6 +31,11 @@ export default abstract class ModelRelation<S extends Model, O extends Model, R
return this.cachedModels; return this.cachedModels;
} }
public getOrFail(): R {
if (!this.cachedModels) throw new Error('Models were not fetched');
return this.cachedModels;
}
public abstract async eagerLoad(relations: ModelRelation<S, O, R>[]): Promise<ModelQueryResult<O>>; public abstract async eagerLoad(relations: ModelRelation<S, O, R>[]): Promise<ModelQueryResult<O>>;
public abstract async populate(models: ModelQueryResult<O>): Promise<void>; public abstract async populate(models: ModelQueryResult<O>): Promise<void>;