diff options
Diffstat (limited to 'util/src')
-rw-r--r-- | util/src/entities/BaseClass.ts | 17 | ||||
-rw-r--r-- | util/src/entities/Config.ts | 2 | ||||
-rw-r--r-- | util/src/entities/ReadState.ts | 4 | ||||
-rw-r--r-- | util/src/entities/Template.ts | 7 | ||||
-rw-r--r-- | util/src/entities/User.ts | 4 | ||||
-rw-r--r-- | util/src/entities/schema.json | 543 | ||||
-rw-r--r-- | util/src/util/Config.ts | 19 | ||||
-rw-r--r-- | util/src/util/Database.ts | 14 | ||||
-rw-r--r-- | util/src/util/checkToken.ts | 7 | ||||
-rw-r--r-- | util/src/util/index.ts | 10 | ||||
-rw-r--r-- | util/src/util/toBigInt.ts | 4 |
11 files changed, 583 insertions, 48 deletions
diff --git a/util/src/entities/BaseClass.ts b/util/src/entities/BaseClass.ts index 2d2457de..bb6ccea1 100644 --- a/util/src/entities/BaseClass.ts +++ b/util/src/entities/BaseClass.ts @@ -3,6 +3,10 @@ import { BaseEntity, BeforeInsert, BeforeUpdate, PrimaryColumn } from "typeorm"; import { Snowflake } from "../util/Snowflake"; import Ajv, { ValidateFunction } from "ajv"; import schema from "./schema.json"; +import "missing-native-js-functions"; + +// TODO use class-validator https://typeorm.io/#/validation with class annotators (isPhone/isEmail) combined with types from typescript-json-schema +// btw. we don't use class-validator for everything, because we need to explicitly set the type instead of deriving it from typescript also it doesn't easily support nested objects const ajv = new Ajv({ removeAdditional: "all", @@ -12,7 +16,6 @@ const ajv = new Ajv({ validateFormats: false, allowUnionTypes: true, }); -// const validator = ajv.compile<BaseClass>(schema); export class BaseClass extends BaseEntity { @PrimaryColumn() @@ -43,10 +46,20 @@ export class BaseClass extends BaseEntity { delete props.opts; + const properties = new Set(this.metadata.columns.map((x: any) => x.propertyName)); + // will not include relational properties (e.g. @RelationId @ManyToMany) + for (const key in props) { if (this.hasOwnProperty(key)) continue; + if (!properties.has(key)) continue; + // @ts-ignore + const setter = this[`set${key.capitalize()}`]; - Object.defineProperty(this, key, { value: props[key] }); + if (setter) { + setter.call(this, props[key]); + } else { + Object.defineProperty(this, key, { value: props[key] }); + } } } diff --git a/util/src/entities/Config.ts b/util/src/entities/Config.ts index 60d84958..c9326be2 100644 --- a/util/src/entities/Config.ts +++ b/util/src/entities/Config.ts @@ -1,7 +1,7 @@ import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; -import { Snowflake } from "../util"; import { BaseClass } from "./BaseClass"; import crypto from "crypto"; +import { Snowflake } from "../util/Snowflake"; @Entity("config") export class ConfigEntity extends BaseClass { diff --git a/util/src/entities/ReadState.ts b/util/src/entities/ReadState.ts index 0310cb5f..70a38b84 100644 --- a/util/src/entities/ReadState.ts +++ b/util/src/entities/ReadState.ts @@ -4,6 +4,10 @@ import { Channel } from "./Channel"; import { Message } from "./Message"; import { User } from "./User"; +// for read receipts +// notification cursor and public read receipt need to be forwards-only (the former to prevent re-pinging when marked as unread, and the latter to be acceptable as a legal acknowledgement in criminal proceedings), and private read marker needs to be advance-rewind capable +// public read receipt ≥ notification cursor ≥ private fully read marker + @Entity("read_states") export class ReadState extends BaseClass { @RelationId((read_state: ReadState) => read_state.channel) diff --git a/util/src/entities/Template.ts b/util/src/entities/Template.ts index c8d2034c..1dfc3b1b 100644 --- a/util/src/entities/Template.ts +++ b/util/src/entities/Template.ts @@ -1,11 +1,11 @@ -import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, PrimaryColumn, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { User } from "./User"; @Entity("templates") export class Template extends BaseClass { - @Column() + @PrimaryColumn() code: string; @Column() @@ -36,4 +36,7 @@ export class Template extends BaseClass { @JoinColumn({ name: "source_guild_id" }) @ManyToOne(() => Guild, (guild: Guild) => guild.id) source_guild: Guild; + + @Column("simple-json") + serialized_source_guild: Guild; } diff --git a/util/src/entities/User.ts b/util/src/entities/User.ts index bdf0d35f..03cb3af4 100644 --- a/util/src/entities/User.ts +++ b/util/src/entities/User.ts @@ -29,8 +29,8 @@ export class User extends BaseClass { 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; + if (number <= 0 || number > 10000) throw new Error("discriminator must be between 1 and 9999"); + this.discriminator = val.toString(); } @Column() diff --git a/util/src/entities/schema.json b/util/src/entities/schema.json index ebf66e88..7f192690 100644 --- a/util/src/entities/schema.json +++ b/util/src/entities/schema.json @@ -1198,6 +1198,380 @@ }, "type": "object" }, + "ConfigEntity": { + "properties": { + "construct": { + }, + "id": { + "type": "string" + }, + "metadata": { + }, + "opts": { + "properties": { + "id": { + "type": "string" + } + }, + "type": "object" + }, + "value": { + "$ref": "#/definitions/ConfigValue" + } + }, + "type": "object" + }, + "ConfigValue": { + "properties": { + "cdn": { + "properties": { + "endpoint": { + "type": [ + "null", + "string" + ] + }, + "endpointClient": { + "type": [ + "null", + "string" + ] + } + }, + "type": "object" + }, + "gateway": { + "properties": { + "endpoint": { + "type": [ + "null", + "string" + ] + }, + "endpointClient": { + "type": [ + "null", + "string" + ] + } + }, + "type": "object" + }, + "general": { + "properties": { + "instance_id": { + "type": "string" + } + }, + "type": "object" + }, + "kafka": { + "properties": { + "brokers": { + "anyOf": [ + { + "items": { + "$ref": "#/definitions/KafkaBroker" + }, + "type": "array" + }, + { + "type": "null" + } + ] + } + }, + "type": "object" + }, + "limits": { + "properties": { + "channel": { + "properties": { + "maxPins": { + "type": "number" + }, + "maxTopic": { + "type": "number" + } + }, + "type": "object" + }, + "guild": { + "properties": { + "hideOfflineMember": { + "type": "number" + }, + "maxChannels": { + "type": "number" + }, + "maxChannelsInCategory": { + "type": "number" + }, + "maxMembers": { + "type": "number" + }, + "maxRoles": { + "type": "number" + } + }, + "type": "object" + }, + "message": { + "properties": { + "maxAttachmentSize": { + "type": "number" + }, + "maxBulkDelete": { + "type": "number" + }, + "maxCharacters": { + "type": "number" + }, + "maxReactions": { + "type": "number" + }, + "maxTTSCharacters": { + "type": "number" + } + }, + "type": "object" + }, + "rate": { + "properties": { + "error": { + "$ref": "#/definitions/RateLimitOptions" + }, + "global": { + "$ref": "#/definitions/RateLimitOptions" + }, + "ip": { + "$ref": "#/definitions/Omit<RateLimitOptions,\"bot_count\">" + }, + "routes": { + "properties": { + "auth": { + "properties": { + "login": { + "$ref": "#/definitions/RateLimitOptions" + }, + "register": { + "$ref": "#/definitions/RateLimitOptions" + } + }, + "type": "object" + }, + "channel": { + "$ref": "#/definitions/RateLimitOptions" + }, + "guild": { + "$ref": "#/definitions/RateLimitOptions" + }, + "webhook": { + "$ref": "#/definitions/RateLimitOptions" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "user": { + "properties": { + "maxFriends": { + "type": "number" + }, + "maxGuilds": { + "type": "number" + }, + "maxUsername": { + "type": "number" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "login": { + "properties": { + "requireCaptcha": { + "type": "boolean" + } + }, + "type": "object" + }, + "permissions": { + "properties": { + "user": { + "properties": { + "createGuilds": { + "type": "boolean" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "rabbitmq": { + "properties": { + "host": { + "type": [ + "null", + "string" + ] + } + }, + "type": "object" + }, + "regions": { + "properties": { + "available": { + "items": { + "$ref": "#/definitions/Region" + }, + "type": "array" + }, + "default": { + "type": "string" + } + }, + "type": "object" + }, + "register": { + "properties": { + "allowMultipleAccounts": { + "type": "boolean" + }, + "allowNewRegistration": { + "type": "boolean" + }, + "blockProxies": { + "type": "boolean" + }, + "dateOfBirth": { + "properties": { + "minimum": { + "type": "number" + }, + "necessary": { + "type": "boolean" + } + }, + "type": "object" + }, + "email": { + "properties": { + "allowlist": { + "type": "boolean" + }, + "blocklist": { + "type": "boolean" + }, + "domains": { + "items": { + "type": "string" + }, + "type": "array" + }, + "necessary": { + "type": "boolean" + } + }, + "type": "object" + }, + "password": { + "properties": { + "minLength": { + "type": "number" + }, + "minNumbers": { + "type": "number" + }, + "minSymbols": { + "type": "number" + }, + "minUpperCase": { + "type": "number" + } + }, + "type": "object" + }, + "requireCaptcha": { + "type": "boolean" + }, + "requireInvite": { + "type": "boolean" + } + }, + "type": "object" + }, + "security": { + "properties": { + "autoUpdate": { + "type": [ + "number", + "boolean" + ] + }, + "captcha": { + "properties": { + "enabled": { + "type": "boolean" + }, + "secret": { + "type": [ + "null", + "string" + ] + }, + "service": { + "anyOf": [ + { + "enum": [ + "hcaptcha", + "recaptcha" + ], + "type": "string" + }, + { + "type": "null" + } + ] + }, + "sitekey": { + "type": [ + "null", + "string" + ] + } + }, + "type": "object" + }, + "forwadedFor": { + "type": [ + "null", + "string" + ] + }, + "ipdataApiKey": { + "type": [ + "null", + "string" + ] + }, + "jwtSecret": { + "type": "string" + }, + "requestSignature": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + }, "ConnectedAccount": { "properties": { "access_token": { @@ -1721,6 +2095,9 @@ "public_updates_channel": { "$ref": "#/definitions/Channel" }, + "public_updates_channel_id": { + "type": "string" + }, "region": { "type": "string" }, @@ -1760,6 +2137,9 @@ "vanity_url": { "$ref": "#/definitions/Invite" }, + "vanity_url_code": { + "type": "string" + }, "verification_level": { "type": "number" }, @@ -1809,6 +2189,9 @@ "widget_channel": { "$ref": "#/definitions/Channel" }, + "widget_channel_id": { + "type": "string" + }, "widget_enabled": { "type": "boolean" } @@ -2520,12 +2903,12 @@ "target_user": { "type": "string" }, + "target_user_id": { + "type": "string" + }, "target_user_type": { "type": "number" }, - "target_usser_id": { - "type": "string" - }, "temporary": { "type": "boolean" }, @@ -2615,6 +2998,17 @@ }, "type": "object" }, + "KafkaBroker": { + "properties": { + "ip": { + "type": "string" + }, + "port": { + "type": "number" + } + }, + "type": "object" + }, "ListenEventOpts": { "properties": { "acknowledge": { @@ -3507,12 +3901,12 @@ "target_user": { "type": "string" }, + "target_user_id": { + "type": "string" + }, "target_user_type": { "type": "number" }, - "target_usser_id": { - "type": "string" - }, "temporary": { "type": "boolean" }, @@ -3843,6 +4237,23 @@ }, "type": "object" }, + "Omit<RateLimitOptions,\"bot_count\">": { + "properties": { + "bot": { + "type": "number" + }, + "count": { + "type": "number" + }, + "onyIp": { + "type": "boolean" + }, + "window": { + "type": "number" + } + }, + "type": "object" + }, "Omit<Relationship,\"nickname\">": { "properties": { "assign": { @@ -4322,6 +4733,23 @@ }, "type": "object" }, + "RateLimitOptions": { + "properties": { + "bot": { + "type": "number" + }, + "count": { + "type": "number" + }, + "onyIp": { + "type": "boolean" + }, + "window": { + "type": "number" + } + }, + "type": "object" + }, "Reaction": { "properties": { "count": { @@ -4355,6 +4783,9 @@ "last_message": { "$ref": "#/definitions/Message" }, + "last_message_id": { + "type": "string" + }, "last_pin_timestamp": { "format": "date-time", "type": "string" @@ -4763,6 +5194,29 @@ "Record<string,string|null>": { "type": "object" }, + "Region": { + "properties": { + "custom": { + "type": "boolean" + }, + "deprecated": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "optimal": { + "type": "boolean" + }, + "vip": { + "type": "boolean" + } + }, + "type": "object" + }, "Relationship": { "properties": { "construct": { @@ -5053,6 +5507,9 @@ "creator": { "$ref": "#/definitions/User" }, + "creator_id": { + "type": "string" + }, "description": { "type": "string" }, @@ -5072,9 +5529,15 @@ }, "type": "object" }, + "serialized_source_guild": { + "$ref": "#/definitions/Guild" + }, "source_guild": { "$ref": "#/definitions/Guild" }, + "source_guild_id": { + "type": "string" + }, "updated_at": { "format": "date-time", "type": "string" @@ -5164,6 +5627,18 @@ "format": "date-time", "type": "string" }, + "data": { + "properties": { + "hash": { + "type": "string" + }, + "valid_tokens_since": { + "format": "date-time", + "type": "string" + } + }, + "type": "object" + }, "deleted": { "type": "boolean" }, @@ -5179,15 +5654,27 @@ "email": { "type": "string" }, + "fingerprints": { + "items": { + "type": "string" + }, + "type": "array" + }, "flags": { "type": "bigint" }, - "guilds": { + "guild_ids": { "items": { "type": "string" }, "type": "array" }, + "guilds": { + "items": { + "$ref": "#/definitions/Guild" + }, + "type": "array" + }, "id": { "type": "string" }, @@ -5240,24 +5727,6 @@ "system": { "type": "boolean" }, - "user_data": { - "properties": { - "fingerprints": { - "items": { - "type": "string" - }, - "type": "array" - }, - "hash": { - "type": "string" - }, - "valid_tokens_since": { - "format": "date-time", - "type": "string" - } - }, - "type": "object" - }, "username": { "type": "string" }, @@ -5544,6 +6013,9 @@ "channel": { "$ref": "#/definitions/Channel" }, + "channel_id": { + "type": "string" + }, "construct": { }, "deaf": { @@ -5552,6 +6024,9 @@ "guild": { "$ref": "#/definitions/Guild" }, + "guild_id": { + "type": "string" + }, "id": { "type": "string" }, @@ -5588,6 +6063,9 @@ }, "user": { "$ref": "#/definitions/User" + }, + "user_id": { + "type": "string" } }, "type": "object" @@ -5634,17 +6112,26 @@ "Webhook": { "properties": { "application": { + "$ref": "#/definitions/Application" + }, + "application_id": { "type": "string" }, "avatar": { "type": "string" }, "channel": { + "$ref": "#/definitions/Channel" + }, + "channel_id": { "type": "string" }, "construct": { }, "guild": { + "$ref": "#/definitions/Guild" + }, + "guild_id": { "type": "string" }, "id": { @@ -5664,6 +6151,9 @@ "type": "object" }, "source_guild": { + "$ref": "#/definitions/Guild" + }, + "source_guild_id": { "type": "string" }, "token": { @@ -5673,6 +6163,9 @@ "$ref": "#/definitions/WebhookType" }, "user": { + "$ref": "#/definitions/User" + }, + "user_id": { "type": "string" } }, diff --git a/util/src/util/Config.ts b/util/src/util/Config.ts new file mode 100644 index 00000000..f8574f38 --- /dev/null +++ b/util/src/util/Config.ts @@ -0,0 +1,19 @@ +import "missing-native-js-functions"; +import { ConfigValue, ConfigEntity, DefaultConfigOptions } from "../entities/Config"; + +var config: ConfigEntity; +// TODO: use events to inform about config updates + +export const Config = { + init: async function init() { + config = new ConfigEntity({}, { id: "0" }); + return this.set((config.value || {}).merge(DefaultConfigOptions)); + }, + get: function get() { + return config.value as ConfigValue; + }, + set: function set(val: any) { + config.value = val.merge(config.value); + return config.save(); + }, +}; diff --git a/util/src/util/Database.ts b/util/src/util/Database.ts index 90ba9079..8bb8078e 100644 --- a/util/src/util/Database.ts +++ b/util/src/util/Database.ts @@ -1,11 +1,12 @@ import "reflect-metadata"; -import { createConnection } from "typeorm"; +import { Connection, createConnection } from "typeorm"; import * as Models from "../entities"; // UUID extension option is only supported with postgres // We want to generate all id's with Snowflakes that's why we have our own BaseEntity class var promise: Promise<any>; +var dbConnection: Connection | undefined; export function initDatabase() { if (promise) return promise; // prevent initalizing multiple times @@ -20,7 +21,16 @@ export function initDatabase() { logging: false, }); - promise.then(() => console.log("[Database] connected")); + promise.then((connection) => { + dbConnection = connection; + console.log("[Database] connected"); + }); return promise; } + +export { dbConnection }; + +export function closeDatabase() { + dbConnection?.close(); +} diff --git a/util/src/util/checkToken.ts b/util/src/util/checkToken.ts index 58e537ea..1e203006 100644 --- a/util/src/util/checkToken.ts +++ b/util/src/util/checkToken.ts @@ -9,13 +9,10 @@ export function checkToken(token: string, jwtSecret: string): Promise<any> { jwt.verify(token, jwtSecret, JWTOptions, async (err, decoded: any) => { if (err || !decoded) return rej("Invalid Token"); - const user = await User.findOne( - { id: decoded.id }, - { select: ["user_data", "bot", "disabled", "deleted"] } - ); + 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.user_data.valid_tokens_since.setSeconds(0, 0)) return rej("Invalid Token"); + if (decoded.iat * 1000 < 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"); diff --git a/util/src/util/index.ts b/util/src/util/index.ts index 700ecfa5..16b98ca3 100644 --- a/util/src/util/index.ts +++ b/util/src/util/index.ts @@ -1,13 +1,13 @@ export * from "./Database"; -export * from "./Regex"; -export * from "./String"; export * from "./BitField"; +export * from "./Config"; +export * from "./checkToken"; +export * from "./Event"; export * from "./Intents"; export * from "./MessageFlags"; export * from "./Permissions"; export * from "./Snowflake"; -export * from "./toBigInt"; export * from "./RabbitMQ"; -export * from "./Event"; -export * from "./checkToken"; +export * from "./Regex"; +export * from "./String"; diff --git a/util/src/util/toBigInt.ts b/util/src/util/toBigInt.ts deleted file mode 100644 index b7985928..00000000 --- a/util/src/util/toBigInt.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default function toBigInt(string: string): bigint { - return BigInt(string); -} - |