added Date + Email Body Type

This commit is contained in:
Flam3rboy 2021-02-01 21:46:52 +01:00
parent d0506036da
commit 7158521911
2 changed files with 79 additions and 21 deletions

6
src/util/String.ts Normal file
View File

@ -0,0 +1,6 @@
export const WHITE_SPACE = /\s\s+/g;
export const SPECIAL_CHAR = /[@#`:\r\n\t\f\v\p{C}]/gu;
export function trim(str: string) {
return str.replace(SPECIAL_CHAR, "").replace(WHITE_SPACE, " ").trim();
}

View File

@ -1,14 +1,17 @@
// different version of lambert-server instanceOf with discord error format // different version of lambert-server instanceOf with discord error format
import { NextFunction, Request, Response } from "express"; import { NextFunction, Request, Response } from "express";
import { TFunction } from "i18next";
import { Tuple } from "lambert-server"; import { Tuple } from "lambert-server";
import "missing-native-js-functions";
const OPTIONAL_PREFIX = "$"; export const OPTIONAL_PREFIX = "$";
export const EMAIL_REGEX = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
export function check(schema: any) { export function check(schema: any) {
return (req: Request, res: Response, next: NextFunction) => { return (req: Request, res: Response, next: NextFunction) => {
try { try {
const result = instanceOf(schema, req.body, { path: "body" }); const result = instanceOf(schema, req.body, { path: "body", t: req.t, ref: { obj: null, key: "" } });
if (result === true) return next(); if (result === true) return next();
throw result; throw result;
} catch (error) { } catch (error) {
@ -17,66 +20,113 @@ export function check(schema: any) {
}; };
} }
class FieldError extends Error { export function FieldErrors(fields: Record<string, { code?: string; message: string }>) {
constructor(public code: string, public message: string) { return new FieldError(
50035,
"Invalid Form Body",
fields.map(({ message, code }) => ({
_errors: [
{
message,
code: code || "BASE_TYPE_INVALID",
},
],
}))
);
}
export class FieldError extends Error {
constructor(public code: string | number, public message: string, public errors?: any) {
super(message); super(message);
} }
} }
export class Email {
constructor(public email: string) {}
check() {
return !!this.email.match(EMAIL_REGEX);
}
}
export function instanceOf( export function instanceOf(
type: any, type: any,
value: any, value: any,
{ path = "", optional = false, errors = {} }: { path?: string; optional?: boolean; errors?: any } = {} {
path = "",
optional = false,
errors = {},
t,
ref,
}: { path?: string; optional?: boolean; errors?: any; t: TFunction; ref: { key: string | number; obj: any } }
): Boolean { ): Boolean {
try { try {
if (!type) return true; // no type was specified if (!type) return true; // no type was specified
if (value == null) { if (value == null) {
if (optional) return true; if (optional) return true;
throw new FieldError("BASE_TYPE_REQUIRED", `This field is required`); throw new FieldError("BASE_TYPE_REQUIRED", t("common:field.BASE_TYPE_REQUIRED"));
} }
switch (type) { switch (type) {
case String: case String:
if (typeof value === "string") return true; if (typeof value === "string") return true;
throw new FieldError("BASE_TYPE_STRING", `This field must be a string`); throw new FieldError("BASE_TYPE_STRING", t("common:field.BASE_TYPE_STRING"));
case Number: case Number:
value = Number(value); value = Number(value);
ref.obj[ref.key] = value;
if (typeof value === "number" && !isNaN(value)) return true; if (typeof value === "number" && !isNaN(value)) return true;
throw new FieldError("BASE_TYPE_NUMBER", `This field must be a number`); throw new FieldError("BASE_TYPE_NUMBER", t("common:field.BASE_TYPE_NUMBER"));
case BigInt: case BigInt:
try { try {
value = BigInt(value); value = BigInt(value);
ref.obj[ref.key] = value;
if (typeof value === "bigint") return true; if (typeof value === "bigint") return true;
} catch (error) {} } catch (error) {}
throw new FieldError("BASE_TYPE_BIGINT", `This field must be a bigint`); throw new FieldError("BASE_TYPE_BIGINT", t("common:field.BASE_TYPE_BIGINT"));
case Boolean: case Boolean:
if (value == "true") value = true; if (value == "true") value = true;
if (value == "false") value = false; if (value == "false") value = false;
ref.obj[ref.key] = value;
if (typeof value === "boolean") return true; if (typeof value === "boolean") return true;
throw new FieldError("BASE_TYPE_BOOLEAN", `This field must be a boolean`); throw new FieldError("BASE_TYPE_BOOLEAN", t("common:field.BASE_TYPE_BOOLEAN"));
case Tuple:
if ((<Tuple>type).types.some((x) => instanceOf(x, value, { path, optional, errors, t, ref })))
return true;
throw new FieldError("BASE_TYPE_CHOICES", t("common:field.BASE_TYPE_CHOICES", { types: type.types }));
case Email:
if ((<Email>type).check()) return true;
throw new FieldError("EMAIL_TYPE_INVALID_EMAIL", t("common:field.EMAIL_TYPE_INVALID_EMAIL"));
case Date:
value = new Date(value);
ref.obj[ref.key] = value;
// value.getTime() can be < 0, if it is before 1970
if (!isNaN(value)) return true;
throw new FieldError("DATE_TYPE_PARSE", t("common:field.DATE_TYPE_PARSE"));
} }
if (typeof type === "object") { if (typeof type === "object") {
if (type?.constructor?.name != "Object") { if (type?.constructor?.name != "Object") {
if (type instanceof Tuple) {
if ((<Tuple>type).types.some((x) => instanceOf(x, value, { path, optional, errors }))) return true;
throw new FieldError("BASE_TYPE_CHOICES", `This field must be one of (${type.types})`);
}
if (value instanceof type) return true; if (value instanceof type) return true;
throw new FieldError("BASE_TYPE_CLASS", `This field must be an instance of ${type}`); throw new FieldError("BASE_TYPE_CLASS", t("common:field.BASE_TYPE_CLASS", { type }));
} }
if (typeof value !== "object") throw new FieldError("BASE_TYPE_OBJECT", `This field must be a object`); if (typeof value !== "object") throw new FieldError("BASE_TYPE_OBJECT", t("common:field.BASE_TYPE_OBJECT"));
if (Array.isArray(type)) { if (Array.isArray(type)) {
if (!Array.isArray(value)) throw new FieldError("BASE_TYPE_ARRAY", `This field must be an array`); if (!Array.isArray(value)) throw new FieldError("BASE_TYPE_ARRAY", t("common:field.BASE_TYPE_ARRAY"));
if (!type.length) return true; // type array didn't specify any type if (!type.length) return true; // type array didn't specify any type
return ( return (
value.every((val, i) => { value.every((val, i) => {
errors[i] = {}; errors[i] = {};
return ( return (
instanceOf(type[0], val, { path: `${path}[${i}]`, optional, errors: errors[i] }) === true instanceOf(type[0], val, {
path: `${path}[${i}]`,
optional,
errors: errors[i],
t,
ref: { key: i, obj: value },
}) === true
); );
}) || errors }) || errors
); );
@ -86,7 +136,7 @@ export function instanceOf(
Object.keys(type).map((x) => (x.startsWith(OPTIONAL_PREFIX) ? x.slice(OPTIONAL_PREFIX.length) : x)) Object.keys(type).map((x) => (x.startsWith(OPTIONAL_PREFIX) ? x.slice(OPTIONAL_PREFIX.length) : x))
); );
if (diff.length) throw new FieldError("UNKOWN_FIELD", `Unkown key ${diff}`); if (diff.length) throw new FieldError("UNKOWN_FIELD", t("common:field.UNKOWN_FIELD", { key: diff }));
return ( return (
Object.keys(type).every((key) => { Object.keys(type).every((key) => {
@ -100,16 +150,18 @@ export function instanceOf(
path: `${path}.${newKey}`, path: `${path}.${newKey}`,
optional: OPTIONAL, optional: OPTIONAL,
errors: errors[key], errors: errors[key],
t,
ref: { key: newKey, obj: value },
}) === true }) === true
); );
}) || errors }) || errors
); );
} else if (typeof type === "number" || typeof type === "string" || typeof type === "boolean") { } else if (typeof type === "number" || typeof type === "string" || typeof type === "boolean") {
if (value === type) return true; if (value === type) return true;
throw new FieldError("BASE_TYPE_CONSTANT", `This field must be ${value}`); throw new FieldError("BASE_TYPE_CONSTANT", t("common:field.BASE_TYPE_CONSTANT", { value: type }));
} else if (typeof type === "bigint") { } else if (typeof type === "bigint") {
if (BigInt(value) === type) return true; if (BigInt(value) === type) return true;
throw new FieldError("BASE_TYPE_CONSTANT", `This field must be ${value}`); throw new FieldError("BASE_TYPE_CONSTANT", t("common:field.BASE_TYPE_CONSTANT", { value: type }));
} }
return type == value; return type == value;