diff --git a/util/src/models/BaseClass.ts b/util/src/models/BaseClass.ts
index 78cd329c..d4f635f6 100644
--- a/util/src/models/BaseClass.ts
+++ b/util/src/models/BaseClass.ts
@@ -1,22 +1,24 @@
import "reflect-metadata";
-import { BaseEntity, Column } from "typeorm";
+import { BaseEntity, BeforeInsert, BeforeUpdate, Column, PrimaryGeneratedColumn } from "typeorm";
+import { Snowflake } from "../util/Snowflake";
+import { IsString, validateOrReject } from "class-validator";
export class BaseClass extends BaseEntity {
+ @PrimaryGeneratedColumn()
@Column()
- id?: string;
+ @IsString()
+ id: string;
- constructor(props?: any) {
+ constructor(props?: any, opts: { id?: string } = {}) {
super();
- BaseClass.assign(props, this, "body.");
+ this.id = opts.id || Snowflake.generate();
+ Object.defineProperties(this, props);
}
- private static assign(props: any, object: any, path?: string): any {
- const expectedType = Reflect.getMetadata("design:type", object, props);
- console.log(expectedType, object, props, path, typeof object);
-
- if (typeof object !== typeof props) throw new Error(`Property at ${path} must be`);
- if (typeof object === "object")
- return Object.keys(object).map((key) => BaseClass.assign(props[key], object[key], `${path}.${key}`));
+ @BeforeUpdate()
+ @BeforeInsert()
+ async validate() {
+ await validateOrReject(this, {});
}
}
diff --git a/util/src/models/User.ts b/util/src/models/User.ts
index 38045738..27aa63d1 100644
--- a/util/src/models/User.ts
+++ b/util/src/models/User.ts
@@ -2,6 +2,7 @@ import { Column, PrimaryColumn, PrimaryGeneratedColumn } from "typeorm";
import { Activity } from "./Activity";
import { BaseClass } from "./BaseClass";
import { ClientStatus, Status } from "./Status";
+import { validateOrReject, IsInt, IsEmail, IsPhoneNumber, IsBoolean, IsString, ValidateNested } from "class-validator";
export const PublicUserProjection = {
username: true,
@@ -16,67 +17,80 @@ export const PublicUserProjection = {
};
export class User extends BaseClass {
- @PrimaryGeneratedColumn()
- id: string;
-
@Column()
+ @IsString()
username: string; // username max length 32, min 2 (should be configurable)
@Column()
+ @IsInt()
discriminator: string; // #0001 4 digit long string from #0001 - #9999
@Column()
+ @IsString()
avatar: string | null; // hash of the user avatar
@Column()
+ @IsInt()
accent_color: number | null; // banner color of user
@Column()
banner: string | null; // hash of the user banner
@Column()
+ @IsPhoneNumber()
phone: string | null; // phone number of the user
@Column()
+ @IsBoolean()
desktop: boolean; // if the user has desktop app installed
@Column()
+ @IsBoolean()
mobile: boolean; // if the user has mobile app installed
@Column()
+ @IsBoolean()
premium: boolean; // if user bought nitro
@Column()
premium_type: number; // nitro level
@Column()
+ @IsBoolean()
bot: boolean; // if user is bot
@Column()
bio: string; // short description of the user (max 190 chars -> should be configurable)
@Column()
+ @IsBoolean()
system: boolean; // shouldn't be used, the api sents this field type true, if the generated message comes from a system generated author
@Column()
+ @IsBoolean()
nsfw_allowed: boolean; // if the user is older than 18 (resp. Config)
@Column()
+ @IsBoolean()
mfa_enabled: boolean; // if multi factor authentication is enabled
@Column()
created_at: Date; // registration date
@Column()
+ @IsBoolean()
verified: boolean; // if the user is offically verified
@Column()
+ @IsBoolean()
disabled: boolean; // if the account is disabled
@Column()
+ @IsBoolean()
deleted: boolean; // if the user was deleted
@Column()
+ @IsEmail()
email: string | null; // email of the user
@Column()
@@ -86,15 +100,19 @@ export class User extends BaseClass {
public_flags: bigint;
@Column("simple-array") // string in simple-array must not contain commas
+ @IsString({ each: true })
guilds: string[]; // array of guild ids the user is part of
@Column("simple-json")
- user_settings: UserSettings;
-
- @Column("simple-json")
- user_data: UserData;
+ @ValidateNested() // TODO: https://github.com/typestack/class-validator#validating-nested-objects
+ user_data: {
+ valid_tokens_since: Date; // all tokens with a previous issue date are invalid
+ hash: string; // hash of the password, salt is saved in password (bcrypt)
+ fingerprints: string[]; // array of fingerprints -> used to prevent multiple accounts
+ };
@Column("simple-json")
+ @ValidateNested() // TODO: https://github.com/typestack/class-validator#validating-nested-objects
presence: {
status: Status;
activities: Activity[];
@@ -102,22 +120,76 @@ export class User extends BaseClass {
};
@Column("simple-json")
- relationships: Relationship[];
+ @ValidateNested() // TODO: https://github.com/typestack/class-validator#validating-nested-objects
+ relationships: {
+ id: string;
+ nickname?: string;
+ type: RelationshipType;
+ }[];
@Column("simple-json")
- connected_accounts: ConnectedAccount[];
-}
-
-// @ts-ignore
-global.User = User;
+ @ValidateNested() // TODO: https://github.com/typestack/class-validator#validating-nested-objects
+ connected_accounts: {
+ access_token: string;
+ friend_sync: boolean;
+ id: string;
+ name: string;
+ revoked: boolean;
+ show_activity: boolean;
+ type: string;
+ verifie: boolean;
+ visibility: number;
+ }[];
-// Private user data that should never get sent to the client
-export interface UserData {
- valid_tokens_since: Date; // all tokens with a previous issue date are invalid
- hash: string; // hash of the password, salt is saved in password (bcrypt)
- fingerprints: string[]; // array of fingerprints -> used to prevent multiple accounts
+ @Column("simple-json")
+ @ValidateNested() // TODO: https://github.com/typestack/class-validator#validating-nested-objects
+ user_settings: {
+ afk_timeout: number;
+ allow_accessibility_detection: boolean;
+ animate_emoji: boolean;
+ animate_stickers: number;
+ contact_sync_enabled: boolean;
+ convert_emoticons: boolean;
+ custom_status: {
+ emoji_id: string | null;
+ emoji_name: string | null;
+ expires_at: number | null;
+ text: string | null;
+ };
+ default_guilds_restricted: boolean;
+ detect_platform_accounts: boolean;
+ developer_mode: boolean;
+ disable_games_tab: boolean;
+ enable_tts_command: boolean;
+ explicit_content_filter: number;
+ friend_source_flags: { all: boolean };
+ gateway_connected: boolean;
+ gif_auto_play: boolean;
+ guild_folders: // every top guild is displayed as a "folder"
+ {
+ color: number;
+ guild_ids: string[];
+ id: number;
+ name: string;
+ }[];
+ guild_positions: string[]; // guild ids ordered by position
+ inline_attachment_media: boolean;
+ inline_embed_media: boolean;
+ locale: string; // en_US
+ message_display_compact: boolean;
+ native_phone_integration_enabled: boolean;
+ render_embeds: boolean;
+ render_reactions: boolean;
+ restricted_guilds: string[];
+ show_current_game: boolean;
+ status: "online" | "offline" | "dnd" | "idle";
+ stream_notifications_enabled: boolean;
+ theme: "dark" | "white"; // dark
+ timezone_offset: number; // e.g -60
+ };
}
+// Private user data that should never get sent to the client
export interface PublicUser {
id: string;
discriminator: string;
@@ -129,72 +201,9 @@ export interface PublicUser {
bot: boolean;
}
-export interface ConnectedAccount {
- access_token: string;
- friend_sync: boolean;
- id: string;
- name: string;
- revoked: boolean;
- show_activity: boolean;
- type: string;
- verifie: boolean;
- visibility: number;
-}
-
-export interface Relationship {
- id: string;
- nickname?: string;
- type: RelationshipType;
-}
-
export enum RelationshipType {
outgoing = 4,
incoming = 3,
blocked = 2,
friends = 1,
}
-
-export interface UserSettings {
- afk_timeout: number;
- allow_accessibility_detection: boolean;
- animate_emoji: boolean;
- animate_stickers: number;
- contact_sync_enabled: boolean;
- convert_emoticons: boolean;
- custom_status: {
- emoji_id: string | null;
- emoji_name: string | null;
- expires_at: number | null;
- text: string | null;
- };
- default_guilds_restricted: boolean;
- detect_platform_accounts: boolean;
- developer_mode: boolean;
- disable_games_tab: boolean;
- enable_tts_command: boolean;
- explicit_content_filter: number;
- friend_source_flags: { all: boolean };
- gateway_connected: boolean;
- gif_auto_play: boolean;
- guild_folders: // every top guild is displayed as a "folder"
- {
- color: number;
- guild_ids: string[];
- id: number;
- name: string;
- }[];
- guild_positions: string[]; // guild ids ordered by position
- inline_attachment_media: boolean;
- inline_embed_media: boolean;
- locale: string; // en_US
- message_display_compact: boolean;
- native_phone_integration_enabled: boolean;
- render_embeds: boolean;
- render_reactions: boolean;
- restricted_guilds: string[];
- show_current_game: boolean;
- status: "online" | "offline" | "dnd" | "idle";
- stream_notifications_enabled: boolean;
- theme: "dark" | "white"; // dark
- timezone_offset: number; // e.g -60
-}
|