diff --git a/util/src/entities/User.ts b/src/util/entities/User.ts
index 9b1c494e..1237b676 100644
--- a/util/src/entities/User.ts
+++ b/src/util/entities/User.ts
@@ -1,10 +1,11 @@
-import { Column, Entity, FindOneOptions, JoinColumn, ManyToMany, OneToMany, RelationId } from "typeorm";
-import { BaseClass } from "./BaseClass";
+import { Column, Entity, FindOneOptions, FindOptionsSelectByString, JoinColumn, OneToMany, OneToOne } from "typeorm";
+import { Member, Session, UserSettings } from ".";
+import { Config, FieldErrors, Snowflake, trimSpecial } from "..";
import { BitField } from "../util/BitField";
-import { Relationship } from "./Relationship";
+import { OrmUtils } from "../util/imports/OrmUtils";
+import { BaseClass } from "./BaseClass";
import { ConnectedAccount } from "./ConnectedAccount";
-import { Config, FieldErrors, Snowflake, trimSpecial } from "..";
-import { Member, Session } from ".";
+import { Relationship } from "./Relationship";
export enum PublicUserEnum {
username,
@@ -16,7 +17,7 @@ export enum PublicUserEnum {
banner,
bio,
bot,
- premium_since,
+ premium_since
}
export type PublicUserKeys = keyof typeof PublicUserEnum;
@@ -30,17 +31,15 @@ export enum PrivateUserEnum {
premium,
premium_type,
disabled,
- settings,
+ settings
// locale
}
export type PrivateUserKeys = keyof typeof PrivateUserEnum | PublicUserKeys;
-export const PublicUserProjection = Object.values(PublicUserEnum).filter(
- (x) => typeof x === "string"
-) as PublicUserKeys[];
+export const PublicUserProjection = Object.values(PublicUserEnum).filter((x) => typeof x === "string") as PublicUserKeys[];
export const PrivateUserProjection = [
...PublicUserProjection,
- ...Object.values(PrivateUserEnum).filter((x) => typeof x === "string"),
+ ...Object.values(PrivateUserEnum).filter((x) => typeof x === "string")
] as PrivateUserKeys[];
// Private user data that should never get sent to the client
@@ -82,58 +81,64 @@ export class User extends BaseClass {
phone?: string; // phone number of the user
@Column({ select: false })
- desktop: boolean; // if the user has desktop app installed
+ desktop: boolean = false; // if the user has desktop app installed
@Column({ select: false })
- mobile: boolean; // if the user has mobile app installed
+ mobile: boolean = false; // if the user has mobile app installed
@Column()
- premium: boolean; // if user bought individual premium
-
- @Column()
- premium_type: number; // individual premium level
+ premium: boolean = Config.get().defaults.user.premium; // if user bought individual premium
@Column()
- bot: boolean; // if user is bot
+ premium_type: number = Config.get().defaults.user.premium_type; // individual premium level
@Column()
+ bot: boolean = false; // if user is bot
+
+ @Column({ nullable: true })
bio: string; // short description of the user (max 190 chars -> should be configurable)
@Column()
- system: boolean; // shouldn't be used, the api sends this field type true, if the generated message comes from a system generated author
+ system: boolean = false; // shouldn't be used, the api sends this field type true, if the generated message comes from a system generated author
@Column({ select: false })
- nsfw_allowed: boolean; // if the user can do age-restricted actions (NSFW channels/guilds/commands)
-
- @Column({ select: false })
+ nsfw_allowed: boolean = true; // if the user can do age-restricted actions (NSFW channels/guilds/commands) // TODO: depending on age
+
+ @Column({ select: false, nullable: true })
mfa_enabled: boolean; // if multi factor authentication is enabled
+ @Column({ select: false, nullable: true })
+ totp_secret?: string;
+
+ @Column({ nullable: true, select: false })
+ totp_last_ticket?: string;
+
@Column()
- created_at: Date; // registration date
+ created_at: Date = new Date(); // registration date
@Column({ nullable: true })
- premium_since: Date; // premium date
+ premium_since: Date = new Date(); // premium date
@Column({ select: false })
- verified: boolean; // if the user is offically verified
+ verified: boolean = Config.get().defaults.user.verified; // if the user is offically verified
@Column()
- disabled: boolean; // if the account is disabled
+ disabled: boolean = false; // if the account is disabled
@Column()
- deleted: boolean; // if the user was deleted
+ deleted: boolean = false; // if the user was deleted
@Column({ nullable: true, select: false })
email?: string; // email of the user
@Column()
- flags: string; // UserFlags
+ flags: string = "0"; // UserFlags // TODO: generate
@Column()
- public_flags: number;
+ public_flags: number = 0;
@Column({ type: "bigint" })
- rights: string; // Rights
+ rights: string = Config.get().register.defaultRights; // Rights
@OneToMany(() => Session, (session: Session) => session.user)
sessions: Session[];
@@ -141,14 +146,14 @@ export class User extends BaseClass {
@JoinColumn({ name: "relationship_ids" })
@OneToMany(() => Relationship, (relationship: Relationship) => relationship.from, {
cascade: true,
- orphanedRowAction: "delete",
+ orphanedRowAction: "delete"
})
relationships: Relationship[];
@JoinColumn({ name: "connected_account_ids" })
@OneToMany(() => ConnectedAccount, (account: ConnectedAccount) => account.user, {
cascade: true,
- orphanedRowAction: "delete",
+ orphanedRowAction: "delete"
})
connected_accounts: ConnectedAccount[];
@@ -159,17 +164,29 @@ export class User extends BaseClass {
};
@Column({ type: "simple-array", select: false })
- fingerprints: string[]; // array of fingerprints -> used to prevent multiple accounts
+ fingerprints: string[] = []; // array of fingerprints -> used to prevent multiple accounts
- @Column({ type: "simple-json", select: false })
+ @OneToOne(() => UserSettings, {
+ cascade: true,
+ orphanedRowAction: "delete",
+ eager: false
+ })
+ @JoinColumn()
settings: UserSettings;
-
+
// workaround to prevent fossord-unaware clients from deleting settings not used by them
@Column({ type: "simple-json", select: false })
- extended_settings: string;
+ extended_settings: string = "{}";
@Column({ type: "simple-json" })
- notes: { [key: string]: string }; //key is ID of user
+ notes: { [key: string]: string } = {}; //key is ID of user
+
+ async save(): Promise<any> {
+ if (!this.settings) this.settings = new UserSettings();
+ this.settings.id = this.id;
+ //await this.settings.save();
+ return super.save();
+ }
toPublicUser() {
const user: any = {};
@@ -180,19 +197,17 @@ export class User extends BaseClass {
}
static async getPublicUser(user_id: string, opts?: FindOneOptions<User>) {
- return await User.findOneOrFail(
- { id: user_id },
- {
- ...opts,
- select: [...PublicUserProjection, ...(opts?.select || [])],
- }
- );
+ return await User.findOneOrFail({
+ where: { id: user_id },
+ select: [...PublicUserProjection, ...((opts?.select as FindOptionsSelectByString<User>) || [])],
+ ...opts
+ });
}
- private static async generateDiscriminator(username: string): Promise<string | undefined> {
+ public static async generateDiscriminator(username: string): Promise<string | undefined> {
if (Config.get().register.incrementingDiscriminators) {
// discriminator will be incrementally generated
-
+
// First we need to figure out the currently highest discrimnator for the given username and then increment it
const users = await User.find({ where: { username }, select: ["discriminator"] });
const highestDiscriminator = Math.max(0, ...users.map((u) => Number(u.discriminator)));
@@ -223,7 +238,7 @@ export class User extends BaseClass {
username,
password,
date_of_birth,
- req,
+ req
}: {
username: string;
password?: string;
@@ -240,48 +255,30 @@ export class User extends BaseClass {
throw FieldErrors({
username: {
code: "USERNAME_TOO_MANY_USERS",
- message: req.t("auth:register.USERNAME_TOO_MANY_USERS"),
- },
+ message: req?.t("auth:register.USERNAME_TOO_MANY_USERS")
+ }
});
}
// TODO: save date_of_birth
// appearently discord doesn't save the date of birth and just calculate if nsfw is allowed
// if nsfw_allowed is null/undefined it'll require date_of_birth to set it to true/false
- const language = req.language === "en" ? "en-US" : req.language || "en-US";
+ const language = req?.language === "en" ? "en-US" : req?.language || "en-US";
- const user = new User({
- created_at: new Date(),
+ const user = OrmUtils.mergeDeep(new User(), {
+ //required:
username: username,
discriminator,
id: Snowflake.generate(),
- bot: false,
- system: false,
- premium_since: new Date(),
- desktop: false,
- mobile: false,
- premium: true,
- premium_type: 2,
- bio: "",
- mfa_enabled: false,
- verified: true,
- disabled: false,
- deleted: false,
email: email,
- rights: "0", // TODO: grant rights correctly, as 0 actually stands for no rights at all
- nsfw_allowed: true, // TODO: depending on age
- public_flags: "0",
- flags: "0", // TODO: generate
data: {
hash: password,
- valid_tokens_since: new Date(),
+ valid_tokens_since: new Date()
},
- settings: { ...defaultSettings, locale: language },
- extended_settings: {},
- fingerprints: [],
- notes: {},
+ settings: { ...new UserSettings(), locale: language }
});
+ //await (user.settings as UserSettings).save();
await user.save();
setImmediate(async () => {
@@ -296,85 +293,6 @@ export class User extends BaseClass {
}
}
-export const defaultSettings: UserSettings = {
- afk_timeout: 3600,
- allow_accessibility_detection: true,
- animate_emoji: true,
- animate_stickers: 0,
- contact_sync_enabled: false,
- convert_emoticons: false,
- custom_status: null,
- default_guilds_restricted: false,
- detect_platform_accounts: false,
- developer_mode: true,
- disable_games_tab: true,
- enable_tts_command: false,
- explicit_content_filter: 0,
- friend_source_flags: { all: true },
- gateway_connected: false,
- gif_auto_play: true,
- guild_folders: [],
- guild_positions: [],
- inline_attachment_media: true,
- inline_embed_media: true,
- locale: "en-US",
- message_display_compact: true,
- native_phone_integration_enabled: true,
- render_embeds: true,
- render_reactions: true,
- restricted_guilds: [],
- show_current_game: true,
- status: "online",
- stream_notifications_enabled: false,
- theme: "dark",
- timezone_offset: 0, // TODO: timezone from request
-};
-
-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;
- emoji_name?: string;
- expires_at?: number;
- 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;
- // every top guild is displayed as a "folder"
- guild_folders: {
- 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" | "invisible";
- stream_notifications_enabled: boolean;
- theme: "dark" | "white"; // dark
- timezone_offset: number; // e.g -60
-}
-
export const CUSTOM_USER_FLAG_OFFSET = BigInt(1) << BigInt(32);
export class UserFlags extends BitField {
@@ -398,6 +316,6 @@ export class UserFlags extends BitField {
VERIFIED_BOT: BigInt(1) << BigInt(16),
EARLY_VERIFIED_BOT_DEVELOPER: BigInt(1) << BigInt(17),
CERTIFIED_MODERATOR: BigInt(1) << BigInt(18),
- BOT_HTTP_INTERACTIONS: BigInt(1) << BigInt(19),
+ BOT_HTTP_INTERACTIONS: BigInt(1) << BigInt(19)
};
}
|