From b86032a001b45d3f345820b84ba4489981116919 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Tue, 24 Aug 2021 16:34:46 +0200 Subject: :sparkles: typeorm entities --- util/src/entities/index.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 util/src/entities/index.ts (limited to 'util/src/entities/index.ts') diff --git a/util/src/entities/index.ts b/util/src/entities/index.ts new file mode 100644 index 00000000..9cb10016 --- /dev/null +++ b/util/src/entities/index.ts @@ -0,0 +1,21 @@ +export * from "./Application"; +export * from "./AuditLog"; +export * from "./Ban"; +export * from "./BaseClass"; +export * from "./Channel"; +export * from "./ConnectedAccount"; +export * from "./Emoji"; +export * from "./Guild"; +export * from "./Invite"; +export * from "./Member"; +export * from "./Message"; +export * from "./RateLimit"; +export * from "./ReadState"; +export * from "./Relationship"; +export * from "./Role"; +export * from "./Team"; +export * from "./TeamMember"; +export * from "./Template"; +export * from "./User"; +export * from "./VoiceState"; +export * from "./Webhook"; -- cgit 1.5.1 From db3f5c0d1b5556e16c37704467b3599a6230be02 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Thu, 26 Aug 2021 20:48:09 +0200 Subject: :sparkles: use RelationId --- util/src/entities/Application.ts | 6 +- util/src/entities/AuditLog.ts | 6 +- util/src/entities/Ban.ts | 8 +- util/src/entities/Channel.ts | 12 +- util/src/entities/Config.ts | 274 ++++++++++++++++++++++++++++++++++++++ util/src/entities/Emoji.ts | 4 +- util/src/entities/Guild.ts | 29 ++-- util/src/entities/Invite.ts | 12 +- util/src/entities/Member.ts | 6 +- util/src/entities/Message.ts | 29 ++-- util/src/entities/RateLimit.ts | 4 +- util/src/entities/ReadState.ts | 9 +- util/src/entities/Relationship.ts | 4 +- util/src/entities/Role.ts | 4 +- util/src/entities/Team.ts | 6 +- util/src/entities/TeamMember.ts | 6 +- util/src/entities/Template.ts | 8 +- util/src/entities/User.ts | 41 +++++- util/src/entities/VoiceState.ts | 11 +- util/src/entities/Webhook.ts | 46 +++++-- util/src/entities/index.ts | 1 + 21 files changed, 444 insertions(+), 82 deletions(-) create mode 100644 util/src/entities/Config.ts (limited to 'util/src/entities/index.ts') diff --git a/util/src/entities/Application.ts b/util/src/entities/Application.ts index 64b5d2e2..90d0f056 100644 --- a/util/src/entities/Application.ts +++ b/util/src/entities/Application.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Team } from "./Team"; @@ -38,14 +38,14 @@ export class Application extends BaseClass { @Column() verify_key: string; - @Column() + @RelationId((application: Application) => application.team) team_id: string; @JoinColumn({ name: "team_id" }) @ManyToOne(() => Team, (team: Team) => team.id) team?: Team; - @Column() + @RelationId((application: Application) => application.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) diff --git a/util/src/entities/AuditLog.ts b/util/src/entities/AuditLog.ts index 53e99261..2195771b 100644 --- a/util/src/entities/AuditLog.ts +++ b/util/src/entities/AuditLog.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { ChannelPermissionOverwrite } from "./Channel"; import { User } from "./User"; @@ -43,14 +43,14 @@ export enum AuditLogEvents { @Entity("audit_logs") export class AuditLogEntry extends BaseClass { - @Column() + @RelationId((auditlog: AuditLogEntry) => auditlog.target) target_id: string; @JoinColumn({ name: "user_id" }) @ManyToOne(() => User, (user: User) => user.id) target?: User; - @Column() + @RelationId((auditlog: AuditLogEntry) => auditlog.user) user_id: string; @JoinColumn({ name: "user_id" }) diff --git a/util/src/entities/Ban.ts b/util/src/entities/Ban.ts index ceea4a05..2553e886 100644 --- a/util/src/entities/Ban.ts +++ b/util/src/entities/Ban.ts @@ -1,25 +1,25 @@ -import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { User } from "./User"; @Entity("bans") export class Ban extends BaseClass { - @Column() + @RelationId((ban: Ban) => ban.user) user_id: string; @JoinColumn({ name: "user_id" }) @ManyToOne(() => User, (user: User) => user.id) user: User; - @Column() + @RelationId((ban: Ban) => ban.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) @ManyToOne(() => Guild, (guild: Guild) => guild.id) guild: Guild; - @Column() + @RelationId((ban: Ban) => ban.executor) executor_id: string; @JoinColumn({ name: "executor_id" }) diff --git a/util/src/entities/Channel.ts b/util/src/entities/Channel.ts index 5845607a..d26f1054 100644 --- a/util/src/entities/Channel.ts +++ b/util/src/entities/Channel.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToMany, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Message } from "./Message"; @@ -25,35 +25,35 @@ export class Channel extends BaseClass { @Column({ type: "simple-enum", enum: ChannelType }) type: ChannelType; - @Column("simple-array") + @RelationId((channel: Channel) => channel.recipients) recipient_ids: string[]; @JoinColumn({ name: "recipient_ids" }) @ManyToMany(() => User, (user: User) => user.id) recipients?: User[]; - @Column() + @RelationId((channel: Channel) => channel.last_message) last_message_id: string; @JoinColumn({ name: "last_message_id" }) @ManyToOne(() => Message, (message: Message) => message.id) last_message?: Message; - @Column() + @RelationId((channel: Channel) => channel.guild) guild_id?: string; @JoinColumn({ name: "guild_id" }) @ManyToOne(() => Guild, (guild: Guild) => guild.id) guild: Guild; - @Column() + @RelationId((channel: Channel) => channel.parent) parent_id: string; @JoinColumn({ name: "parent_id" }) @ManyToOne(() => Channel, (channel: Channel) => channel.id) parent?: Channel; - @Column() + @RelationId((channel: Channel) => channel.owner) owner_id: string; @JoinColumn({ name: "owner_id" }) diff --git a/util/src/entities/Config.ts b/util/src/entities/Config.ts new file mode 100644 index 00000000..60d84958 --- /dev/null +++ b/util/src/entities/Config.ts @@ -0,0 +1,274 @@ +import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Snowflake } from "../util"; +import { BaseClass } from "./BaseClass"; +import crypto from "crypto"; + +@Entity("config") +export class ConfigEntity extends BaseClass { + @Column("simple-json") + value: ConfigValue; +} + +export interface RateLimitOptions { + bot?: number; + count: number; + window: number; + onyIp?: boolean; +} + +export interface Region { + id: string; + name: string; + vip: boolean; + custom: boolean; + deprecated: boolean; + optimal: boolean; +} + +export interface KafkaBroker { + ip: string; + port: number; +} + +export interface ConfigValue { + gateway: { + endpointClient: string | null; + endpoint: string | null; + }; + cdn: { + endpointClient: string | null; + endpoint: string | null; + }; + general: { + instance_id: string; + }; + permissions: { + user: { + createGuilds: boolean; + }; + }; + limits: { + user: { + maxGuilds: number; + maxUsername: number; + maxFriends: number; + }; + guild: { + maxRoles: number; + maxMembers: number; + maxChannels: number; + maxChannelsInCategory: number; + hideOfflineMember: number; + }; + message: { + maxCharacters: number; + maxTTSCharacters: number; + maxReactions: number; + maxAttachmentSize: number; + maxBulkDelete: number; + }; + channel: { + maxPins: number; + maxTopic: number; + }; + rate: { + ip: Omit; + global: RateLimitOptions; + error: RateLimitOptions; + routes: { + guild: RateLimitOptions; + webhook: RateLimitOptions; + channel: RateLimitOptions; + auth: { + login: RateLimitOptions; + register: RateLimitOptions; + }; + // TODO: rate limit configuration for all routes + }; + }; + }; + security: { + autoUpdate: boolean | number; + requestSignature: string; + jwtSecret: string; + forwadedFor: string | null; // header to get the real user ip address + captcha: { + enabled: boolean; + service: "recaptcha" | "hcaptcha" | null; // TODO: hcaptcha, custom + sitekey: string | null; + secret: string | null; + }; + ipdataApiKey: string | null; + }; + login: { + requireCaptcha: boolean; + }; + register: { + email: { + necessary: boolean; // we have to use necessary instead of required as the cli tool uses json schema and can't use required + allowlist: boolean; + blocklist: boolean; + domains: string[]; + }; + dateOfBirth: { + necessary: boolean; + minimum: number; // in years + }; + requireCaptcha: boolean; + requireInvite: boolean; + allowNewRegistration: boolean; + allowMultipleAccounts: boolean; + blockProxies: boolean; + password: { + minLength: number; + minNumbers: number; + minUpperCase: number; + minSymbols: number; + }; + }; + regions: { + default: string; + available: Region[]; + }; + rabbitmq: { + host: string | null; + }; + kafka: { + brokers: KafkaBroker[] | null; + }; +} + +export const DefaultConfigOptions: ConfigValue = { + gateway: { + endpointClient: null, + endpoint: null, + }, + cdn: { + endpointClient: null, + endpoint: null, + }, + general: { + instance_id: Snowflake.generate(), + }, + permissions: { + user: { + createGuilds: true, + }, + }, + limits: { + user: { + maxGuilds: 100, + maxUsername: 32, + maxFriends: 1000, + }, + guild: { + maxRoles: 250, + maxMembers: 250000, + maxChannels: 500, + maxChannelsInCategory: 50, + hideOfflineMember: 1000, + }, + message: { + maxCharacters: 2000, + maxTTSCharacters: 200, + maxReactions: 20, + maxAttachmentSize: 8388608, + maxBulkDelete: 100, + }, + channel: { + maxPins: 50, + maxTopic: 1024, + }, + rate: { + ip: { + count: 500, + window: 5, + }, + global: { + count: 20, + window: 5, + bot: 250, + }, + error: { + count: 10, + window: 5, + }, + routes: { + guild: { + count: 5, + window: 5, + }, + webhook: { + count: 5, + window: 20, + }, + channel: { + count: 5, + window: 20, + }, + auth: { + login: { + count: 5, + window: 60, + }, + register: { + count: 2, + window: 60 * 60 * 12, + }, + }, + }, + }, + }, + security: { + autoUpdate: true, + requestSignature: crypto.randomBytes(32).toString("base64"), + jwtSecret: crypto.randomBytes(256).toString("base64"), + forwadedFor: null, + // forwadedFor: "X-Forwarded-For" // nginx/reverse proxy + // forwadedFor: "CF-Connecting-IP" // cloudflare: + captcha: { + enabled: false, + service: null, + sitekey: null, + secret: null, + }, + ipdataApiKey: "eca677b284b3bac29eb72f5e496aa9047f26543605efe99ff2ce35c9", + }, + login: { + requireCaptcha: false, + }, + register: { + email: { + necessary: true, + allowlist: false, + blocklist: true, + domains: [], // TODO: efficiently save domain blocklist in database + // domains: fs.readFileSync(__dirname + "/blockedEmailDomains.txt", { encoding: "utf8" }).split("\n"), + }, + dateOfBirth: { + necessary: true, + minimum: 13, + }, + requireInvite: false, + requireCaptcha: true, + allowNewRegistration: true, + allowMultipleAccounts: true, + blockProxies: true, + password: { + minLength: 8, + minNumbers: 2, + minUpperCase: 2, + minSymbols: 0, + }, + }, + regions: { + default: "fosscord", + available: [{ id: "fosscord", name: "Fosscord", vip: false, custom: false, deprecated: false, optimal: false }], + }, + rabbitmq: { + host: null, + }, + kafka: { + brokers: null, + }, +}; diff --git a/util/src/entities/Emoji.ts b/util/src/entities/Emoji.ts index 366549db..b31ddb3b 100644 --- a/util/src/entities/Emoji.ts +++ b/util/src/entities/Emoji.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToMany, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Role } from "./Role"; @@ -30,7 +30,7 @@ export class Emoji extends BaseClass { @Column() url: string; - @Column("simple-array") + @RelationId((emoji: Emoji) => emoji.roles) role_ids: string[]; @JoinColumn({ name: "role_ids" }) diff --git a/util/src/entities/Guild.ts b/util/src/entities/Guild.ts index d46d31bc..d7b4dff4 100644 --- a/util/src/entities/Guild.ts +++ b/util/src/entities/Guild.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, OneToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, OneToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; import { Emoji } from "./Emoji"; @@ -10,7 +10,7 @@ import { VoiceState } from "./VoiceState"; @Entity("guilds") export class Guild extends BaseClass { - @Column() + @RelationId((guild: Guild) => guild.afk_channel) afk_channel_id?: string; @JoinColumn({ name: "afk_channel_id" }) @@ -64,35 +64,35 @@ export class Guild extends BaseClass { @Column() presence_count?: number; // users online - @Column("simple-array") + @RelationId((guild: Guild) => guild.members) member_ids: string[]; @JoinColumn({ name: "member_ids" }) @ManyToMany(() => Member, (member: Member) => member.id) members: Member[]; - @Column("simple-array") + @RelationId((guild: Guild) => guild.roles) role_ids: string[]; @JoinColumn({ name: "role_ids" }) @ManyToMany(() => Role, (role: Role) => role.id) roles: Role[]; - @Column("simple-array") + @RelationId((guild: Guild) => guild.channels) channel_ids: string[]; @JoinColumn({ name: "channel_ids" }) @ManyToMany(() => Channel, (channel: Channel) => channel.id) channels: Channel[]; - @Column("simple-array") + @RelationId((guild: Guild) => guild.emojis) emoji_ids: string[]; @JoinColumn({ name: "emoji_ids" }) @ManyToMany(() => Emoji, (emoji: Emoji) => emoji.id) emojis: Emoji[]; - @Column("simple-array") + @RelationId((guild: Guild) => guild.voice_states) voice_state_ids: string[]; @JoinColumn({ name: "voice_state_ids" }) @@ -105,7 +105,7 @@ export class Guild extends BaseClass { @Column() name: string; - @Column() + @RelationId((guild: Guild) => guild.owner) owner_id: string; @JoinColumn({ name: "owner_id" }) @@ -121,11 +121,14 @@ export class Guild extends BaseClass { @Column() premium_tier?: number; // nitro boost level + @RelationId((guild: Guild) => guild.public_updates_channel) + public_updates_channel_id: string; + @JoinColumn({ name: "public_updates_channel_id" }) @ManyToOne(() => Channel, (channel: Channel) => channel.id) public_updates_channel?: Channel; - @Column() + @RelationId((guild: Guild) => guild.rules_channel) rules_channel_id?: string; @JoinColumn({ name: "rules_channel_id" }) @@ -138,7 +141,7 @@ export class Guild extends BaseClass { @Column() splash?: string; - @Column() + @RelationId((guild: Guild) => guild.system_channel) system_channel_id?: string; @JoinColumn({ name: "system_channel_id" }) @@ -151,6 +154,9 @@ export class Guild extends BaseClass { @Column() unavailable?: boolean; + @RelationId((guild: Guild) => guild.vanity_url) + vanity_url_code?: string; + @JoinColumn({ name: "vanity_url_code" }) @OneToOne(() => Invite, (invite: Invite) => invite.code) vanity_url?: Invite; @@ -170,6 +176,9 @@ export class Guild extends BaseClass { }[]; }; + @RelationId((guild: Guild) => guild.widget_channel) + widget_channel_id?: string; + @JoinColumn({ name: "widget_channel_id" }) @ManyToOne(() => Channel, (channel: Channel) => channel.id) widget_channel?: Channel; diff --git a/util/src/entities/Invite.ts b/util/src/entities/Invite.ts index 19f7206a..eac0cba0 100644 --- a/util/src/entities/Invite.ts +++ b/util/src/entities/Invite.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; import { Guild } from "./Guild"; @@ -27,29 +27,29 @@ export class Invite extends BaseClass { @Column() expires_at: Date; - @Column() + @RelationId((invite: Invite) => invite.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) @ManyToOne(() => Guild, (guild: Guild) => guild.id) guild: Guild; - @Column() + @RelationId((invite: Invite) => invite.channel) channel_id: string; @JoinColumn({ name: "channel_id" }) @ManyToOne(() => Channel, (channel: Channel) => channel.id) channel: Channel; - @Column() + @RelationId((invite: Invite) => invite.inviter) inviter_id: string; @JoinColumn({ name: "inviter_id" }) @ManyToOne(() => User, (user: User) => user.id) inviter: User; - @Column() - target_usser_id: string; + @RelationId((invite: Invite) => invite.target_user) + target_user_id: string; @JoinColumn({ name: "target_user_id" }) @ManyToOne(() => User, (user: User) => user.id) diff --git a/util/src/entities/Member.ts b/util/src/entities/Member.ts index c367755e..01634d9e 100644 --- a/util/src/entities/Member.ts +++ b/util/src/entities/Member.ts @@ -1,18 +1,18 @@ import { PublicUser, User } from "./User"; import { BaseClass } from "./BaseClass"; -import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { Guild } from "./Guild"; @Entity("members") export class Member extends BaseClass { - @Column() + @RelationId((member: Member) => member.user) user_id: string; @JoinColumn({ name: "user_id" }) @ManyToOne(() => User, (user: User) => user.id) user: User; - @Column() + @RelationId((member: Member) => member.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) diff --git a/util/src/entities/Message.ts b/util/src/entities/Message.ts index 2c0918c7..9daf042c 100644 --- a/util/src/entities/Message.ts +++ b/util/src/entities/Message.ts @@ -4,7 +4,16 @@ import { Role } from "./Role"; import { Channel } from "./Channel"; import { InteractionType } from "../interfaces/Interaction"; import { Application } from "./Application"; -import { Column, CreateDateColumn, Entity, JoinColumn, ManyToMany, ManyToOne, UpdateDateColumn } from "typeorm"; +import { + Column, + CreateDateColumn, + Entity, + JoinColumn, + ManyToMany, + ManyToOne, + RelationId, + UpdateDateColumn, +} from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Webhook } from "./Webhook"; @@ -34,42 +43,42 @@ export class Message extends BaseClass { @Column() id: string; - @Column() + @RelationId((message: Message) => message.channel) channel_id: string; @JoinColumn({ name: "channel_id" }) @ManyToOne(() => Channel, (channel: Channel) => channel.id) channel: Channel; - @Column() + @RelationId((message: Message) => message.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) @ManyToOne(() => Guild, (guild: Guild) => guild.id) guild?: Guild; - @Column() + @RelationId((message: Message) => message.author) author_id: string; @JoinColumn({ name: "author_id" }) @ManyToOne(() => User, (user: User) => user.id) author?: User; - @Column() + @RelationId((message: Message) => message.member) member_id: string; @JoinColumn({ name: "member_id" }) @ManyToOne(() => Member, (member: Member) => member.id) member?: Member; - @Column() + @RelationId((message: Message) => message.webhook) webhook_id: string; @JoinColumn({ name: "webhook_id" }) @ManyToOne(() => Webhook, (webhook: Webhook) => webhook.id) webhook?: Webhook; - @Column() + @RelationId((message: Message) => message.application) application_id: string; @JoinColumn({ name: "application_id" }) @@ -93,21 +102,21 @@ export class Message extends BaseClass { @Column() mention_everyone?: boolean; - @Column("simple-array") + @RelationId((message: Message) => message.mention_users) mention_user_ids: string[]; @JoinColumn({ name: "mention_user_ids" }) @ManyToMany(() => User, (user: User) => user.id) mention_users: User[]; - @Column("simple-array") + @RelationId((message: Message) => message.mention_roles) mention_role_ids: string[]; @JoinColumn({ name: "mention_role_ids" }) @ManyToMany(() => Role, (role: Role) => role.id) mention_roles: Role[]; - @Column("simple-array") + @RelationId((message: Message) => message.mention_channels) mention_channel_ids: string[]; @JoinColumn({ name: "mention_channel_ids" }) diff --git a/util/src/entities/RateLimit.ts b/util/src/entities/RateLimit.ts index 374a0759..3ac35df3 100644 --- a/util/src/entities/RateLimit.ts +++ b/util/src/entities/RateLimit.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { User } from "./User"; @@ -7,7 +7,7 @@ export class RateLimit extends BaseClass { @Column() id: "global" | "error" | string; // channel_239842397 | guild_238927349823 | webhook_238923423498 - @Column() + @RelationId((rate_limit: RateLimit) => rate_limit.user) user_id: string; @JoinColumn({ name: "user_id" }) diff --git a/util/src/entities/ReadState.ts b/util/src/entities/ReadState.ts index 7c56b6c6..0310cb5f 100644 --- a/util/src/entities/ReadState.ts +++ b/util/src/entities/ReadState.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; import { Message } from "./Message"; @@ -6,20 +6,23 @@ import { User } from "./User"; @Entity("read_states") export class ReadState extends BaseClass { - @Column() + @RelationId((read_state: ReadState) => read_state.channel) channel_id: string; @JoinColumn({ name: "channel_id" }) @ManyToOne(() => Channel, (channel: Channel) => channel.id) channel: Channel; - @Column() + @RelationId((read_state: ReadState) => read_state.user) user_id: string; @JoinColumn({ name: "user_id" }) @ManyToOne(() => User, (user: User) => user.id) user: User; + @RelationId((read_state: ReadState) => read_state.last_message) + last_message_id: string; + @JoinColumn({ name: "last_message_id" }) @ManyToOne(() => Message, (message: Message) => message.id) last_message?: Message; diff --git a/util/src/entities/Relationship.ts b/util/src/entities/Relationship.ts index bd5861f0..3e1280c7 100644 --- a/util/src/entities/Relationship.ts +++ b/util/src/entities/Relationship.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { User } from "./User"; @@ -11,7 +11,7 @@ export enum RelationshipType { @Entity("relationships") export class Relationship extends BaseClass { - @Column() + @RelationId((relationship: Relationship) => relationship.user) user_id: string; @JoinColumn({ name: "user_id" }) diff --git a/util/src/entities/Role.ts b/util/src/entities/Role.ts index 7bb144cc..e48fd293 100644 --- a/util/src/entities/Role.ts +++ b/util/src/entities/Role.ts @@ -1,10 +1,10 @@ -import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; @Entity("roles") export class Role extends BaseClass { - @Column() + @RelationId((role: Role) => role.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) diff --git a/util/src/entities/Team.ts b/util/src/entities/Team.ts index 5e645650..fa1b0ed2 100644 --- a/util/src/entities/Team.ts +++ b/util/src/entities/Team.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToMany, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { TeamMember } from "./TeamMember"; import { User } from "./User"; @@ -8,7 +8,7 @@ export class Team extends BaseClass { @Column() icon?: string; - @Column("simple-array") + @RelationId((team: Team) => team.members) member_ids: string[]; @JoinColumn({ name: "member_ids" }) @@ -18,7 +18,7 @@ export class Team extends BaseClass { @Column() name: string; - @Column() + @RelationId((team: Team) => team.owner_user) owner_user_id: string; @JoinColumn({ name: "owner_user_id" }) diff --git a/util/src/entities/TeamMember.ts b/util/src/entities/TeamMember.ts index 2b1c76f1..f0b54c6f 100644 --- a/util/src/entities/TeamMember.ts +++ b/util/src/entities/TeamMember.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { User } from "./User"; @@ -15,14 +15,14 @@ export class TeamMember extends BaseClass { @Column("simple-array") permissions: string[]; - @Column() + @RelationId((member: TeamMember) => member.team) team_id: string; @JoinColumn({ name: "team_id" }) @ManyToOne(() => require("./Team").Team, (team: import("./Team").Team) => team.id) team: import("./Team").Team; - @Column() + @RelationId((member: TeamMember) => member.user) user_id: string; @JoinColumn({ name: "user_id" }) diff --git a/util/src/entities/Template.ts b/util/src/entities/Template.ts index 5c9a5120..c8d2034c 100644 --- a/util/src/entities/Template.ts +++ b/util/src/entities/Template.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { User } from "./User"; @@ -17,6 +17,9 @@ export class Template extends BaseClass { @Column() usage_count?: number; + @RelationId((template: Template) => template.creator) + creator_id: string; + @JoinColumn({ name: "creator_id" }) @ManyToOne(() => User, (user: User) => user.id) creator: User; @@ -27,6 +30,9 @@ export class Template extends BaseClass { @Column() updated_at: Date; + @RelationId((template: Template) => template.source_guild) + source_guild_id: string; + @JoinColumn({ name: "source_guild_id" }) @ManyToOne(() => Guild, (guild: Guild) => guild.id) source_guild: Guild; diff --git a/util/src/entities/User.ts b/util/src/entities/User.ts index 39e59da4..bdf0d35f 100644 --- a/util/src/entities/User.ts +++ b/util/src/entities/User.ts @@ -1,8 +1,10 @@ -import { Column, Entity, JoinColumn, OneToMany } from "typeorm"; +import { Column, Entity, JoinColumn, OneToMany, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { BitField } from "../util/BitField"; import { Relationship } from "./Relationship"; import { ConnectedAccount } from "./ConnectedAccount"; +import { HTTPError } from "lambert-server"; +import { Guild } from "./Guild"; export const PublicUserProjection = { username: true, @@ -24,6 +26,13 @@ export class User extends BaseClass { @Column() discriminator: string; // #0001 4 digit long string from #0001 - #9999 + setDiscriminator(val: string) { + const number = Number(val); + if (isNaN(number)) throw new Error("invalid discriminator"); + if (number > 0 && number < 10000) throw new Error("discriminator must be between 1 and 9999"); + this.discriminator = val; + } + @Column() avatar?: string; // hash of the user avatar @@ -84,17 +93,21 @@ export class User extends BaseClass { @Column({ type: "bigint" }) public_flags: bigint; - @Column("simple-array") // string in simple-array must not contain commas - guilds: string[]; // array of guild ids the user is part of + @RelationId((user: User) => user.guilds) + guild_ids: string[]; // array of guild ids the user is part of + + @JoinColumn({ name: "guild_ids" }) + @OneToMany(() => Guild, (guild: Guild) => guild.id) + guilds: Guild[]; - @Column("simple-array") // string in simple-array must not contain commas + @RelationId((user: User) => user.relationships) relationship_ids: string[]; // array of guild ids the user is part of @JoinColumn({ name: "relationship_ids" }) @OneToMany(() => User, (user: User) => user.id) relationships: Relationship[]; - @Column("simple-array") // string in simple-array must not contain commas + @RelationId((user: User) => user.connected_accounts) connected_account_ids: string[]; // array of guild ids the user is part of @JoinColumn({ name: "connected_account_ids" }) @@ -102,14 +115,28 @@ export class User extends BaseClass { connected_accounts: ConnectedAccount[]; @Column({ type: "simple-json", select: false }) - user_data: { + 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({ type: "simple-array" }) + fingerprints: string[]; // array of fingerprints -> used to prevent multiple accounts + @Column("simple-json") settings: UserSettings; + + static async getPublicUser(user_id: string, additional_fields?: any) { + const user = await User.findOne( + { id: user_id }, + { + ...PublicUserProjection, + ...additional_fields, + } + ); + if (!user) throw new HTTPError("User not found", 404); + return user; + } } export interface UserSettings { diff --git a/util/src/entities/VoiceState.ts b/util/src/entities/VoiceState.ts index 2416c6c0..6707a575 100644 --- a/util/src/entities/VoiceState.ts +++ b/util/src/entities/VoiceState.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne, OneToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, OneToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; import { Guild } from "./Guild"; @@ -6,14 +6,23 @@ import { User } from "./User"; @Entity("voice_states") export class VoiceState extends BaseClass { + @RelationId((voice_state: VoiceState) => voice_state.guild) + guild_id: string; + @JoinColumn({ name: "guild_id" }) @ManyToOne(() => Guild, (guild: Guild) => guild.id) guild?: Guild; + @RelationId((voice_state: VoiceState) => voice_state.channel) + channel_id: string; + @JoinColumn({ name: "channel_id" }) @ManyToOne(() => Channel, (channel: Channel) => channel.id) channel: Channel; + @RelationId((voice_state: VoiceState) => voice_state.user) + user_id: string; + @JoinColumn({ name: "user_id" }) @ManyToOne(() => User, (user: User) => user.id) user: User; diff --git a/util/src/entities/Webhook.ts b/util/src/entities/Webhook.ts index 54233638..dc929c18 100644 --- a/util/src/entities/Webhook.ts +++ b/util/src/entities/Webhook.ts @@ -1,5 +1,9 @@ -import { Column, Entity, JoinColumn } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; +import { Application } from "./Application"; import { BaseClass } from "./BaseClass"; +import { Channel } from "./Channel"; +import { Guild } from "./Guild"; +import { User } from "./User"; export enum WebhookType { Incoming = 1, @@ -23,18 +27,38 @@ export class Webhook extends BaseClass { @Column() token?: string; - @JoinColumn() - guild?: string; + @RelationId((webhook: Webhook) => webhook.guild) + guild_id: string; - @JoinColumn() - channel: string; + @JoinColumn({ name: "guild_id" }) + @ManyToOne(() => Guild, (guild: Guild) => guild.id) + guild: Guild; - @JoinColumn() - application?: string; + @RelationId((webhook: Webhook) => webhook.channel) + channel_id: string; - @JoinColumn() - user?: string; + @JoinColumn({ name: "channel_id" }) + @ManyToOne(() => Channel, (channel: Channel) => channel.id) + channel: Channel; - @JoinColumn() - source_guild: string; + @RelationId((webhook: Webhook) => webhook.application) + application_id: string; + + @JoinColumn({ name: "application_id" }) + @ManyToOne(() => Application, (application: Application) => application.id) + application: Application; + + @RelationId((webhook: Webhook) => webhook.user) + user_id: string; + + @JoinColumn({ name: "user_id" }) + @ManyToOne(() => User, (user: User) => user.id) + user: User; + + @RelationId((webhook: Webhook) => webhook.guild) + source_guild_id: string; + + @JoinColumn({ name: "source_guild_id" }) + @ManyToOne(() => Guild, (guild: Guild) => guild.id) + source_guild: Guild; } diff --git a/util/src/entities/index.ts b/util/src/entities/index.ts index 9cb10016..b9e361c1 100644 --- a/util/src/entities/index.ts +++ b/util/src/entities/index.ts @@ -3,6 +3,7 @@ export * from "./AuditLog"; export * from "./Ban"; export * from "./BaseClass"; export * from "./Channel"; +export * from "./Config"; export * from "./ConnectedAccount"; export * from "./Emoji"; export * from "./Guild"; -- cgit 1.5.1 From ac84431cc2d85d2510a19c2726a6eb3d7ae3fc9c Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Sun, 29 Aug 2021 16:58:37 +0200 Subject: fix util --- util/src/entities/Guild.ts | 2 +- util/src/entities/Message.ts | 4 ++-- util/src/entities/RateLimit.ts | 8 ++------ util/src/entities/Role.ts | 1 + util/src/entities/User.ts | 38 +++++++++++++++++++------------------- util/src/entities/index.ts | 1 + util/src/interfaces/Event.ts | 2 +- util/src/util/Config.ts | 4 +++- util/src/util/Database.ts | 12 +++++++----- util/src/util/checkToken.ts | 3 ++- 10 files changed, 39 insertions(+), 36 deletions(-) (limited to 'util/src/entities/index.ts') diff --git a/util/src/entities/Guild.ts b/util/src/entities/Guild.ts index 9ca4b1e4..e6a93824 100644 --- a/util/src/entities/Guild.ts +++ b/util/src/entities/Guild.ts @@ -158,7 +158,7 @@ export class Guild extends BaseClass { vanity_url_code?: string; @JoinColumn({ name: "vanity_url_code" }) - @OneToOne(() => Invite, (invite: Invite) => invite.code) + @ManyToOne(() => Invite) vanity_url?: Invite; @Column({ nullable: true }) diff --git a/util/src/entities/Message.ts b/util/src/entities/Message.ts index 0c41a2eb..43d0f9d0 100644 --- a/util/src/entities/Message.ts +++ b/util/src/entities/Message.ts @@ -148,8 +148,8 @@ export class Message extends BaseClass { party_id: string; }; - @Column({ type: "bigint", nullable: true }) - flags?: bigint; + @Column({ nullable: true }) + flags?: string; @RelationId((message: Message) => message.stickers) sticker_ids: string[]; diff --git a/util/src/entities/RateLimit.ts b/util/src/entities/RateLimit.ts index 3ac35df3..49af0416 100644 --- a/util/src/entities/RateLimit.ts +++ b/util/src/entities/RateLimit.ts @@ -7,12 +7,8 @@ export class RateLimit extends BaseClass { @Column() id: "global" | "error" | string; // channel_239842397 | guild_238927349823 | webhook_238923423498 - @RelationId((rate_limit: RateLimit) => rate_limit.user) - user_id: string; - - @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user) => user.id) - user: User; + @Column() // no relation as it also + executor_id: string; @Column() hits: number; diff --git a/util/src/entities/Role.ts b/util/src/entities/Role.ts index 7c6ce64e..ddae7e40 100644 --- a/util/src/entities/Role.ts +++ b/util/src/entities/Role.ts @@ -1,4 +1,5 @@ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; + import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; diff --git a/util/src/entities/User.ts b/util/src/entities/User.ts index c5f870fa..73afba67 100644 --- a/util/src/entities/User.ts +++ b/util/src/entities/User.ts @@ -49,7 +49,7 @@ export class User extends BaseClass { avatar?: string; // hash of the user avatar @Column({ nullable: true }) - accent_color?: number = 0; // banner color of user + accent_color?: number; // banner color of user @Column({ nullable: true }) banner?: string; // hash of the user banner @@ -58,52 +58,52 @@ export class User extends BaseClass { phone?: string; // phone number of the user @Column() - desktop: boolean = false; // if the user has desktop app installed + desktop: boolean; // if the user has desktop app installed @Column() - mobile: boolean = false; // if the user has mobile app installed + mobile: boolean; // if the user has mobile app installed @Column() - premium: boolean = false; // if user bought nitro + premium: boolean; // if user bought nitro @Column() - premium_type: number = 0; // nitro level + premium_type: number; // nitro level @Column() - bot: boolean = false; // if user is bot + bot: boolean; // if user is bot @Column() - bio: string = ""; // short description of the user (max 190 chars -> should be configurable) + bio: string; // short description of the user (max 190 chars -> should be configurable) @Column() - system: boolean = false; // shouldn't be used, the api sents this field type true, if the generated message comes from a system generated author + system: boolean; // shouldn't be used, the api sents this field type true, if the generated message comes from a system generated author @Column() - nsfw_allowed: boolean = false; // if the user is older than 18 (resp. Config) + nsfw_allowed: boolean; // if the user is older than 18 (resp. Config) @Column() - mfa_enabled: boolean = false; // if multi factor authentication is enabled + mfa_enabled: boolean; // if multi factor authentication is enabled @Column() created_at: Date = new Date(); // registration date @Column() - verified: boolean = false; // if the user is offically verified + verified: boolean; // if the user is offically verified @Column() - disabled: boolean = false; // if the account is disabled + disabled: boolean; // if the account is disabled @Column() - deleted: boolean = false; // if the user was deleted + deleted: boolean; // if the user was deleted @Column({ nullable: true }) email?: string; // email of the user - @Column({ type: "bigint" }) - flags: bigint = BigInt(0); // UserFlags + @Column() + flags: string; // UserFlags - @Column({ type: "bigint" }) - public_flags: bigint = BigInt(0); + @Column() + public_flags: string; @RelationId((user: User) => user.relationships) relationship_ids: string[]; // array of guild ids the user is part of @@ -123,13 +123,13 @@ export class User extends BaseClass { 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) - } = { valid_tokens_since: new Date() }; + }; @Column({ type: "simple-array" }) fingerprints: string[] = []; // array of fingerprints -> used to prevent multiple accounts @Column({ type: "simple-json" }) - settings: UserSettings = defaultSettings; + settings: UserSettings; static async getPublicUser(user_id: string, opts?: FindOneOptions) { const user = await User.findOne(user_id, { diff --git a/util/src/entities/index.ts b/util/src/entities/index.ts index b9e361c1..e0246a10 100644 --- a/util/src/entities/index.ts +++ b/util/src/entities/index.ts @@ -14,6 +14,7 @@ export * from "./RateLimit"; export * from "./ReadState"; export * from "./Relationship"; export * from "./Role"; +export * from "./Sticker"; export * from "./Team"; export * from "./TeamMember"; export * from "./Template"; diff --git a/util/src/interfaces/Event.ts b/util/src/interfaces/Event.ts index bab6f4dc..e855095c 100644 --- a/util/src/interfaces/Event.ts +++ b/util/src/interfaces/Event.ts @@ -515,4 +515,4 @@ export type EVENT = | "RELATIONSHIP_REMOVE" | CUSTOMEVENTS; -export type CUSTOMEVENTS = "INVALIDATED"; +export type CUSTOMEVENTS = "INVALIDATED" | "RATELIMIT"; diff --git a/util/src/util/Config.ts b/util/src/util/Config.ts index f8574f38..f16921bd 100644 --- a/util/src/util/Config.ts +++ b/util/src/util/Config.ts @@ -6,6 +6,7 @@ var config: ConfigEntity; export const Config = { init: async function init() { + if (config) return config; config = new ConfigEntity({}, { id: "0" }); return this.set((config.value || {}).merge(DefaultConfigOptions)); }, @@ -13,7 +14,8 @@ export const Config = { return config.value as ConfigValue; }, set: function set(val: any) { - config.value = val.merge(config.value); + if (!config) return; + config.value = val.merge(config?.value || {}); return config.save(); }, }; diff --git a/util/src/util/Database.ts b/util/src/util/Database.ts index f49fb04c..c22d8abd 100644 --- a/util/src/util/Database.ts +++ b/util/src/util/Database.ts @@ -1,5 +1,5 @@ import "reflect-metadata"; -import { Connection, createConnection } from "typeorm"; +import { Connection, createConnection, ValueTransformer } from "typeorm"; import * as Models from "../entities"; // UUID extension option is only supported with postgres @@ -14,10 +14,10 @@ export function initDatabase() { console.log("[Database] connecting ..."); // @ts-ignore promise = createConnection({ - // type: "sqlite", - // database: "database.db", - type: "postgres", - url: "postgres://fosscord:wb94SmuURM2Syv&@localhost/fosscord", + type: "sqlite", + database: "database.db", + // type: "postgres", + // url: "postgres://fosscord:wb94SmuURM2Syv&@localhost/fosscord", // entities: Object.values(Models).filter((x) => x.constructor.name !== "Object"), synchronize: true, @@ -25,6 +25,8 @@ export function initDatabase() { cache: { duration: 1000 * 3, // cache all find queries for 3 seconds }, + bigNumberStrings: false, + supportBigNumbers: true, }); promise.then((connection) => { diff --git a/util/src/util/checkToken.ts b/util/src/util/checkToken.ts index 1e203006..8415e8c0 100644 --- a/util/src/util/checkToken.ts +++ b/util/src/util/checkToken.ts @@ -12,7 +12,8 @@ export function checkToken(token: string, jwtSecret: string): Promise { const user = await User.findOne({ id: decoded.id }, { select: ["data", "bot", "disabled", "deleted"] }); if (!user) return rej("Invalid Token"); // we need to round it to seconds as it saved as seconds in jwt iat and valid_tokens_since is stored in milliseconds - if (decoded.iat * 1000 < user.data.valid_tokens_since.setSeconds(0, 0)) return rej("Invalid Token"); + if (decoded.iat * 1000 < new Date(user.data.valid_tokens_since).setSeconds(0, 0)) + return rej("Invalid Token"); if (user.disabled) return rej("User disabled"); if (user.deleted) return rej("User not found"); -- cgit 1.5.1 From f31317851811cd172bf5690671c86a1b514f0d0c Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Tue, 31 Aug 2021 17:54:09 +0200 Subject: :sparkles: delete _ids from entities --- util/package-lock.json | 14 ++--- util/package.json | 2 +- util/src/entities/Application.ts | 16 +++--- util/src/entities/Attachment.ts | 34 ++++++++++++ util/src/entities/AuditLog.ts | 8 ++- util/src/entities/Ban.ts | 9 ++-- util/src/entities/BaseClass.ts | 34 ++++++------ util/src/entities/Channel.ts | 98 +++++++++++++++++++++++++++++------ util/src/entities/Config.ts | 8 +-- util/src/entities/ConnectedAccount.ts | 3 +- util/src/entities/Emoji.ts | 11 +--- util/src/entities/Guild.ts | 68 ++++++++++++++++-------- util/src/entities/Invite.ts | 12 +++-- util/src/entities/Member.ts | 86 ++++++++++++++++-------------- util/src/entities/Message.ts | 78 ++++++++++++---------------- util/src/entities/ReadState.ts | 9 ++-- util/src/entities/Recipient.ts | 19 +++++++ util/src/entities/Relationship.ts | 3 +- util/src/entities/Role.ts | 3 +- util/src/entities/Sticker.ts | 2 +- util/src/entities/Team.ts | 10 ++-- util/src/entities/TeamMember.ts | 6 ++- util/src/entities/Template.ts | 6 ++- util/src/entities/User.ts | 22 ++++---- util/src/entities/VoiceState.ts | 11 ++-- util/src/entities/Webhook.ts | 15 ++++-- util/src/entities/index.ts | 2 + util/src/interfaces/Event.ts | 13 ++--- util/src/tes.ts | 12 +++-- util/src/util/Config.ts | 2 +- util/src/util/Permissions.ts | 7 +-- 31 files changed, 382 insertions(+), 241 deletions(-) create mode 100644 util/src/entities/Attachment.ts create mode 100644 util/src/entities/Recipient.ts (limited to 'util/src/entities/index.ts') diff --git a/util/package-lock.json b/util/package-lock.json index bb04d0bc..47aca2d1 100644 --- a/util/package-lock.json +++ b/util/package-lock.json @@ -24,7 +24,7 @@ "reflect-metadata": "^0.1.13", "sqlite3": "^5.0.2", "typeorm": "^0.2.37", - "typescript": "^4.3.5", + "typescript": "^4.4.2", "typescript-json-schema": "^0.50.1" }, "devDependencies": { @@ -8235,9 +8235,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz", + "integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -15150,9 +15150,9 @@ } }, "typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==" + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz", + "integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==" }, "typescript-json-schema": { "version": "0.50.1", diff --git a/util/package.json b/util/package.json index af14a521..39af7526 100644 --- a/util/package.json +++ b/util/package.json @@ -51,7 +51,7 @@ "reflect-metadata": "^0.1.13", "sqlite3": "^5.0.2", "typeorm": "^0.2.37", - "typescript": "^4.3.5", + "typescript": "^4.4.2", "typescript-json-schema": "^0.50.1" }, "jest": { diff --git a/util/src/entities/Application.ts b/util/src/entities/Application.ts index b179d171..2092cd4e 100644 --- a/util/src/entities/Application.ts +++ b/util/src/entities/Application.ts @@ -2,6 +2,7 @@ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Team } from "./Team"; +import { User } from "./User"; @Entity("applications") export class Application extends BaseClass { @@ -29,8 +30,9 @@ export class Application extends BaseClass { @Column({ nullable: true }) privacy_policy_url?: string; - @Column() - owner_id: string; + @JoinColumn({ name: "owner_id" }) + @ManyToOne(() => User) + owner?: User; @Column({ nullable: true }) summary?: string; @@ -38,18 +40,12 @@ export class Application extends BaseClass { @Column() verify_key: string; - @RelationId((application: Application) => application.team) - team_id: string; - @JoinColumn({ name: "team_id" }) - @ManyToOne(() => Team, (team: Team) => team.id) + @ManyToOne(() => Team) team?: Team; - @RelationId((application: Application) => application.guild) - guild_id: string; - @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) guild: Guild; // if this application is a game sold, this field will be the guild to which it has been linked @Column({ nullable: true }) diff --git a/util/src/entities/Attachment.ts b/util/src/entities/Attachment.ts new file mode 100644 index 00000000..ca893400 --- /dev/null +++ b/util/src/entities/Attachment.ts @@ -0,0 +1,34 @@ +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; +import { BaseClass } from "./BaseClass"; + +@Entity("attachments") +export class Attachment extends BaseClass { + @Column() + filename: string; // name of file attached + + @Column() + size: number; // size of file in bytes + + @Column() + url: string; // source url of file + + @Column() + proxy_url: string; // a proxied url of file + + @Column({ nullable: true }) + height?: number; // height of file (if image) + + @Column({ nullable: true }) + width?: number; // width of file (if image) + + @Column({ nullable: true }) + content_type?: string; + + @Column({ nullable: true }) + @RelationId((attachment: Attachment) => attachment.message) + message_id: string; + + @JoinColumn({ name: "message_id" }) + @ManyToOne(() => require("./Message").Message, (message: import("./Message").Message) => message.attachments) + message: import("./Message").Message; +} diff --git a/util/src/entities/AuditLog.ts b/util/src/entities/AuditLog.ts index ea8aa641..ceeb21fd 100644 --- a/util/src/entities/AuditLog.ts +++ b/util/src/entities/AuditLog.ts @@ -43,18 +43,16 @@ export enum AuditLogEvents { @Entity("audit_logs") export class AuditLogEntry extends BaseClass { - @RelationId((auditlog: AuditLogEntry) => auditlog.target) - target_id: string; - @JoinColumn({ name: "target_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) target?: User; + @Column({ nullable: true }) @RelationId((auditlog: AuditLogEntry) => auditlog.user) user_id: string; @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) user: User; @Column({ diff --git a/util/src/entities/Ban.ts b/util/src/entities/Ban.ts index f1cbd849..e8a6d648 100644 --- a/util/src/entities/Ban.ts +++ b/util/src/entities/Ban.ts @@ -5,25 +5,28 @@ import { User } from "./User"; @Entity("bans") export class Ban extends BaseClass { + @Column({ nullable: true }) @RelationId((ban: Ban) => ban.user) user_id: string; @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) user: User; + @Column({ nullable: true }) @RelationId((ban: Ban) => ban.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) guild: Guild; + @Column({ nullable: true }) @RelationId((ban: Ban) => ban.executor) executor_id: string; @JoinColumn({ name: "executor_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) executor: User; @Column() diff --git a/util/src/entities/BaseClass.ts b/util/src/entities/BaseClass.ts index 63ce5836..0856ccd1 100644 --- a/util/src/entities/BaseClass.ts +++ b/util/src/entities/BaseClass.ts @@ -1,13 +1,5 @@ import "reflect-metadata"; -import { - BaseEntity, - BeforeInsert, - BeforeUpdate, - EntityMetadata, - FindConditions, - FindManyOptions, - PrimaryColumn, -} from "typeorm"; +import { BaseEntity, BeforeInsert, BeforeUpdate, EntityMetadata, FindConditions, PrimaryColumn } from "typeorm"; import { Snowflake } from "../util/Snowflake"; import "missing-native-js-functions"; @@ -16,13 +8,12 @@ import "missing-native-js-functions"; export class BaseClass extends BaseEntity { @PrimaryColumn() - id: string; + id: string = Snowflake.generate(); // @ts-ignore - constructor(public props?: any, public opts: { id?: string } = {}) { + constructor(public props?: any) { super(); - - this.id = this.opts.id || Snowflake.generate(); + this.assign(props); } get construct(): any { @@ -35,13 +26,15 @@ export class BaseClass extends BaseEntity { assign(props: any) { if (!props || typeof props !== "object") return; - - delete props.id; delete props.opts; delete props.props; - const properties = new Set(this.metadata.columns.map((x: any) => x.propertyName)); - // will not include relational properties (e.g. @RelationId @ManyToMany) + const properties = new Set( + this.metadata.columns + .map((x: any) => x.propertyName) + .concat(this.metadata.relations.map((x) => x.propertyName)) + ); + // will not include relational properties for (const key in props) { if (!properties.has(key)) continue; @@ -65,8 +58,11 @@ export class BaseClass extends BaseEntity { } toJSON(): any { - // @ts-ignore - return Object.fromEntries(this.metadata.columns.map((x) => [x.propertyName, this[x.propertyName]])); + return Object.fromEntries( + this.metadata.columns // @ts-ignore + .map((x) => [x.propertyName, this[x.propertyName]]) // @ts-ignore + .concat(this.metadata.relations.map((x) => [x.propertyName, this[x.propertyName]])) + ); } static increment(conditions: FindConditions, propertyPath: string, value: number | string) { diff --git a/util/src/entities/Channel.ts b/util/src/entities/Channel.ts index 4900f485..e3586dfc 100644 --- a/util/src/entities/Channel.ts +++ b/util/src/entities/Channel.ts @@ -1,8 +1,12 @@ -import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, RelationId } from "typeorm"; +import { Column, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, OneToMany, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Message } from "./Message"; import { User } from "./User"; +import { HTTPError } from "lambert-server"; +import { emitEvent, getPermission, Snowflake } from "../util"; +import { ChannelCreateEvent } from "../interfaces"; +import { Recipient } from "./Recipient"; export enum ChannelType { GUILD_TEXT = 0, // a text channel within a server @@ -25,40 +29,39 @@ export class Channel extends BaseClass { @Column({ type: "simple-enum", enum: ChannelType }) type: ChannelType; - @Column("simple-array") - @RelationId((channel: Channel) => channel.recipients) - recipient_ids: string[]; - - @JoinColumn({ name: "recipient_ids" }) - @ManyToMany(() => User, (user: User) => user.id) - recipients?: User[]; + @OneToMany(() => Recipient, (recipient: Recipient) => recipient.channel, { cascade: true }) + recipients?: Recipient[]; + @Column({ nullable: true }) @RelationId((channel: Channel) => channel.last_message) last_message_id: string; @JoinColumn({ name: "last_message_id" }) - @ManyToOne(() => Message, (message: Message) => message.id) + @ManyToOne(() => Message) last_message?: Message; + @Column({ nullable: true }) @RelationId((channel: Channel) => channel.guild) guild_id?: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) guild: Guild; + @Column({ nullable: true }) @RelationId((channel: Channel) => channel.parent) parent_id: string; @JoinColumn({ name: "parent_id" }) - @ManyToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) parent?: Channel; + @Column({ nullable: true }) @RelationId((channel: Channel) => channel.owner) owner_id: string; @JoinColumn({ name: "owner_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) owner: User; @Column({ nullable: true }) @@ -82,14 +85,77 @@ export class Channel extends BaseClass { @Column({ nullable: true }) user_limit?: number; - @Column() - nsfw: boolean; + @Column({ nullable: true }) + nsfw?: boolean; - @Column() - rate_limit_per_user: number; + @Column({ nullable: true }) + rate_limit_per_user?: number; @Column({ nullable: true }) topic?: string; + + // TODO: DM channel + static async createChannel( + channel: Partial, + user_id: string = "0", + opts?: { + keepId?: boolean; + skipExistsCheck?: boolean; + skipPermissionCheck?: boolean; + skipEventEmit?: boolean; + } + ) { + if (!opts?.skipPermissionCheck) { + // Always check if user has permission first + const permissions = await getPermission(user_id, channel.guild_id); + permissions.hasThrow("MANAGE_CHANNELS"); + } + + switch (channel.type) { + case ChannelType.GUILD_TEXT: + case ChannelType.GUILD_VOICE: + if (channel.parent_id && !opts?.skipExistsCheck) { + const exists = await Channel.findOneOrFail({ id: channel.parent_id }); + if (!exists) throw new HTTPError("Parent id channel doesn't exist", 400); + if (exists.guild_id !== channel.guild_id) + throw new HTTPError("The category channel needs to be in the guild"); + } + break; + case ChannelType.GUILD_CATEGORY: + break; + case ChannelType.DM: + case ChannelType.GROUP_DM: + throw new HTTPError("You can't create a dm channel in a guild"); + // TODO: check if guild is community server + case ChannelType.GUILD_STORE: + case ChannelType.GUILD_NEWS: + default: + throw new HTTPError("Not yet supported"); + } + + if (!channel.permission_overwrites) channel.permission_overwrites = []; + // TODO: auto generate position + + channel = { + ...channel, + ...(!opts?.keepId && { id: Snowflake.generate() }), + created_at: new Date(), + position: channel.position || 0, + }; + + await Promise.all([ + Channel.insert(channel), + !opts?.skipEventEmit + ? emitEvent({ + event: "CHANNEL_CREATE", + data: channel, + guild_id: channel.guild_id, + } as ChannelCreateEvent) + : Promise.resolve(), + ]); + + return channel; + } } export interface ChannelPermissionOverwrite { diff --git a/util/src/entities/Config.ts b/util/src/entities/Config.ts index 320a729c..04dc3c36 100644 --- a/util/src/entities/Config.ts +++ b/util/src/entities/Config.ts @@ -199,12 +199,12 @@ export const DefaultConfigOptions: ConfigValue = { window: 5, }, webhook: { - count: 5, - window: 20, + count: 10, + window: 5, }, channel: { - count: 5, - window: 20, + count: 10, + window: 5, }, auth: { login: { diff --git a/util/src/entities/ConnectedAccount.ts b/util/src/entities/ConnectedAccount.ts index e48bc322..75982d01 100644 --- a/util/src/entities/ConnectedAccount.ts +++ b/util/src/entities/ConnectedAccount.ts @@ -4,11 +4,12 @@ import { User } from "./User"; @Entity("connected_accounts") export class ConnectedAccount extends BaseClass { + @Column({ nullable: true }) @RelationId((account: ConnectedAccount) => account.user) user_id: string; @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user: User) => user.connected_accounts) + @ManyToOne(() => User) user: User; @Column({ select: false }) diff --git a/util/src/entities/Emoji.ts b/util/src/entities/Emoji.ts index 4c0fccd3..181aff2c 100644 --- a/util/src/entities/Emoji.ts +++ b/util/src/entities/Emoji.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, RelationId } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Role } from "./Role"; @@ -15,7 +15,7 @@ export class Emoji extends BaseClass { guild_id: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.emojis) + @ManyToOne(() => Guild) guild: Guild; @Column() @@ -26,11 +26,4 @@ export class Emoji extends BaseClass { @Column() require_colons: boolean; - - @RelationId((emoji: Emoji) => emoji.roles) - role_ids: string[]; - - @JoinColumn({ name: "role_ids" }) - @ManyToMany(() => Role, (role: Role) => role.id) - roles: Role[]; // roles this emoji is whitelisted to (new discord feature?) } diff --git a/util/src/entities/Guild.ts b/util/src/entities/Guild.ts index 3e7e8917..c1ef00ac 100644 --- a/util/src/entities/Guild.ts +++ b/util/src/entities/Guild.ts @@ -1,20 +1,30 @@ import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, OneToMany, OneToOne, RelationId } from "typeorm"; +import { Ban } from "./Ban"; import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; import { Emoji } from "./Emoji"; import { Invite } from "./Invite"; import { Member } from "./Member"; import { Role } from "./Role"; +import { Sticker } from "./Sticker"; +import { Template } from "./Template"; import { User } from "./User"; import { VoiceState } from "./VoiceState"; +import { Webhook } from "./Webhook"; + +// TODO: application_command_count, application_command_counts: {1: 0, 2: 0, 3: 0} +// TODO: guild_scheduled_events +// TODO: stage_instances +// TODO: threads @Entity("guilds") export class Guild extends BaseClass { + @Column({ nullable: true }) @RelationId((guild: Guild) => guild.afk_channel) afk_channel_id?: string; @JoinColumn({ name: "afk_channel_id" }) - @ManyToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) afk_channel?: Channel; @Column({ nullable: true }) @@ -25,6 +35,10 @@ export class Guild extends BaseClass { // @Column({ nullable: true }) // application?: string; + @JoinColumn({ name: "ban_ids" }) + @OneToMany(() => Ban, (ban: Ban) => ban.guild) + bans: Ban[]; + @Column({ nullable: true }) banner?: string; @@ -64,52 +78,57 @@ export class Guild extends BaseClass { @Column({ nullable: true }) presence_count?: number; // users online - @RelationId((guild: Guild) => guild.members) - member_ids: string[]; - - @JoinColumn({ name: "member_ids" }) @OneToMany(() => Member, (member: Member) => member.guild) members: Member[]; - @RelationId((guild: Guild) => guild.roles) - role_ids: string[]; - @JoinColumn({ name: "role_ids" }) @OneToMany(() => Role, (role: Role) => role.guild) roles: Role[]; - @RelationId((guild: Guild) => guild.channels) - channel_ids: string[]; - @JoinColumn({ name: "channel_ids" }) @OneToMany(() => Channel, (channel: Channel) => channel.guild) channels: Channel[]; - @RelationId((guild: Guild) => guild.emojis) - emoji_ids: string[]; + @Column({ nullable: true }) + @RelationId((guild: Guild) => guild.template) + template_id: string; + + @JoinColumn({ name: "template_id" }) + @ManyToOne(() => Template) + template: Template; @JoinColumn({ name: "emoji_ids" }) @OneToMany(() => Emoji, (emoji: Emoji) => emoji.guild) emojis: Emoji[]; - @RelationId((guild: Guild) => guild.voice_states) - voice_state_ids: string[]; + @JoinColumn({ name: "sticker_ids" }) + @OneToMany(() => Sticker, (sticker: Sticker) => sticker.guild) + stickers: Sticker[]; + + @JoinColumn({ name: "invite_ids" }) + @OneToMany(() => Invite, (invite: Invite) => invite.guild) + invites: Invite[]; @JoinColumn({ name: "voice_state_ids" }) @OneToMany(() => VoiceState, (voicestate: VoiceState) => voicestate.guild) voice_states: VoiceState[]; + @JoinColumn({ name: "webhook_ids" }) + @OneToMany(() => Webhook, (webhook: Webhook) => webhook.guild) + webhooks: Webhook[]; + @Column({ nullable: true }) mfa_level?: number; @Column() name: string; + @Column({ nullable: true }) @RelationId((guild: Guild) => guild.owner) owner_id: string; - @JoinColumn({ name: "owner_id" }) - @OneToOne(() => User) + @JoinColumn([{ name: "owner_id", referencedColumnName: "id" }]) + @ManyToOne(() => User) owner: User; @Column({ nullable: true }) @@ -121,18 +140,20 @@ export class Guild extends BaseClass { @Column({ nullable: true }) premium_tier?: number; // nitro boost level + @Column({ nullable: true }) @RelationId((guild: Guild) => guild.public_updates_channel) public_updates_channel_id: string; @JoinColumn({ name: "public_updates_channel_id" }) - @OneToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) public_updates_channel?: Channel; + @Column({ nullable: true }) @RelationId((guild: Guild) => guild.rules_channel) rules_channel_id?: string; @JoinColumn({ name: "rules_channel_id" }) - @OneToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) rules_channel?: string; @Column({ nullable: true }) @@ -141,11 +162,12 @@ export class Guild extends BaseClass { @Column({ nullable: true }) splash?: string; + @Column({ nullable: true }) @RelationId((guild: Guild) => guild.system_channel) system_channel_id?: string; @JoinColumn({ name: "system_channel_id" }) - @OneToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) system_channel?: Channel; @Column({ nullable: true }) @@ -154,11 +176,12 @@ export class Guild extends BaseClass { @Column({ nullable: true }) unavailable?: boolean; + @Column({ nullable: true }) @RelationId((guild: Guild) => guild.vanity_url) vanity_url_code?: string; @JoinColumn({ name: "vanity_url_code" }) - @OneToOne(() => Invite) + @ManyToOne(() => Invite) vanity_url?: Invite; @Column({ nullable: true }) @@ -176,11 +199,12 @@ export class Guild extends BaseClass { }[]; }; + @Column({ nullable: true }) @RelationId((guild: Guild) => guild.widget_channel) widget_channel_id?: string; @JoinColumn({ name: "widget_channel_id" }) - @OneToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) widget_channel?: Channel; @Column({ nullable: true }) diff --git a/util/src/entities/Invite.ts b/util/src/entities/Invite.ts index d8c6d69c..01e22294 100644 --- a/util/src/entities/Invite.ts +++ b/util/src/entities/Invite.ts @@ -27,32 +27,36 @@ export class Invite extends BaseClass { @Column() expires_at: Date; + @Column({ nullable: true }) @RelationId((invite: Invite) => invite.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) guild: Guild; + @Column({ nullable: true }) @RelationId((invite: Invite) => invite.channel) channel_id: string; @JoinColumn({ name: "channel_id" }) - @ManyToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) channel: Channel; + @Column({ nullable: true }) @RelationId((invite: Invite) => invite.inviter) inviter_id: string; @JoinColumn({ name: "inviter_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) inviter: User; + @Column({ nullable: true }) @RelationId((invite: Invite) => invite.target_user) target_user_id: string; @JoinColumn({ name: "target_user_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) target_user?: string; // could be used for "User specific invites" https://github.com/fosscord/fosscord/issues/62 @Column({ nullable: true }) diff --git a/util/src/entities/Member.ts b/util/src/entities/Member.ts index c5d289ef..d2d78bb9 100644 --- a/util/src/entities/Member.ts +++ b/util/src/entities/Member.ts @@ -15,27 +15,22 @@ import { Role } from "./Role"; @Entity("members") export class Member extends BaseClass { - @RelationId((member: Member) => member.user) - user_id: string; - - @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @JoinColumn({ name: "id" }) + @ManyToOne(() => User) user: User; + @Column({ nullable: true }) @RelationId((member: Member) => member.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.members) + @ManyToOne(() => Guild) guild: Guild; @Column({ nullable: true }) nick?: string; - @RelationId((member: Member) => member.roles) - role_ids: string[]; - - @JoinTable() + @JoinTable({ name: "member_roles" }) @ManyToMany(() => Role) roles: Role[]; @@ -62,7 +57,7 @@ export class Member extends BaseClass { read_state: Record; static async IsInGuildOrFail(user_id: string, guild_id: string) { - if (await Member.count({ id: user_id, guild_id })) return true; + if (await Member.count({ id: user_id, guild: { id: guild_id } })) return true; throw new HTTPError("You are not member of this guild", 403); } @@ -99,46 +94,54 @@ export class Member extends BaseClass { static async addRole(user_id: string, guild_id: string, role_id: string) { const [member] = await Promise.all([ + // @ts-ignore Member.findOneOrFail({ where: { id: user_id, guild_id: guild_id }, - relations: ["user"], // we don't want to load the role objects just the ids + relations: ["user", "roles"], // we don't want to load the role objects just the ids + select: ["roles.id"], }), await Role.findOneOrFail({ id: role_id, guild_id: guild_id }), ]); - member.role_ids.push(role_id); - member.save(); + member.roles.push(new Role({ id: role_id })); - await emitEvent({ - event: "GUILD_MEMBER_UPDATE", - data: { + await Promise.all([ + member.save(), + emitEvent({ + event: "GUILD_MEMBER_UPDATE", + data: { + guild_id: guild_id, + user: member.user, + roles: member.roles.map((x) => x.id), + }, guild_id: guild_id, - user: member.user, - roles: member.role_ids, - }, - guild_id: guild_id, - } as GuildMemberUpdateEvent); + } as GuildMemberUpdateEvent), + ]); } static async removeRole(user_id: string, guild_id: string, role_id: string) { const [member] = await Promise.all([ + // @ts-ignore Member.findOneOrFail({ where: { id: user_id, guild_id: guild_id }, - relations: ["user"], // we don't want to load the role objects just the ids + relations: ["user", "roles"], // we don't want to load the role objects just the ids + select: ["roles.id"], }), await Role.findOneOrFail({ id: role_id, guild_id: guild_id }), ]); - member.role_ids.remove(role_id); - member.save(); + member.roles = member.roles.filter((x) => x.id == role_id); - await emitEvent({ - event: "GUILD_MEMBER_UPDATE", - data: { + await Promise.all([ + member.save(), + emitEvent({ + event: "GUILD_MEMBER_UPDATE", + data: { + guild_id: guild_id, + user: member.user, + roles: member.roles.map((x) => x.id), + }, guild_id: guild_id, - user: member.user, - roles: member.role_ids, - }, - guild_id: guild_id, - } as GuildMemberUpdateEvent); + } as GuildMemberUpdateEvent), + ]); } static async changeNickname(user_id: string, guild_id: string, nickname: string) { @@ -175,11 +178,14 @@ export class Member extends BaseClass { throw new HTTPError(`You are at the ${maxGuilds} server limit.`, 403); } - const guild = await Guild.findOneOrFail(guild_id, { + const guild = await Guild.findOneOrFail({ + where: { + id: guild_id, + }, relations: ["channels", "emojis", "members", "roles", "stickers"], }); - if (await Member.count({ id: user.id, guild_id })) + if (await Member.count({ id: user.id, guild: { id: guild_id } })) throw new HTTPError("You are already a member of this guild", 400); const member = { @@ -194,23 +200,23 @@ export class Member extends BaseClass { pending: false, }; // @ts-ignore - guild.joined_at = member.joined_at; + guild.joined_at = member.joined_at.toISOString(); await Promise.all([ - new Member({ + Member.insert({ ...member, + roles: undefined, read_state: {}, settings: { channel_overrides: [], message_notifications: 0, mobile_push: true, - mute_config: null, muted: false, suppress_everyone: false, suppress_roles: false, version: 0, }, - }).save(), + }), Guild.increment({ id: guild_id }, "member_count", 1), emitEvent({ event: "GUILD_MEMBER_ADD", @@ -223,7 +229,7 @@ export class Member extends BaseClass { } as GuildMemberAddEvent), emitEvent({ event: "GUILD_CREATE", - data: guild, + data: { ...guild, members: [...guild.members, member] }, user_id, } as GuildCreateEvent), ]); diff --git a/util/src/entities/Message.ts b/util/src/entities/Message.ts index 43d0f9d0..542b2b55 100644 --- a/util/src/entities/Message.ts +++ b/util/src/entities/Message.ts @@ -9,8 +9,10 @@ import { CreateDateColumn, Entity, JoinColumn, + JoinTable, ManyToMany, ManyToOne, + OneToMany, RelationId, UpdateDateColumn, } from "typeorm"; @@ -18,6 +20,7 @@ import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Webhook } from "./Webhook"; import { Sticker } from "./Sticker"; +import { Attachment } from "./Attachment"; export enum MessageType { DEFAULT = 0, @@ -44,46 +47,52 @@ export class Message extends BaseClass { @Column() id: string; + @Column({ nullable: true }) @RelationId((message: Message) => message.channel) channel_id: string; @JoinColumn({ name: "channel_id" }) - @ManyToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) channel: Channel; + @Column({ nullable: true }) @RelationId((message: Message) => message.guild) guild_id?: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) guild?: Guild; + @Column({ nullable: true }) @RelationId((message: Message) => message.author) author_id: string; - @JoinColumn({ name: "author_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @JoinColumn({ name: "author_id", referencedColumnName: "id" }) + @ManyToOne(() => User) author?: User; + @Column({ nullable: true }) @RelationId((message: Message) => message.member) member_id: string; @JoinColumn({ name: "member_id" }) - @ManyToOne(() => Member, (member: Member) => member.id) + @ManyToOne(() => Member) member?: Member; + @Column({ nullable: true }) @RelationId((message: Message) => message.webhook) webhook_id: string; @JoinColumn({ name: "webhook_id" }) - @ManyToOne(() => Webhook, (webhook: Webhook) => webhook.id) + @ManyToOne(() => Webhook) webhook?: Webhook; + @Column({ nullable: true }) @RelationId((message: Message) => message.application) application_id: string; @JoinColumn({ name: "application_id" }) - @ManyToOne(() => Application, (application: Application) => application.id) + @ManyToOne(() => Application) application?: Application; @Column({ nullable: true }) @@ -103,29 +112,25 @@ export class Message extends BaseClass { @Column({ nullable: true }) mention_everyone?: boolean; - @RelationId((message: Message) => message.mentions) - mention_user_ids: string[]; - - @JoinColumn({ name: "mention_user_ids" }) - @ManyToMany(() => User, (user: User) => user.id) + @JoinTable({ name: "message_user_mentions" }) + @ManyToMany(() => User) mentions: User[]; - @RelationId((message: Message) => message.mention_roles) - mention_role_ids: string[]; - - @JoinColumn({ name: "mention_role_ids" }) - @ManyToMany(() => Role, (role: Role) => role.id) + @JoinTable({ name: "message_role_mentions" }) + @ManyToMany(() => Role) mention_roles: Role[]; - @RelationId((message: Message) => message.mention_channels) - mention_channel_ids: string[]; - - @JoinColumn({ name: "mention_channel_ids" }) - @ManyToMany(() => Channel, (channel: Channel) => channel.id) + @JoinTable({ name: "message_channel_mentions" }) + @ManyToMany(() => Channel) mention_channels: Channel[]; - @Column({ type: "simple-json" }) - attachments: Attachment[]; + @JoinTable({ name: "message_stickers" }) + @ManyToMany(() => Sticker) + sticker_items?: Sticker[]; + + @JoinColumn({ name: "attachment_ids" }) + @OneToMany(() => Attachment, (attachment: Attachment) => attachment.message) + attachments?: Attachment[]; @Column({ type: "simple-json" }) embeds: Embed[]; @@ -150,14 +155,6 @@ export class Message extends BaseClass { @Column({ nullable: true }) flags?: string; - - @RelationId((message: Message) => message.stickers) - sticker_ids: string[]; - - @JoinColumn({ name: "sticker_ids" }) - @ManyToMany(() => Sticker, (sticker: Sticker) => sticker.id) - stickers?: Sticker[]; - @Column({ type: "simple-json", nullable: true }) message_reference?: { message_id: string; @@ -166,7 +163,7 @@ export class Message extends BaseClass { }; @JoinColumn({ name: "message_reference_id" }) - @ManyToOne(() => Message, (message: Message) => message.id) + @ManyToOne(() => Message) referenced_message?: Message; @Column({ type: "simple-json", nullable: true }) @@ -178,8 +175,8 @@ export class Message extends BaseClass { // user: User; // TODO: autopopulate user }; - @Column({ type: "simple-json" }) - components: MessageComponent[]; + @Column({ type: "simple-json", nullable: true }) + components?: MessageComponent[]; } export interface MessageComponent { @@ -198,17 +195,6 @@ export enum MessageComponentType { Button = 2, } -export interface Attachment { - id: string; // attachment id - filename: string; // name of file attached - size: number; // size of file in bytes - url: string; // source url of file - proxy_url: string; // a proxied url of file - height?: number; // height of file (if image) - width?: number; // width of file (if image) - content_type?: string; -} - export interface Embed { title?: string; //title of embed type?: EmbedType; // type of embed (always "rich" for webhook embeds) diff --git a/util/src/entities/ReadState.ts b/util/src/entities/ReadState.ts index 650ee4c3..8dd05b21 100644 --- a/util/src/entities/ReadState.ts +++ b/util/src/entities/ReadState.ts @@ -10,25 +10,28 @@ import { User } from "./User"; @Entity("read_states") export class ReadState extends BaseClass { + @Column({ nullable: true }) @RelationId((read_state: ReadState) => read_state.channel) channel_id: string; @JoinColumn({ name: "channel_id" }) - @ManyToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) channel: Channel; + @Column({ nullable: true }) @RelationId((read_state: ReadState) => read_state.user) user_id: string; @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) user: User; + @Column({ nullable: true }) @RelationId((read_state: ReadState) => read_state.last_message) last_message_id: string; @JoinColumn({ name: "last_message_id" }) - @ManyToOne(() => Message, (message: Message) => message.id) + @ManyToOne(() => Message) last_message?: Message; @Column({ nullable: true }) diff --git a/util/src/entities/Recipient.ts b/util/src/entities/Recipient.ts new file mode 100644 index 00000000..75d5b94d --- /dev/null +++ b/util/src/entities/Recipient.ts @@ -0,0 +1,19 @@ +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; +import { BaseClass } from "./BaseClass"; + +@Entity("recipients") +export class Recipient extends BaseClass { + @Column() + @RelationId((recipient: Recipient) => recipient.channel) + channel_id: string; + + @JoinColumn({ name: "channel_id" }) + @ManyToOne(() => require("./Channel").Channel) + channel: import("./Channel").Channel; + + @JoinColumn({ name: "id" }) + @ManyToOne(() => require("./User").User) + user: import("./User").User; + + // TODO: settings/mute/nick/added at/encryption keys/read_state +} diff --git a/util/src/entities/Relationship.ts b/util/src/entities/Relationship.ts index cf3624bf..5935f5b6 100644 --- a/util/src/entities/Relationship.ts +++ b/util/src/entities/Relationship.ts @@ -11,11 +11,12 @@ export enum RelationshipType { @Entity("relationships") export class Relationship extends BaseClass { + @Column({ nullable: true }) @RelationId((relationship: Relationship) => relationship.user) user_id: string; @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user: User) => user.relationships) + @ManyToOne(() => User) user: User; @Column({ nullable: true }) diff --git a/util/src/entities/Role.ts b/util/src/entities/Role.ts index be8c731d..33c8d272 100644 --- a/util/src/entities/Role.ts +++ b/util/src/entities/Role.ts @@ -5,11 +5,12 @@ import { Guild } from "./Guild"; @Entity("roles") export class Role extends BaseClass { + @Column({ nullable: true }) @RelationId((role: Role) => role.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) guild: Guild; @Column() diff --git a/util/src/entities/Sticker.ts b/util/src/entities/Sticker.ts index 12394207..7730a86a 100644 --- a/util/src/entities/Sticker.ts +++ b/util/src/entities/Sticker.ts @@ -31,7 +31,7 @@ export class Sticker extends BaseClass { guild_id?: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) guild?: Guild; @Column({ type: "simple-enum", enum: StickerType }) diff --git a/util/src/entities/Team.ts b/util/src/entities/Team.ts index b37f368c..beb8bf68 100644 --- a/util/src/entities/Team.ts +++ b/util/src/entities/Team.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, RelationId } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, OneToMany, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { TeamMember } from "./TeamMember"; import { User } from "./User"; @@ -8,20 +8,18 @@ export class Team extends BaseClass { @Column({ nullable: true }) icon?: string; - @RelationId((team: Team) => team.members) - member_ids: string[]; - @JoinColumn({ name: "member_ids" }) - @ManyToMany(() => TeamMember, (member: TeamMember) => member.id) + @OneToMany(() => TeamMember, (member: TeamMember) => member.team) members: TeamMember[]; @Column() name: string; + @Column({ nullable: true }) @RelationId((team: Team) => team.owner_user) owner_user_id: string; @JoinColumn({ name: "owner_user_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) owner_user: User; } diff --git a/util/src/entities/TeamMember.ts b/util/src/entities/TeamMember.ts index d4c95397..6b184d08 100644 --- a/util/src/entities/TeamMember.ts +++ b/util/src/entities/TeamMember.ts @@ -15,17 +15,19 @@ export class TeamMember extends BaseClass { @Column({ type: "simple-array" }) permissions: string[]; + @Column({ nullable: true }) @RelationId((member: TeamMember) => member.team) team_id: string; @JoinColumn({ name: "team_id" }) - @ManyToOne(() => require("./Team").Team, (team: import("./Team").Team) => team.id) + @ManyToOne(() => require("./Team").Team, (team: import("./Team").Team) => team.members) team: import("./Team").Team; + @Column({ nullable: true }) @RelationId((member: TeamMember) => member.user) user_id: string; @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) user: User; } diff --git a/util/src/entities/Template.ts b/util/src/entities/Template.ts index 2d8c9b6c..76f77ba6 100644 --- a/util/src/entities/Template.ts +++ b/util/src/entities/Template.ts @@ -17,11 +17,12 @@ export class Template extends BaseClass { @Column({ nullable: true }) usage_count?: number; + @Column({ nullable: true }) @RelationId((template: Template) => template.creator) creator_id: string; @JoinColumn({ name: "creator_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) creator: User; @Column() @@ -30,11 +31,12 @@ export class Template extends BaseClass { @Column() updated_at: Date; + @Column({ nullable: true }) @RelationId((template: Template) => template.source_guild) source_guild_id: string; @JoinColumn({ name: "source_guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) source_guild: Guild; @Column({ type: "simple-json" }) diff --git a/util/src/entities/User.ts b/util/src/entities/User.ts index 73afba67..39f654be 100644 --- a/util/src/entities/User.ts +++ b/util/src/entities/User.ts @@ -1,9 +1,10 @@ -import { Column, Entity, FindOneOptions, JoinColumn, OneToMany, RelationId } from "typeorm"; +import { Column, Entity, FindOneOptions, JoinColumn, ManyToMany, OneToMany, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { BitField } from "../util/BitField"; import { Relationship } from "./Relationship"; import { ConnectedAccount } from "./ConnectedAccount"; import { HTTPError } from "lambert-server"; +import { Channel } from "./Channel"; type PublicUserKeys = | "username" @@ -105,16 +106,10 @@ export class User extends BaseClass { @Column() public_flags: string; - @RelationId((user: User) => user.relationships) - relationship_ids: string[]; // array of guild ids the user is part of - @JoinColumn({ name: "relationship_ids" }) @OneToMany(() => Relationship, (relationship: Relationship) => relationship.user, { cascade: true }) relationships: Relationship[]; - @RelationId((user: User) => user.connected_accounts) - connected_account_ids: string[]; // array of guild ids the user is part of - @JoinColumn({ name: "connected_account_ids" }) @OneToMany(() => ConnectedAccount, (account: ConnectedAccount) => account.user) connected_accounts: ConnectedAccount[]; @@ -126,16 +121,19 @@ export class User extends BaseClass { }; @Column({ type: "simple-array" }) - fingerprints: string[] = []; // array of fingerprints -> used to prevent multiple accounts + fingerprints: string[]; // array of fingerprints -> used to prevent multiple accounts @Column({ type: "simple-json" }) settings: UserSettings; static async getPublicUser(user_id: string, opts?: FindOneOptions) { - const user = await User.findOne(user_id, { - ...opts, - select: [...PublicUserProjection, ...(opts?.select || [])], - }); + const user = await User.findOne( + { id: user_id }, + { + ...opts, + select: [...PublicUserProjection, ...(opts?.select || [])], + } + ); if (!user) throw new HTTPError("User not found", 404); return user; } diff --git a/util/src/entities/VoiceState.ts b/util/src/entities/VoiceState.ts index e9d3dfa2..c5040cf1 100644 --- a/util/src/entities/VoiceState.ts +++ b/util/src/entities/VoiceState.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne, OneToOne, RelationId } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; import { Guild } from "./Guild"; @@ -6,25 +6,28 @@ import { User } from "./User"; @Entity("voice_states") export class VoiceState extends BaseClass { + @Column({ nullable: true }) @RelationId((voice_state: VoiceState) => voice_state.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) guild?: Guild; + @Column({ nullable: true }) @RelationId((voice_state: VoiceState) => voice_state.channel) channel_id: string; @JoinColumn({ name: "channel_id" }) - @ManyToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) channel: Channel; + @Column({ nullable: true }) @RelationId((voice_state: VoiceState) => voice_state.user) user_id: string; @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) user: User; @Column() diff --git a/util/src/entities/Webhook.ts b/util/src/entities/Webhook.ts index a75cb959..12ba0d08 100644 --- a/util/src/entities/Webhook.ts +++ b/util/src/entities/Webhook.ts @@ -27,38 +27,43 @@ export class Webhook extends BaseClass { @Column({ nullable: true }) token?: string; + @Column({ nullable: true }) @RelationId((webhook: Webhook) => webhook.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) guild: Guild; + @Column({ nullable: true }) @RelationId((webhook: Webhook) => webhook.channel) channel_id: string; @JoinColumn({ name: "channel_id" }) - @ManyToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) channel: Channel; + @Column({ nullable: true }) @RelationId((webhook: Webhook) => webhook.application) application_id: string; @JoinColumn({ name: "application_id" }) - @ManyToOne(() => Application, (application: Application) => application.id) + @ManyToOne(() => Application) application: Application; + @Column({ nullable: true }) @RelationId((webhook: Webhook) => webhook.user) user_id: string; @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) user: User; + @Column({ nullable: true }) @RelationId((webhook: Webhook) => webhook.guild) source_guild_id: string; @JoinColumn({ name: "source_guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) source_guild: Guild; } diff --git a/util/src/entities/index.ts b/util/src/entities/index.ts index e0246a10..aa37ae2e 100644 --- a/util/src/entities/index.ts +++ b/util/src/entities/index.ts @@ -1,4 +1,5 @@ export * from "./Application"; +export * from "./Attachment"; export * from "./AuditLog"; export * from "./Ban"; export * from "./BaseClass"; @@ -12,6 +13,7 @@ export * from "./Member"; export * from "./Message"; export * from "./RateLimit"; export * from "./ReadState"; +export * from "./Recipient"; export * from "./Relationship"; export * from "./Role"; export * from "./Sticker"; diff --git a/util/src/interfaces/Event.ts b/util/src/interfaces/Event.ts index 814a8beb..7ea1bd49 100644 --- a/util/src/interfaces/Event.ts +++ b/util/src/interfaces/Event.ts @@ -89,14 +89,7 @@ export interface ReadyEventData { }; merged_members?: Omit[][]; // probably all users who the user is in contact with - users?: { - avatar: string | null; - discriminator: string; - id: string; - username: string; - bot: boolean; - public_flags: string; - }[]; + users?: PublicUser[]; } export interface ReadyEvent extends Event { @@ -130,7 +123,9 @@ export interface ChannelPinsUpdateEvent extends Event { export interface GuildCreateEvent extends Event { event: "GUILD_CREATE"; - data: Guild; + data: Guild & { + joined_at: Date; + }; } export interface GuildUpdateEvent extends Event { diff --git a/util/src/tes.ts b/util/src/tes.ts index b469f1d8..e326dee1 100644 --- a/util/src/tes.ts +++ b/util/src/tes.ts @@ -5,10 +5,14 @@ import { initDatabase } from "./util"; initDatabase().then(async (x) => { try { - const user = await new User( - { guilds: [], discriminator: "1", username: "test", flags: "0", public_flags: "0" }, - { id: "0" } - ).save(); + const user = await new User({ + guilds: [], + discriminator: "1", + username: "test", + flags: "0", + public_flags: "0", + id: "0", + }).save(); user.relationships = [new Relationship({ type: RelationshipType.friends })]; diff --git a/util/src/util/Config.ts b/util/src/util/Config.ts index 508a8901..1ec71ad0 100644 --- a/util/src/util/Config.ts +++ b/util/src/util/Config.ts @@ -7,7 +7,7 @@ var config: ConfigEntity; export const Config = { init: async function init() { if (config) return config; - config = (await ConfigEntity.findOne({ id: "0" })) || new ConfigEntity({}, { id: "0" }); + config = (await ConfigEntity.findOne({ id: "0" })) || new ConfigEntity({ id: "0" }); return this.set((config.value || {}).merge(DefaultConfigOptions)); }, diff --git a/util/src/util/Permissions.ts b/util/src/util/Permissions.ts index ab6aa795..89b316ee 100644 --- a/util/src/util/Permissions.ts +++ b/util/src/util/Permissions.ts @@ -220,13 +220,14 @@ export async function getPermission(user_id?: string, guild_id?: string, channel guild = await Guild.findOneOrFail({ id: guild_id }); if (guild.owner_id === user_id) return new Permissions(Permissions.FLAGS.ADMINISTRATOR); - member = await Member.findOneOrFail({ where: { guild_id, id: user_id }, relations: ["roles"] }); + member = await Member.findOneOrFail({ where: { guild: guild_id, id: user_id }, relations: ["roles"] }); } + // TODO: remove guild.roles and convert recipient_ids to recipients var permission = Permissions.finalPermission({ user: { id: user_id, - roles: member?.role_ids || [], + roles: member?.roles.map((x) => x.id) || [], }, guild: { roles: member?.roles || [], @@ -234,7 +235,7 @@ export async function getPermission(user_id?: string, guild_id?: string, channel channel: { overwrites: channel?.permission_overwrites, owner_id: channel?.owner_id, - recipient_ids: channel?.recipient_ids, + recipient_ids: channel?.recipients?.map((x) => x.id), }, }); -- cgit 1.5.1