summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.md41
-rw-r--r--.github/ISSUE_TEMPLATE/feature_request.md20
-rw-r--r--README.md11
-rw-r--r--assets/index.html1
-rw-r--r--assets/schemas.json2730
-rw-r--r--package.json2
-rw-r--r--scripts/db_migrations.js124
-rwxr-xr-xscripts/first_setup.js4
-rw-r--r--scripts/utils/ask.js6
-rw-r--r--src/api/routes/applications/#id/bot/index.ts3
-rw-r--r--src/api/routes/auth/mfa/totp.ts3
-rw-r--r--src/api/routes/auth/verify/view-backup-codes-challenge.ts30
-rw-r--r--src/api/routes/guilds/#guild_id/member-verification.ts14
-rw-r--r--src/api/routes/guilds/#guild_id/members/#member_id/index.ts7
-rw-r--r--src/api/routes/guilds/#guild_id/profile/index.ts30
-rw-r--r--src/api/routes/guilds/#guild_id/welcome_screen.ts2
-rw-r--r--src/api/routes/policies/stats.ts22
-rw-r--r--src/api/routes/users/#id/profile.ts63
-rw-r--r--src/api/routes/users/@me/mfa/codes-verification.ts41
-rw-r--r--src/api/routes/users/@me/mfa/totp/disable.ts3
-rw-r--r--src/api/routes/users/@me/mfa/totp/enable.ts3
-rw-r--r--src/cdn/Server.ts7
-rw-r--r--src/cdn/routes/guild-profiles.ts84
-rw-r--r--src/util/config/types/SecurityConfiguration.ts1
-rw-r--r--src/util/entities/Guild.ts2
-rw-r--r--src/util/entities/Member.ts23
-rw-r--r--src/util/interfaces/Event.ts1
-rw-r--r--src/util/migrations/mariadb/1661273147273-test.ts71
-rw-r--r--src/util/migrations/mariadb/1661273179287-test2.ts15
-rw-r--r--src/util/migrations/mariadb/1661885910534-guild-member-profiles.ts40
-rw-r--r--src/util/migrations/postgres/1661885830688-guild-member-profiles.ts40
-rw-r--r--src/util/migrations/sqlite/1661885742207-guild-member-profiles.ts136
-rw-r--r--src/util/schemas/BackupCodesChallengeSchema.ts3
-rw-r--r--src/util/schemas/CodesVerificationSchema.ts5
-rw-r--r--src/util/schemas/MemberChangeProfileSchema.ts5
-rw-r--r--src/util/schemas/MemberChangeSchema.ts2
-rw-r--r--src/util/schemas/UserProfileModifySchema.ts5
-rw-r--r--src/util/schemas/index.ts6
-rw-r--r--src/util/util/Database.ts2
-rw-r--r--src/util/util/Rights.ts6
40 files changed, 2199 insertions, 1415 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644

index 00000000..4684dec8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,41 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Console Logs** +If applicable, add console logs to help give more information about your problem. + +**System Information (please complete the following information):** + - OS: [e.g. Debian Linux, Arch Linux etc.] + - Version (If not applicable skip): [e.g Ubuntu 22.04 LTS/Windows Server 2022] + - Node Version: [e.g Node v18.7.0] + - Python 3 Version: [e.g Python 3.10.6] + +**Env and Software info** + - Release: [e.g. 0.1.0] + - Branch (if release is not applicable): [e.g Staging] + - Commit Hash (if release is not applicable): [e.g 401eda069a3ced17f1c43294d19765663cb8dcb7] + - Database: [e.g Postgres 14] + - Reverse Proxy: [e.g ngnix] + - Thread Count: [e.g 1] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644
index 00000000..bd1f64b6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an feature for Fosscord Server +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/README.md b/README.md
index f2743ed1..2bce4fa8 100644 --- a/README.md +++ b/README.md
@@ -18,13 +18,10 @@ This repository contains: -- [Fosscord HTTP API Server](/api) -- [WebSocket Gateway Server](/gateway) -- [HTTP CDN Server](/cdn) -- [Utility and Database Models](/util) -- [RTC Server](/rtc) -- [WebRTC Server](/webrtc) -- [Admin Dashboard](/dashboard) +- [Fosscord HTTP API Server](/src/api) +- [WebSocket Gateway Server](/src/gateway) +- [HTTP CDN Server](/src/cdn) +- [Utility and Database Models](/src/util) ## [Resources](https://docs.fosscord.com/resources/) diff --git a/assets/index.html b/assets/index.html
index 1d4e4543..f73fe99f 100644 --- a/assets/index.html +++ b/assets/index.html
@@ -72,6 +72,7 @@ localStorage.setItem("UserSettingsStore", JSON.stringify(settings)); } </script> + <script src="/assets/checkLocale.js"></script> <script src="/assets/2f2e0c25e45eb2f5a6f1.js"></script> <script src="/assets/006e72c08a4c69cb66fc.js"></script> diff --git a/assets/schemas.json b/assets/schemas.json
index 05650a4e..f3d96959 100644 --- a/assets/schemas.json +++ b/assets/schemas.json
@@ -1,1274 +1,1458 @@ { - "ActivitySchema": { - "type": "object", - "properties": { - "afk": { - "type": "boolean" - }, - "status": {}, - "activities": { - "type": "array", - "items": {} - }, - "since": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": ["afk", "status"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "BanCreateSchema": { - "type": "object", - "properties": { - "delete_message_days": { - "type": "string" - }, - "reason": { - "type": "string" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "BanModeratorSchema": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "user_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "executor_id": { - "type": "string" - }, - "reason": { - "type": "string" - } - }, - "additionalProperties": false, - "required": ["executor_id", "guild_id", "id", "user_id"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "BanRegistrySchema": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "user_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "executor_id": { - "type": "string" - }, - "ip": { - "type": "string" - }, - "reason": { - "type": "string" - } - }, - "additionalProperties": false, - "required": ["executor_id", "guild_id", "id", "user_id"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "BulkDeleteSchema": { - "type": "object", - "properties": { - "messages": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false, - "required": ["messages"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ts.server.TypingInstallerResponse": { - "type": "object", - "properties": { - "kind": { - "enum": [ - "action::invalidate", - "action::packageInstalled", - "action::set", - "event::beginInstallTypes", - "event::endInstallTypes", - "event::initializationFailed", - "event::typesRegistry" - ], - "type": "string" - } - }, - "additionalProperties": false, - "required": ["kind"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ts.server.PackageInstalledResponse": { - "type": "object", - "properties": { - "kind": { - "type": "string", - "enum": ["action::packageInstalled"] - }, - "success": { - "type": "boolean" - }, - "message": { - "type": "string" - }, - "projectName": { - "type": "string" - } - }, - "additionalProperties": false, - "required": ["kind", "message", "projectName", "success"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ts.server.InitializationFailedResponse": { - "type": "object", - "properties": { - "kind": { - "type": "string", - "enum": ["event::initializationFailed"] - }, - "message": { - "type": "string" - }, - "stack": { - "type": "string" - } - }, - "additionalProperties": false, - "required": ["kind", "message"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ts.server.ProjectResponse": { - "type": "object", - "properties": { - "projectName": { - "type": "string" - }, - "kind": { - "enum": [ - "action::invalidate", - "action::packageInstalled", - "action::set", - "event::beginInstallTypes", - "event::endInstallTypes", - "event::initializationFailed", - "event::typesRegistry" - ], - "type": "string" - } - }, - "additionalProperties": false, - "required": ["kind", "projectName"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ChannelPermissionOverwriteSchema": { - "type": "object", - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ChannelReorderSchema": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "position": { - "type": "integer" - }, - "lock_permissions": { - "type": "boolean" - }, - "parent_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": ["id"] - }, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "DmChannelCreateSchema": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "recipients": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false, - "required": ["recipients"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "EmojiCreateSchema": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "image": { - "type": "string" - }, - "require_colons": { - "type": ["null", "boolean"] - }, - "roles": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false, - "required": ["image"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "EmojiModifySchema": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "roles": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildCreateSchema": { - "type": "object", - "properties": { - "name": { - "maxLength": 100, - "type": "string" - }, - "region": { - "type": "string" - }, - "icon": { - "type": ["null", "string"] - }, - "channels": { - "type": "array", - "items": { - "$ref": "#/definitions/ChannelModifySchema" - } - }, - "guild_template_code": { - "type": "string" - }, - "system_channel_id": { - "type": "string" - }, - "rules_channel_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": ["name"], - "definitions": { - "ChannelModifySchema": { - "type": "object", - "properties": { - "name": { - "maxLength": 100, - "type": "string" - }, - "type": { - "enum": [0, 1, 10, 11, 12, 13, 14, 15, 2, 255, 3, 33, 34, 35, 4, 5, 6, 64, 7, 8, 9], - "type": "number" - }, - "topic": { - "type": "string" - }, - "icon": { - "type": ["null", "string"] - }, - "bitrate": { - "type": "integer" - }, - "user_limit": { - "type": "integer" - }, - "rate_limit_per_user": { - "type": "integer" - }, - "position": { - "type": "integer" - }, - "permission_overwrites": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "$ref": "#/definitions/ChannelPermissionOverwriteType" - }, - "allow": { - "type": "string" - }, - "deny": { - "type": "string" - } - }, - "additionalProperties": false, - "required": ["allow", "deny", "id", "type"] - } - }, - "parent_id": { - "type": "string" - }, - "id": { - "type": "string" - }, - "nsfw": { - "type": "boolean" - }, - "rtc_region": { - "type": "string" - }, - "default_auto_archive_duration": { - "type": "integer" - }, - "flags": { - "type": "integer" - }, - "default_thread_rate_limit_per_user": { - "type": "integer" - } - }, - "additionalProperties": false - }, - "ChannelPermissionOverwriteType": { - "enum": [0, 1, 2], - "type": "number" - } - }, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildTemplateCreateSchema": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "avatar": { - "type": ["null", "string"] - } - }, - "additionalProperties": false, - "required": ["name"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildUpdateSchema": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "banner": { - "type": ["null", "string"] - }, - "splash": { - "type": ["null", "string"] - }, - "description": { - "type": "string" - }, - "features": { - "type": "array", - "items": { - "type": "string" - } - }, - "verification_level": { - "type": "integer" - }, - "default_message_notifications": { - "type": "integer" - }, - "system_channel_flags": { - "type": "integer" - }, - "explicit_content_filter": { - "type": "integer" - }, - "public_updates_channel_id": { - "type": "string" - }, - "afk_timeout": { - "type": "integer" - }, - "afk_channel_id": { - "type": "string" - }, - "preferred_locale": { - "type": "string" - }, - "premium_progress_bar_enabled": { - "type": "boolean" - }, - "region": { - "type": "string" - }, - "icon": { - "type": ["null", "string"] - }, - "guild_template_code": { - "type": "string" - }, - "system_channel_id": { - "type": "string" - }, - "rules_channel_id": { - "type": "string" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildUpdateWelcomeScreenSchema": { - "type": "object", - "properties": { - "welcome_channels": { - "type": "array", - "items": { - "type": "object", - "properties": { - "channel_id": { - "type": "string" - }, - "description": { - "type": "string" - }, - "emoji_id": { - "type": "string" - }, - "emoji_name": { - "type": "string" - } - }, - "additionalProperties": false, - "required": ["channel_id", "description", "emoji_name"] - } - }, - "enabled": { - "type": "boolean" - }, - "description": { - "type": "string" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "IdentifySchema": { - "type": "object", - "properties": { - "token": { - "type": "string" - }, - "properties": { - "type": "object", - "properties": { - "os": { - "type": "string" - }, - "os_atch": { - "type": "string" - }, - "browser": { - "type": "string" - }, - "device": { - "type": "string" - }, - "$os": { - "type": "string" - }, - "$browser": { - "type": "string" - }, - "$device": { - "type": "string" - }, - "browser_user_agent": { - "type": "string" - }, - "browser_version": { - "type": "string" - }, - "os_version": { - "type": "string" - }, - "referrer": { - "type": "string" - }, - "referring_domain": { - "type": "string" - }, - "referrer_current": { - "type": "string" - }, - "referring_domain_current": { - "type": "string" - }, - "release_channel": { - "enum": ["canary", "dev", "ptb", "stable"], - "type": "string" - }, - "client_build_number": { - "type": "integer" - }, - "client_event_source": {}, - "client_version": { - "type": "string" - }, - "system_locale": { - "type": "string" - } - }, - "additionalProperties": false - }, - "intents": { - "type": "string" - }, - "presence": { - "$ref": "#/definitions/ActivitySchema" - }, - "compress": { - "type": "boolean" - }, - "large_threshold": { - "type": "integer" - }, - "shard": { - "type": "array", - "items": [ - { - "type": "integer" - }, - { - "type": "integer" - } - ], - "minItems": 2, - "maxItems": 2 - }, - "guild_subscriptions": { - "type": "boolean" - }, - "capabilities": { - "type": "integer" - }, - "client_state": { - "type": "object", - "properties": { - "guild_hashes": {}, - "highest_last_message_id": { - "type": "string" - }, - "read_state_version": { - "type": "integer" - }, - "user_guild_settings_version": { - "type": "integer" - }, - "user_settings_version": { - "type": "integer" - } - }, - "additionalProperties": false - }, - "v": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": ["properties", "token"], - "definitions": { - "ActivitySchema": { - "type": "object", - "properties": { - "afk": { - "type": "boolean" - }, - "status": {}, - "activities": { - "type": "array", - "items": {} - }, - "since": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": ["afk", "status"] - } - }, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "InviteCreateSchema": { - "type": "object", - "properties": { - "target_user_id": { - "type": "string" - }, - "target_type": { - "type": "string" - }, - "validate": { - "type": "string" - }, - "max_age": { - "type": "integer" - }, - "max_uses": { - "type": "integer" - }, - "temporary": { - "type": "boolean" - }, - "unique": { - "type": "boolean" - }, - "target_user": { - "type": "string" - }, - "target_user_type": { - "type": "integer" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "LoginSchema": { - "type": "object", - "properties": { - "login": { - "type": "string" - }, - "password": { - "type": "string" - }, - "undelete": { - "type": "boolean" - }, - "captcha_key": { - "type": "string" - }, - "login_source": { - "type": "string" - }, - "gift_code_sku_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": ["login", "password"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "MemberChangeSchema": { - "type": "object", - "properties": { - "roles": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "MemberNickChangeSchema": { - "type": "object", - "properties": { - "nick": { - "type": "string" - } - }, - "additionalProperties": false, - "required": ["nick"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "MessageAcknowledgeSchema": { - "type": "object", - "properties": { - "manual": { - "type": "boolean" - }, - "mention_count": { - "type": "integer" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "MessageCreateSchema": { - "type": "object", - "properties": { - "type": { - "type": "integer" - }, - "content": { - "type": "string" - }, - "nonce": { - "type": "string" - }, - "channel_id": { - "type": "string" - }, - "tts": { - "type": "boolean" - }, - "flags": { - "type": "string" - }, - "embeds": { - "type": "array", - "items": {} - }, - "embed": {}, - "allowed_mentions": { - "type": "object", - "properties": { - "parse": { - "type": "array", - "items": { - "type": "string" - } - }, - "roles": { - "type": "array", - "items": { - "type": "string" - } - }, - "users": { - "type": "array", - "items": { - "type": "string" - } - }, - "replied_user": { - "type": "boolean" - } - }, - "additionalProperties": false - }, - "message_reference": { - "type": "object", - "properties": { - "message_id": { - "type": "string" - }, - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "fail_if_not_exists": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": ["channel_id", "message_id"] - }, - "payload_json": { - "type": "string" - }, - "file": {}, - "attachments": { - "description": "TODO: we should create an interface for attachments\nTODO: OpenWAAO<-->attachment-style metadata conversion", - "type": "array", - "items": {} - }, - "sticker_ids": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "MfaCodesSchema": { - "type": "object", - "properties": { - "password": { - "type": "string" - }, - "regenerate": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": ["password"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ModifyGuildStickerSchema": { - "type": "object", - "properties": { - "name": { - "minLength": 2, - "maxLength": 30, - "type": "string" - }, - "description": { - "maxLength": 100, - "type": "string" - }, - "tags": { - "maxLength": 200, - "type": "string" - } - }, - "additionalProperties": false, - "required": ["name", "tags"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "PruneSchema": { - "type": "object", - "properties": { - "days": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": ["days"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "PurgeSchema": { - "type": "object", - "properties": { - "before": { - "type": "string" - }, - "after": { - "type": "string" - } - }, - "additionalProperties": false, - "required": ["after", "before"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "RegisterSchema": { - "type": "object", - "properties": { - "username": { - "minLength": 2, - "maxLength": 32, - "type": "string" - }, - "password": { - "minLength": 1, - "maxLength": 72, - "type": "string" - }, - "consent": { - "type": "boolean" - }, - "email": { - "format": "email", - "type": "string" - }, - "fingerprint": { - "type": "string" - }, - "invite": { - "type": "string" - }, - "date_of_birth": { - "type": "string" - }, - "gift_code_sku_id": { - "type": "string" - }, - "captcha_key": { - "type": "string" - }, - "promotional_email_opt_in": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": ["consent", "username"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "RelationshipPostSchema": { - "type": "object", - "properties": { - "discriminator": { - "type": "string" - }, - "username": { - "type": "string" - } - }, - "additionalProperties": false, - "required": ["discriminator", "username"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "RelationshipPutSchema": { - "type": "object", - "properties": { - "type": {} - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "RoleModifySchema": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "permissions": { - "type": "string" - }, - "color": { - "type": "integer" - }, - "hoist": { - "type": "boolean" - }, - "mentionable": { - "type": "boolean" - }, - "position": { - "type": "integer" - }, - "icon": { - "type": "string" - }, - "unicode_emoji": { - "type": "string" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "RolePositionUpdateSchema": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "position": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": ["id", "position"] - }, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "TemplateCreateSchema": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - } - }, - "additionalProperties": false, - "required": ["name"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "TemplateModifySchema": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - } - }, - "additionalProperties": false, - "required": ["name"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "TotpDisableSchema": { - "type": "object", - "properties": { - "code": { - "type": "string" - } - }, - "additionalProperties": false, - "required": ["code"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "TotpEnableSchema": { - "type": "object", - "properties": { - "password": { - "type": "string" - }, - "code": { - "type": "string" - }, - "secret": { - "type": "string" - } - }, - "additionalProperties": false, - "required": ["password"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "TotpSchema": { - "type": "object", - "properties": { - "code": { - "type": "string" - }, - "ticket": { - "type": "string" - }, - "gift_code_sku_id": { - "type": ["null", "string"] - }, - "login_source": { - "type": ["null", "string"] - } - }, - "additionalProperties": false, - "required": ["code", "ticket"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "UserModifySchema": { - "type": "object", - "properties": { - "username": { - "minLength": 1, - "maxLength": 100, - "type": "string" - }, - "discriminator": { - "type": "string" - }, - "avatar": { - "type": ["null", "string"] - }, - "bio": { - "maxLength": 1024, - "type": "string" - }, - "accent_color": { - "type": "integer" - }, - "banner": { - "type": ["null", "string"] - }, - "password": { - "type": "string" - }, - "new_password": { - "type": "string" - }, - "code": { - "type": "string" - }, - "email": { - "type": "string" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "UserSettingsSchema": { - "type": "object", - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "VanityUrlSchema": { - "type": "object", - "properties": { - "code": { - "minLength": 1, - "maxLength": 20, - "type": "string" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "VoiceStateUpdateSchema": { - "type": "object", - "properties": { - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "suppress": { - "type": "boolean" - }, - "request_to_speak_timestamp": { - "type": "string", - "format": "date-time" - }, - "self_mute": { - "type": "boolean" - }, - "self_deaf": { - "type": "boolean" - }, - "self_video": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": ["channel_id"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "WebhookCreateSchema": { - "type": "object", - "properties": { - "name": { - "maxLength": 80, - "type": "string" - }, - "avatar": { - "type": "string" - } - }, - "additionalProperties": false, - "required": ["name"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "WidgetModifySchema": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "channel_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": ["channel_id", "enabled"], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ChannelModifySchema": { - "type": "object", - "properties": { - "name": { - "maxLength": 100, - "type": "string" - }, - "type": { - "enum": [0, 1, 10, 11, 12, 13, 14, 15, 2, 255, 3, 33, 34, 35, 4, 5, 6, 64, 7, 8, 9], - "type": "number" - }, - "topic": { - "type": "string" - }, - "icon": { - "type": ["null", "string"] - }, - "bitrate": { - "type": "integer" - }, - "user_limit": { - "type": "integer" - }, - "rate_limit_per_user": { - "type": "integer" - }, - "position": { - "type": "integer" - }, - "permission_overwrites": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "$ref": "#/definitions/ChannelPermissionOverwriteType" - }, - "allow": { - "type": "string" - }, - "deny": { - "type": "string" - } - }, - "additionalProperties": false, - "required": ["allow", "deny", "id", "type"] - } - }, - "parent_id": { - "type": "string" - }, - "id": { - "type": "string" - }, - "nsfw": { - "type": "boolean" - }, - "rtc_region": { - "type": "string" - }, - "default_auto_archive_duration": { - "type": "integer" - }, - "flags": { - "type": "integer" - }, - "default_thread_rate_limit_per_user": { - "type": "integer" - } - }, - "additionalProperties": false, - "definitions": { - "ChannelPermissionOverwriteType": { - "enum": [0, 1, 2], - "type": "number" - } - }, - "$schema": "http://json-schema.org/draft-07/schema#" - } -} + "ActivitySchema": { + "type": "object", + "properties": { + "afk": { + "type": "boolean" + }, + "status": {}, + "activities": { + "type": "array", + "items": {} + }, + "since": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "afk", + "status" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "BackupCodesChallengeSchema": { + "type": "object", + "properties": { + "password": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "password" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "BanCreateSchema": { + "type": "object", + "properties": { + "delete_message_days": { + "type": "string" + }, + "reason": { + "type": "string" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "BanModeratorSchema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "user_id": { + "type": "string" + }, + "guild_id": { + "type": "string" + }, + "executor_id": { + "type": "string" + }, + "reason": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "executor_id", + "guild_id", + "id", + "user_id" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "BanRegistrySchema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "user_id": { + "type": "string" + }, + "guild_id": { + "type": "string" + }, + "executor_id": { + "type": "string" + }, + "ip": { + "type": "string" + }, + "reason": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "executor_id", + "guild_id", + "id", + "user_id" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "BulkDeleteSchema": { + "type": "object", + "properties": { + "messages": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "required": [ + "messages" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ChannelPermissionOverwriteSchema": { + "type": "object", + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ChannelReorderSchema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "position": { + "type": "integer" + }, + "lock_permissions": { + "type": "boolean" + }, + "parent_id": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "id" + ] + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "CodesVerificationSchema": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "nonce": { + "type": "string" + }, + "regenerate": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "key", + "nonce" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "DmChannelCreateSchema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "recipients": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "required": [ + "recipients" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "EmojiCreateSchema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "image": { + "type": "string" + }, + "require_colons": { + "type": [ + "null", + "boolean" + ] + }, + "roles": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "required": [ + "image" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "EmojiModifySchema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "roles": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "GuildCreateSchema": { + "type": "object", + "properties": { + "name": { + "maxLength": 100, + "type": "string" + }, + "region": { + "type": "string" + }, + "icon": { + "type": [ + "null", + "string" + ] + }, + "channels": { + "type": "array", + "items": { + "$ref": "#/definitions/ChannelModifySchema" + } + }, + "guild_template_code": { + "type": "string" + }, + "system_channel_id": { + "type": "string" + }, + "rules_channel_id": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "name" + ], + "definitions": { + "ChannelModifySchema": { + "type": "object", + "properties": { + "name": { + "maxLength": 100, + "type": "string" + }, + "type": { + "enum": [ + 0, + 1, + 10, + 11, + 12, + 13, + 14, + 15, + 2, + 255, + 3, + 33, + 34, + 35, + 4, + 5, + 6, + 64, + 7, + 8, + 9 + ], + "type": "number" + }, + "topic": { + "type": "string" + }, + "icon": { + "type": [ + "null", + "string" + ] + }, + "bitrate": { + "type": "integer" + }, + "user_limit": { + "type": "integer" + }, + "rate_limit_per_user": { + "type": "integer" + }, + "position": { + "type": "integer" + }, + "permission_overwrites": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/ChannelPermissionOverwriteType" + }, + "allow": { + "type": "string" + }, + "deny": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "allow", + "deny", + "id", + "type" + ] + } + }, + "parent_id": { + "type": "string" + }, + "id": { + "type": "string" + }, + "nsfw": { + "type": "boolean" + }, + "rtc_region": { + "type": "string" + }, + "default_auto_archive_duration": { + "type": "integer" + }, + "flags": { + "type": "integer" + }, + "default_thread_rate_limit_per_user": { + "type": "integer" + } + }, + "additionalProperties": false + }, + "ChannelPermissionOverwriteType": { + "enum": [ + 0, + 1, + 2 + ], + "type": "number" + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "GuildTemplateCreateSchema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "avatar": { + "type": [ + "null", + "string" + ] + } + }, + "additionalProperties": false, + "required": [ + "name" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "GuildUpdateSchema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "banner": { + "type": [ + "null", + "string" + ] + }, + "splash": { + "type": [ + "null", + "string" + ] + }, + "description": { + "type": "string" + }, + "features": { + "type": "array", + "items": { + "type": "string" + } + }, + "verification_level": { + "type": "integer" + }, + "default_message_notifications": { + "type": "integer" + }, + "system_channel_flags": { + "type": "integer" + }, + "explicit_content_filter": { + "type": "integer" + }, + "public_updates_channel_id": { + "type": "string" + }, + "afk_timeout": { + "type": "integer" + }, + "afk_channel_id": { + "type": "string" + }, + "preferred_locale": { + "type": "string" + }, + "premium_progress_bar_enabled": { + "type": "boolean" + }, + "region": { + "type": "string" + }, + "icon": { + "type": [ + "null", + "string" + ] + }, + "guild_template_code": { + "type": "string" + }, + "system_channel_id": { + "type": "string" + }, + "rules_channel_id": { + "type": "string" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "GuildUpdateWelcomeScreenSchema": { + "type": "object", + "properties": { + "welcome_channels": { + "type": "array", + "items": { + "type": "object", + "properties": { + "channel_id": { + "type": "string" + }, + "description": { + "type": "string" + }, + "emoji_id": { + "type": "string" + }, + "emoji_name": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "channel_id", + "description", + "emoji_name" + ] + } + }, + "enabled": { + "type": "boolean" + }, + "description": { + "type": "string" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "IdentifySchema": { + "type": "object", + "properties": { + "token": { + "type": "string" + }, + "properties": { + "type": "object", + "properties": { + "os": { + "type": "string" + }, + "os_atch": { + "type": "string" + }, + "browser": { + "type": "string" + }, + "device": { + "type": "string" + }, + "$os": { + "type": "string" + }, + "$browser": { + "type": "string" + }, + "$device": { + "type": "string" + }, + "browser_user_agent": { + "type": "string" + }, + "browser_version": { + "type": "string" + }, + "os_version": { + "type": "string" + }, + "referrer": { + "type": "string" + }, + "referring_domain": { + "type": "string" + }, + "referrer_current": { + "type": "string" + }, + "referring_domain_current": { + "type": "string" + }, + "release_channel": { + "enum": [ + "canary", + "dev", + "ptb", + "stable" + ], + "type": "string" + }, + "client_build_number": { + "type": "integer" + }, + "client_event_source": {}, + "client_version": { + "type": "string" + }, + "system_locale": { + "type": "string" + } + }, + "additionalProperties": false + }, + "intents": { + "type": "string" + }, + "presence": { + "$ref": "#/definitions/ActivitySchema" + }, + "compress": { + "type": "boolean" + }, + "large_threshold": { + "type": "integer" + }, + "shard": { + "type": "array", + "items": [ + { + "type": "integer" + }, + { + "type": "integer" + } + ], + "minItems": 2, + "maxItems": 2 + }, + "guild_subscriptions": { + "type": "boolean" + }, + "capabilities": { + "type": "integer" + }, + "client_state": { + "type": "object", + "properties": { + "guild_hashes": {}, + "highest_last_message_id": { + "type": "string" + }, + "read_state_version": { + "type": "integer" + }, + "user_guild_settings_version": { + "type": "integer" + }, + "user_settings_version": { + "type": "integer" + } + }, + "additionalProperties": false + }, + "v": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "properties", + "token" + ], + "definitions": { + "ActivitySchema": { + "type": "object", + "properties": { + "afk": { + "type": "boolean" + }, + "status": {}, + "activities": { + "type": "array", + "items": {} + }, + "since": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "afk", + "status" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "InviteCreateSchema": { + "type": "object", + "properties": { + "target_user_id": { + "type": "string" + }, + "target_type": { + "type": "string" + }, + "validate": { + "type": "string" + }, + "max_age": { + "type": "integer" + }, + "max_uses": { + "type": "integer" + }, + "temporary": { + "type": "boolean" + }, + "unique": { + "type": "boolean" + }, + "target_user": { + "type": "string" + }, + "target_user_type": { + "type": "integer" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "LoginSchema": { + "type": "object", + "properties": { + "login": { + "type": "string" + }, + "password": { + "type": "string" + }, + "undelete": { + "type": "boolean" + }, + "captcha_key": { + "type": "string" + }, + "login_source": { + "type": "string" + }, + "gift_code_sku_id": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "login", + "password" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "MemberChangeProfileSchema": { + "type": "object", + "properties": { + "banner": { + "type": [ + "null", + "string" + ] + }, + "nick": { + "type": "string" + }, + "bio": { + "type": "string" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "MemberChangeSchema": { + "type": "object", + "properties": { + "roles": { + "type": "array", + "items": { + "type": "string" + } + }, + "nick": { + "type": "string" + }, + "avatar": { + "type": [ + "null", + "string" + ] + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "MemberNickChangeSchema": { + "type": "object", + "properties": { + "nick": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "nick" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "MessageAcknowledgeSchema": { + "type": "object", + "properties": { + "manual": { + "type": "boolean" + }, + "mention_count": { + "type": "integer" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "MessageCreateSchema": { + "type": "object", + "properties": { + "type": { + "type": "integer" + }, + "content": { + "type": "string" + }, + "nonce": { + "type": "string" + }, + "channel_id": { + "type": "string" + }, + "tts": { + "type": "boolean" + }, + "flags": { + "type": "string" + }, + "embeds": { + "type": "array", + "items": {} + }, + "embed": {}, + "allowed_mentions": { + "type": "object", + "properties": { + "parse": { + "type": "array", + "items": { + "type": "string" + } + }, + "roles": { + "type": "array", + "items": { + "type": "string" + } + }, + "users": { + "type": "array", + "items": { + "type": "string" + } + }, + "replied_user": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "message_reference": { + "type": "object", + "properties": { + "message_id": { + "type": "string" + }, + "channel_id": { + "type": "string" + }, + "guild_id": { + "type": "string" + }, + "fail_if_not_exists": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "channel_id", + "message_id" + ] + }, + "payload_json": { + "type": "string" + }, + "file": {}, + "attachments": { + "description": "TODO: we should create an interface for attachments\nTODO: OpenWAAO<-->attachment-style metadata conversion", + "type": "array", + "items": {} + }, + "sticker_ids": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "MfaCodesSchema": { + "type": "object", + "properties": { + "password": { + "type": "string" + }, + "regenerate": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "password" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ModifyGuildStickerSchema": { + "type": "object", + "properties": { + "name": { + "minLength": 2, + "maxLength": 30, + "type": "string" + }, + "description": { + "maxLength": 100, + "type": "string" + }, + "tags": { + "maxLength": 200, + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "name", + "tags" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "PruneSchema": { + "type": "object", + "properties": { + "days": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "days" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "PurgeSchema": { + "type": "object", + "properties": { + "before": { + "type": "string" + }, + "after": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "after", + "before" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "RegisterSchema": { + "type": "object", + "properties": { + "username": { + "minLength": 2, + "maxLength": 32, + "type": "string" + }, + "password": { + "minLength": 1, + "maxLength": 72, + "type": "string" + }, + "consent": { + "type": "boolean" + }, + "email": { + "format": "email", + "type": "string" + }, + "fingerprint": { + "type": "string" + }, + "invite": { + "type": "string" + }, + "date_of_birth": { + "type": "string" + }, + "gift_code_sku_id": { + "type": "string" + }, + "captcha_key": { + "type": "string" + }, + "promotional_email_opt_in": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "consent", + "username" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "RelationshipPostSchema": { + "type": "object", + "properties": { + "discriminator": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "discriminator", + "username" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "RelationshipPutSchema": { + "type": "object", + "properties": { + "type": {} + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "RoleModifySchema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "color": { + "type": "integer" + }, + "hoist": { + "type": "boolean" + }, + "mentionable": { + "type": "boolean" + }, + "position": { + "type": "integer" + }, + "icon": { + "type": "string" + }, + "unicode_emoji": { + "type": "string" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "RolePositionUpdateSchema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "position": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "id", + "position" + ] + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "TemplateCreateSchema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "name" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "TemplateModifySchema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "name" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "TotpDisableSchema": { + "type": "object", + "properties": { + "code": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "code" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "TotpEnableSchema": { + "type": "object", + "properties": { + "password": { + "type": "string" + }, + "code": { + "type": "string" + }, + "secret": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "password" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "TotpSchema": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "ticket": { + "type": "string" + }, + "gift_code_sku_id": { + "type": [ + "null", + "string" + ] + }, + "login_source": { + "type": [ + "null", + "string" + ] + } + }, + "additionalProperties": false, + "required": [ + "code", + "ticket" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "UserModifySchema": { + "type": "object", + "properties": { + "username": { + "minLength": 1, + "maxLength": 100, + "type": "string" + }, + "discriminator": { + "type": "string" + }, + "avatar": { + "type": [ + "null", + "string" + ] + }, + "bio": { + "maxLength": 1024, + "type": "string" + }, + "accent_color": { + "type": "integer" + }, + "banner": { + "type": [ + "null", + "string" + ] + }, + "password": { + "type": "string" + }, + "new_password": { + "type": "string" + }, + "code": { + "type": "string" + }, + "email": { + "type": "string" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "UserProfileModifySchema": { + "type": "object", + "properties": { + "bio": { + "type": "string" + }, + "accent_color": { + "type": [ + "null", + "integer" + ] + }, + "banner": { + "type": [ + "null", + "string" + ] + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "UserSettingsSchema": { + "type": "object", + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "VanityUrlSchema": { + "type": "object", + "properties": { + "code": { + "minLength": 1, + "maxLength": 20, + "type": "string" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "VoiceStateUpdateSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string" + }, + "guild_id": { + "type": "string" + }, + "suppress": { + "type": "boolean" + }, + "request_to_speak_timestamp": { + "type": "string", + "format": "date-time" + }, + "self_mute": { + "type": "boolean" + }, + "self_deaf": { + "type": "boolean" + }, + "self_video": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "channel_id" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "WebhookCreateSchema": { + "type": "object", + "properties": { + "name": { + "maxLength": 80, + "type": "string" + }, + "avatar": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "name" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "WidgetModifySchema": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "channel_id": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "channel_id", + "enabled" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ChannelModifySchema": { + "type": "object", + "properties": { + "name": { + "maxLength": 100, + "type": "string" + }, + "type": { + "enum": [ + 0, + 1, + 10, + 11, + 12, + 13, + 14, + 15, + 2, + 255, + 3, + 33, + 34, + 35, + 4, + 5, + 6, + 64, + 7, + 8, + 9 + ], + "type": "number" + }, + "topic": { + "type": "string" + }, + "icon": { + "type": [ + "null", + "string" + ] + }, + "bitrate": { + "type": "integer" + }, + "user_limit": { + "type": "integer" + }, + "rate_limit_per_user": { + "type": "integer" + }, + "position": { + "type": "integer" + }, + "permission_overwrites": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/ChannelPermissionOverwriteType" + }, + "allow": { + "type": "string" + }, + "deny": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "allow", + "deny", + "id", + "type" + ] + } + }, + "parent_id": { + "type": "string" + }, + "id": { + "type": "string" + }, + "nsfw": { + "type": "boolean" + }, + "rtc_region": { + "type": "string" + }, + "default_auto_archive_duration": { + "type": "integer" + }, + "flags": { + "type": "integer" + }, + "default_thread_rate_limit_per_user": { + "type": "integer" + } + }, + "additionalProperties": false, + "definitions": { + "ChannelPermissionOverwriteType": { + "enum": [ + 0, + 1, + 2 + ], + "type": "number" + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" + } +} \ No newline at end of file diff --git a/package.json b/package.json
index d8febb96..878c2e2d 100644 --- a/package.json +++ b/package.json
@@ -13,7 +13,7 @@ "depclean": "node scripts/depclean.js", "depcheck": "node scripts/depcheck.js", "tsnode": "npx ts-node --transpile-only -P tsnode.tsconfig.json src/start.ts", - "genschemas": "node scripts/generate_schemas.js", + "genschemas": "node scripts/generate_schema.js", "migrate": "cd ../util/ && npm i && node --require ts-node/register node_modules/typeorm/cli.js -f ../util/ormconfig.json migration:run", "postinstall": "patch-package && npx --yes node-git-hooks" }, diff --git a/scripts/db_migrations.js b/scripts/db_migrations.js
index df5196b1..fe92a532 100644 --- a/scripts/db_migrations.js +++ b/scripts/db_migrations.js
@@ -6,75 +6,77 @@ const { execIn } = require("./utils.js"); const { ask } = require("./utils/ask.js"); async function main() { - let filename; - if(process.argv[2]) filename = process.argv[2]; - else filename = await ask("Please enter the name of your migration: "); - let dbconf; - try { - dbconf = JSON.parse(fs.readFileSync("dbconf.json")); - } catch (e) { - console.log("No dbconf.json found!"); - dbconf = {}; - } + let filename; + if (process.argv[2]) filename = process.argv[2]; + else filename = await ask("Please enter the name of your migration: "); + let dbconf; + try { + dbconf = JSON.parse(fs.readFileSync("dbconf.json")); + } catch (e) { + console.log("No dbconf.json found!"); + dbconf = {}; + } - if(!dbconf["sqlite"]) - dbconf.sqlite = { - conn_str: "migrations.db", - migrations_dir: "sqlite", - package: "sqlite3" - } - if(!dbconf["postgres"] && process.env.FC_DB_POSTGRES) { - console.log("Found FC_DB_POSTGRES environment variable. Using it!"); - dbconf.postgres = { - conn_str: process.env.FC_DB_POSTGRES, - migrations_dir: "postgres", - package: "pg" - } - } - if(!dbconf["mariadb"] && process.env.FC_DB_MARIADB){ - console.log("Found FC_DB_MARIADB environment variable. Using it!"); - dbconf.mariadb = { - conn_str: process.env.FC_DB_MARIADB, - migrations_dir: "mariadb", - package: "mysql2" - } - } - fs.writeFileSync("dbconf.json", JSON.stringify(dbconf, null, 4)); + if (!dbconf["sqlite"]) + dbconf.sqlite = { + conn_str: "migrations.db", + migrations_dir: "sqlite", + package: "sqlite3" + }; + if (!dbconf["postgres"] && process.env.FC_DB_POSTGRES) { + console.log("Found FC_DB_POSTGRES environment variable. Using it!"); + dbconf.postgres = { + conn_str: process.env.FC_DB_POSTGRES, + migrations_dir: "postgres", + package: "pg" + }; + } + if (!dbconf["mariadb"] && process.env.FC_DB_MARIADB) { + console.log("Found FC_DB_MARIADB environment variable. Using it!"); + dbconf.mariadb = { + conn_str: process.env.FC_DB_MARIADB, + migrations_dir: "mariadb", + package: "mysql2" + }; + } + fs.writeFileSync("dbconf.json", JSON.stringify(dbconf, null, 4)); - //build - execIn(`node scripts/build_new.js`, process.cwd(), {stdio: "inherit"}); + //build + execIn(`node scripts/build_new.js`, process.cwd(), { stdio: "inherit" }); - if(fs.existsSync(".env") && !fs.existsSync(".env.bak")) - fs.renameSync(".env", ".env.bak"); - Object.keys(dbconf).forEach((db) => { - console.log(`Applying migrations for ${db}`); - if(!fs.existsSync(path.join("node_modules", dbconf[db].package))) - execIn(`npm i ${dbconf[db].package}`, process.cwd()); - fs.writeFileSync( - `.env`, - `DATABASE=${dbconf[db].conn_str} + if (fs.existsSync(".env") && !fs.existsSync(".env.bak")) fs.renameSync(".env", ".env.bak"); + Object.keys(dbconf).forEach((db) => { + console.log(`Applying migrations for ${db}`); + if (!fs.existsSync(path.join("node_modules", dbconf[db].package))) execIn(`npm i ${dbconf[db].package}`, process.cwd()); + fs.writeFileSync( + `.env`, + `DATABASE=${dbconf[db].conn_str} THREADS=1 DB_MIGRATE=true DB_VERBOSE=true` - ); - execIn(`node dist/start.js`, process.cwd(), {stdio: "inherit"}); - }); + ); + execIn(`node dist/start.js`, process.cwd(), { stdio: "inherit" }); + }); - Object.keys(dbconf).forEach((db) => { - console.log(`Generating new migrations for ${db}`); - fs.writeFileSync( - `.env`, - `DATABASE=${dbconf[db].conn_str} + Object.keys(dbconf).forEach((db) => { + console.log(`Generating new migrations for ${db}`); + fs.writeFileSync( + `.env`, + `DATABASE=${dbconf[db].conn_str} THREADS=1 DB_MIGRATE=true DB_VERBOSE=true` - ); - execIn(`node node_modules/typeorm/cli.js migration:generate "src/util/migrations/${db}/${filename}" -d dist/util/util/Database.js -p`, process.cwd(), {stdio: "inherit"}); - }); - if(fs.existsSync(".env.bak")) { - fs.rmSync(".env"); - fs.renameSync(".env.bak", ".env"); - } - exit(0); + ); + execIn( + `node node_modules/typeorm/cli.js migration:generate "src/util/migrations/${db}/${filename}" -d dist/util/util/Database.js -p`, + process.cwd(), + { stdio: "inherit" } + ); + }); + if (fs.existsSync(".env.bak")) { + fs.rmSync(".env"); + fs.renameSync(".env.bak", ".env"); + } + exit(0); } -main(); \ No newline at end of file +main(); diff --git a/scripts/first_setup.js b/scripts/first_setup.js
index 4ce6e7de..95fee703 100755 --- a/scripts/first_setup.js +++ b/scripts/first_setup.js
@@ -5,7 +5,6 @@ const { stdout, exit } = require("process"); const { execIn } = require("./utils.js"); const { ask } = require("./utils/ask.js"); - const data = { env: [], config: { register: {} }, extra_pkgs: [] }; let rights = []; @@ -201,8 +200,6 @@ async function askRights() { return selectedRights; } - - function printTitle(input) { let width = stdout.columns / 2 - 1; //40 console.log(); @@ -210,7 +207,6 @@ function printTitle(input) { console.log(); } - function BitFlag(int) { return 1n << BigInt(int); } diff --git a/scripts/utils/ask.js b/scripts/utils/ask.js
index cb8a29f6..4dcd88e2 100644 --- a/scripts/utils/ask.js +++ b/scripts/utils/ask.js
@@ -15,6 +15,6 @@ async function askBool(question) { } module.exports = { - ask, - askBool -} \ No newline at end of file + ask, + askBool +}; diff --git a/src/api/routes/applications/#id/bot/index.ts b/src/api/routes/applications/#id/bot/index.ts
index e663059e..2ac3523b 100644 --- a/src/api/routes/applications/#id/bot/index.ts +++ b/src/api/routes/applications/#id/bot/index.ts
@@ -1,7 +1,6 @@ import { route } from "@fosscord/api"; -import { Application, Config, FieldErrors, generateToken, handleFile, OrmUtils, trimSpecial, User } from "@fosscord/util"; +import { Application, Config, FieldErrors, generateToken, handleFile, OrmUtils, trimSpecial, User, HTTPError } from "@fosscord/util"; import { Request, Response, Router } from "express"; -import { HTTPError } from "lambert-server"; import { verifyToken } from "node-2fa"; const router: Router = Router(); diff --git a/src/api/routes/auth/mfa/totp.ts b/src/api/routes/auth/mfa/totp.ts
index 9938569e..4b080af6 100644 --- a/src/api/routes/auth/mfa/totp.ts +++ b/src/api/routes/auth/mfa/totp.ts
@@ -1,7 +1,6 @@ import { route } from "@fosscord/api"; -import { BackupCode, generateToken, TotpSchema, User } from "@fosscord/util"; +import { BackupCode, generateToken, TotpSchema, User, HTTPError } from "@fosscord/util"; import { Request, Response, Router } from "express"; -import { HTTPError } from "lambert-server"; import { verifyToken } from "node-2fa"; const router = Router(); diff --git a/src/api/routes/auth/verify/view-backup-codes-challenge.ts b/src/api/routes/auth/verify/view-backup-codes-challenge.ts new file mode 100644
index 00000000..d524e0f7 --- /dev/null +++ b/src/api/routes/auth/verify/view-backup-codes-challenge.ts
@@ -0,0 +1,30 @@ +import { Router, Request, Response } from "express"; +import { route } from "@fosscord/api"; +import { FieldErrors, User, BackupCodesChallengeSchema } from "@fosscord/util"; + +let bcrypt: any; +try { + bcrypt = require("bcrypt"); +} catch { + bcrypt = require("bcryptjs"); + console.log("Warning: using bcryptjs because bcrypt is not installed! Performance will be affected."); +} + +const router = Router(); + +router.post("/", route({ body: "BackupCodesChallengeSchema" }), async (req: Request, res: Response) => { + const { password } = req.body as BackupCodesChallengeSchema; + + const user = await User.findOneOrFail({ where: { id: req.user_id }, select: ["data"] }); + + if (!await bcrypt.compare(password, user.data.hash || "")) { + throw FieldErrors({ password: { message: req.t("auth:login.INVALID_PASSWORD"), code: "INVALID_PASSWORD" } }); + } + + return res.json({ + nonce: "NoncePlaceholder", + regenerate_nonce: "RegenNoncePlaceholder", + }); +}); + +export default router; diff --git a/src/api/routes/guilds/#guild_id/member-verification.ts b/src/api/routes/guilds/#guild_id/member-verification.ts new file mode 100644
index 00000000..265a1b35 --- /dev/null +++ b/src/api/routes/guilds/#guild_id/member-verification.ts
@@ -0,0 +1,14 @@ +import { Router, Request, Response } from "express"; +import { route } from "@fosscord/api"; +const router = Router(); + +router.get("/",route({}), async (req: Request, res: Response) => { + // TODO: member verification + + res.status(404).json({ + message: "Unknown Guild Member Verification Form", + code: 10068 + }); +}); + +export default router; diff --git a/src/api/routes/guilds/#guild_id/members/#member_id/index.ts b/src/api/routes/guilds/#guild_id/members/#member_id/index.ts
index 57152f9a..06474f3e 100644 --- a/src/api/routes/guilds/#guild_id/members/#member_id/index.ts +++ b/src/api/routes/guilds/#guild_id/members/#member_id/index.ts
@@ -6,6 +6,7 @@ import { getRights, Guild, GuildMemberUpdateEvent, + handleFile, Member, MemberChangeSchema, OrmUtils, @@ -30,7 +31,7 @@ router.patch("/", route({ body: "MemberChangeSchema" }), async (req: Request, re if (member_id === "@me") member_id = req.user_id; const body = req.body as MemberChangeSchema; - const member = await Member.findOneOrFail({ where: { id: member_id, guild_id }, relations: ["roles", "user"] }); + let member = await Member.findOneOrFail({ where: { id: member_id, guild_id }, relations: ["roles", "user"] }); const permission = await getPermission(req.user_id, guild_id); const everyone = await Role.findOneOrFail({ where: { guild_id: guild_id, name: "@everyone", position: 0 } }); @@ -41,6 +42,10 @@ router.patch("/", route({ body: "MemberChangeSchema" }), async (req: Request, re member.roles = body.roles.map((x) => OrmUtils.mergeDeep(new Role(), { id: x })); // foreign key constraint will fail if role doesn't exist } + if (body.avatar) body.avatar = await handleFile(`/guilds/${guild_id}/users/${member_id}/avatars`, body.avatar as string); + + member = await OrmUtils.mergeDeep(member, body); + await member.save(); member.roles = member.roles.filter((x) => x.id !== everyone.id); diff --git a/src/api/routes/guilds/#guild_id/profile/index.ts b/src/api/routes/guilds/#guild_id/profile/index.ts new file mode 100644
index 00000000..ddc30943 --- /dev/null +++ b/src/api/routes/guilds/#guild_id/profile/index.ts
@@ -0,0 +1,30 @@ +import { route } from "@fosscord/api"; +import { emitEvent, GuildMemberUpdateEvent, handleFile, Member, MemberChangeProfileSchema, OrmUtils } from "@fosscord/util"; +import { Request, Response, Router } from "express"; + +const router = Router(); + +router.patch("/:member_id", route({ body: "MemberChangeProfileSchema" }), async (req: Request, res: Response) => { + let { guild_id, member_id } = req.params; + if (member_id === "@me") member_id = req.user_id; + const body = req.body as MemberChangeProfileSchema; + + let member = await Member.findOneOrFail({ where: { id: req.user_id, guild_id }, relations: ["roles", "user"] }); + + if (body.banner) body.banner = await handleFile(`/guilds/${guild_id}/users/${req.user_id}/avatars`, body.banner as string); + + member = await OrmUtils.mergeDeep(member, body); + + await member.save(); + + // do not use promise.all as we have to first write to db before emitting the event to catch errors + await emitEvent({ + event: "GUILD_MEMBER_UPDATE", + guild_id, + data: { ...member, roles: member.roles.map((x) => x.id) } + } as GuildMemberUpdateEvent); + + res.json(member); +}); + +export default router; diff --git a/src/api/routes/guilds/#guild_id/welcome_screen.ts b/src/api/routes/guilds/#guild_id/welcome_screen.ts
index 85c22a19..7e955c56 100644 --- a/src/api/routes/guilds/#guild_id/welcome_screen.ts +++ b/src/api/routes/guilds/#guild_id/welcome_screen.ts
@@ -24,6 +24,8 @@ router.patch("/", route({ body: "GuildUpdateWelcomeScreenSchema", permission: "M if (body.description) guild.welcome_screen.description = body.description; if (body.enabled != null) guild.welcome_screen.enabled = body.enabled; + await guild.save(); + res.sendStatus(204); }); diff --git a/src/api/routes/policies/stats.ts b/src/api/routes/policies/stats.ts new file mode 100644
index 00000000..d3aad2ec --- /dev/null +++ b/src/api/routes/policies/stats.ts
@@ -0,0 +1,22 @@ +import { route } from "@fosscord/api"; +import { Config, getRights, Guild, Member, Message, User } from "@fosscord/util"; +import { Request, Response, Router } from "express"; +const router = Router(); + +router.get("/", route({}), async (req: Request, res: Response) => { + let users, guilds, msgs, memberships; + // needs to be let otherwise we can't for + + let config = Config.get(); + if (!config.security.statsWorldReadable) { + let rights = await getRights(req.user_id); + rights.hasThrow("VIEW_SERVER_STATS"); + } + users = await User.count(); + guilds = await Guild.count(); + msgs = await Message.count(); + memberships = await Member.count(); + res.json({ user_count: users, guild_count: guilds, msg_count: msgs, membership_rels: memberships }); +}); + +export default router; diff --git a/src/api/routes/users/#id/profile.ts b/src/api/routes/users/#id/profile.ts
index 766c9880..541bb66a 100644 --- a/src/api/routes/users/#id/profile.ts +++ b/src/api/routes/users/#id/profile.ts
@@ -1,5 +1,16 @@ import { route } from "@fosscord/api"; -import { Member, PublicConnectedAccount, User, UserPublic } from "@fosscord/util"; +import { + emitEvent, + handleFile, + Member, + OrmUtils, + PrivateUserProjection, + PublicConnectedAccount, + User, + UserProfileModifySchema, + UserPublic, + UserUpdateEvent +} from "@fosscord/util"; import { Request, Response, Router } from "express"; const router: Router = Router(); @@ -62,12 +73,18 @@ router.get("/", route({ test: { response: { body: "UserProfileResponse" } } }), bot: user.bot }; + const userProfile = { + bio: req.user_bot ? null : user.bio, + accent_color: user.accent_color, + banner: user.banner + }; + const guildMemberDto = guild_member ? { - avatar: user.avatar, // TODO - banner: user.banner, // TODO - bio: req.user_bot ? null : user.bio, // TODO - communication_disabled_until: null, // TODO + avatar: guild_member.avatar, + banner: guild_member.banner, + bio: req.user_bot ? null : guild_member.bio, + communication_disabled_until: guild_member.communication_disabled_until, deaf: guild_member.deaf, flags: user.flags, is_pending: guild_member.pending, @@ -81,13 +98,47 @@ router.get("/", route({ test: { response: { body: "UserProfileResponse" } } }), } : undefined; + const guildMemberProfile = { + accent_color: null, + banner: guild_member?.banner || null, + bio: guild_member?.bio || "", + guild_id + }; res.json({ connected_accounts: user.connected_accounts, premium_guild_since: premium_guild_since, // TODO premium_since: user.premium_since, // TODO mutual_guilds: mutual_guilds, // TODO {id: "", nick: null} when ?with_mutual_guilds=true user: userDto, - guild_member: guildMemberDto + user_profile: userProfile, + guild_member: guild_id && guildMemberDto, + guild_member_profile: guild_id && guildMemberProfile + }); +}); + +router.patch("/", route({ body: "UserProfileModifySchema" }), async (req: Request, res: Response) => { + const body = req.body as UserProfileModifySchema; + + if (body.banner) body.banner = await handleFile(`/banners/${req.user_id}`, body.banner as string); + let user = await User.findOneOrFail({ where: { id: req.user_id }, select: [...PrivateUserProjection, "data"] }); + + user = OrmUtils.mergeDeep(user, body); + await user.save(); + + // @ts-ignore + delete user.data; + + // TODO: send update member list event in gateway + await emitEvent({ + event: "USER_UPDATE", + user_id: req.user_id, + data: user + } as UserUpdateEvent); + + res.json({ + accent_color: user.accent_color, + bio: user.bio, + banner: user.banner }); }); diff --git a/src/api/routes/users/@me/mfa/codes-verification.ts b/src/api/routes/users/@me/mfa/codes-verification.ts new file mode 100644
index 00000000..071c71fa --- /dev/null +++ b/src/api/routes/users/@me/mfa/codes-verification.ts
@@ -0,0 +1,41 @@ +import { Router, Request, Response } from "express"; +import { route } from "@fosscord/api"; +import { BackupCode, generateMfaBackupCodes, User, CodesVerificationSchema } from "@fosscord/util"; + +const router = Router(); + +router.post("/", route({ body: "CodesVerificationSchema" }), async (req: Request, res: Response) => { + const { key, nonce, regenerate } = req.body as CodesVerificationSchema; + + // TODO: We don't have email/etc etc, so can't send a verification code. + // Once that's done, this route can verify `key` + + const user = await User.findOneOrFail({ where: { id: req.user_id } }); + + var codes: BackupCode[]; + if (regenerate) { + await BackupCode.update( + { user: { id: req.user_id } }, + { expired: true } + ); + + codes = generateMfaBackupCodes(req.user_id); + await Promise.all(codes.map(x => x.save())); + } + else { + codes = await BackupCode.find({ + where: { + user: { + id: req.user_id, + }, + expired: false, + } + }); + } + + return res.json({ + backup_codes: codes.map(x => ({ ...x, expired: undefined })), + }); +}); + +export default router; diff --git a/src/api/routes/users/@me/mfa/totp/disable.ts b/src/api/routes/users/@me/mfa/totp/disable.ts
index 6bc9a5c7..07fdbb05 100644 --- a/src/api/routes/users/@me/mfa/totp/disable.ts +++ b/src/api/routes/users/@me/mfa/totp/disable.ts
@@ -1,7 +1,6 @@ import { route } from "@fosscord/api"; -import { BackupCode, generateToken, TotpDisableSchema, User } from "@fosscord/util"; +import { BackupCode, generateToken, TotpDisableSchema, User, HTTPError } from "@fosscord/util"; import { Request, Response, Router } from "express"; -import { HTTPError } from "lambert-server"; import { verifyToken } from "node-2fa"; const router = Router(); diff --git a/src/api/routes/users/@me/mfa/totp/enable.ts b/src/api/routes/users/@me/mfa/totp/enable.ts
index f3a73c28..adf51d6e 100644 --- a/src/api/routes/users/@me/mfa/totp/enable.ts +++ b/src/api/routes/users/@me/mfa/totp/enable.ts
@@ -1,7 +1,6 @@ import { route } from "@fosscord/api"; -import { BackupCode, Config, generateMfaBackupCodes, generateToken, TotpEnableSchema, User } from "@fosscord/util"; +import { BackupCode, Config, generateMfaBackupCodes, generateToken, TotpEnableSchema, User, HTTPError } from "@fosscord/util"; import { Request, Response, Router } from "express"; -import { HTTPError } from "lambert-server"; import { verifyToken } from "node-2fa"; let bcrypt: any; diff --git a/src/cdn/Server.ts b/src/cdn/Server.ts
index ec5edc68..9cedaa02 100644 --- a/src/cdn/Server.ts +++ b/src/cdn/Server.ts
@@ -3,6 +3,7 @@ import bodyParser from "body-parser"; import { Server, ServerOptions } from "lambert-server"; import path from "path"; import avatarsRoute from "./routes/avatars"; +import guildProfilesRoute from "./routes/guild-profiles"; import iconsRoute from "./routes/role-icons"; export interface CDNServerOptions extends ServerOptions {} @@ -65,6 +66,12 @@ export class CDNServer extends Server { this.app.use("/channel-icons/", avatarsRoute); this.log("verbose", "[Server] Route /channel-icons registered"); + this.app.use("/guilds/:guild_id/users/:user_id/avatars", guildProfilesRoute); + this.log("verbose", "[Server] Route /guilds/avatars registered"); + + this.app.use("/guilds/:guild_id/users/:user_id/banners", guildProfilesRoute); + this.log("verbose", "[Server] Route /guilds/banners registered"); + return super.start(); } diff --git a/src/cdn/routes/guild-profiles.ts b/src/cdn/routes/guild-profiles.ts new file mode 100644
index 00000000..32c05ad9 --- /dev/null +++ b/src/cdn/routes/guild-profiles.ts
@@ -0,0 +1,84 @@ +import { Config, HTTPError, Snowflake } from "@fosscord/util"; +import crypto from "crypto"; +import { Request, Response, Router } from "express"; +import FileType from "file-type"; +import { multer } from "../util/multer"; +import { storage } from "../util/Storage"; + +// TODO: check premium and animated pfp are allowed in the config +// TODO: generate different sizes of icon +// TODO: generate different image types of icon +// TODO: delete old icons + +const ANIMATED_MIME_TYPES = ["image/apng", "image/gif", "image/gifv"]; +const STATIC_MIME_TYPES = ["image/png", "image/jpeg", "image/webp", "image/svg+xml", "image/svg"]; +const ALLOWED_MIME_TYPES = [...ANIMATED_MIME_TYPES, ...STATIC_MIME_TYPES]; + +const router = Router(); + +router.post("/", multer.single("file"), async (req: Request, res: Response) => { + if (req.headers.signature !== Config.get().security.requestSignature) throw new HTTPError("Invalid request signature"); + if (!req.file) throw new HTTPError("Missing file"); + const { buffer, mimetype, size, originalname, fieldname } = req.file; + const { guild_id, user_id } = req.params; + + let hash = crypto.createHash("md5").update(Snowflake.generate()).digest("hex"); + + const type = await FileType.fromBuffer(buffer); + if (!type || !ALLOWED_MIME_TYPES.includes(type.mime)) throw new HTTPError("Invalid file type"); + if (ANIMATED_MIME_TYPES.includes(type.mime)) hash = `a_${hash}`; // animated icons have a_ infront of the hash + + const path = `guilds/${guild_id}/users/${user_id}/avatars/${hash}`; + const endpoint = Config.get().cdn.endpointPublic || "http://localhost:3003"; + + await storage.set(path, buffer); + + return res.json({ + id: hash, + content_type: type.mime, + size, + url: `${endpoint}${req.baseUrl}/${user_id}/${hash}` + }); +}); + +router.get("/", async (req: Request, res: Response) => { + let { guild_id, user_id } = req.params; + user_id = user_id.split(".")[0]; // remove .file extension + const path = `guilds/${guild_id}/users/${user_id}/avatars`; + + const file = await storage.get(path); + if (!file) throw new HTTPError("not found", 404); + const type = await FileType.fromBuffer(file); + + res.set("Content-Type", type?.mime); + res.set("Cache-Control", "public, max-age=31536000"); + + return res.send(file); +}); + +router.get("/:hash", async (req: Request, res: Response) => { + let { guild_id, user_id, hash } = req.params; + hash = hash.split(".")[0]; // remove .file extension + const path = `guilds/${guild_id}/users/${user_id}/avatars/${hash}`; + + const file = await storage.get(path); + if (!file) throw new HTTPError("not found", 404); + const type = await FileType.fromBuffer(file); + + res.set("Content-Type", type?.mime); + res.set("Cache-Control", "public, max-age=31536000"); + + return res.send(file); +}); + +router.delete("/:id", async (req: Request, res: Response) => { + if (req.headers.signature !== Config.get().security.requestSignature) throw new HTTPError("Invalid request signature"); + const { guild_id, user_id, id } = req.params; + const path = `guilds/${guild_id}/users/${user_id}/avatars/${id}`; + + await storage.delete(path); + + return res.send({ success: true }); +}); + +export default router; diff --git a/src/util/config/types/SecurityConfiguration.ts b/src/util/config/types/SecurityConfiguration.ts
index a2cebbd3..5a3d5aa6 100644 --- a/src/util/config/types/SecurityConfiguration.ts +++ b/src/util/config/types/SecurityConfiguration.ts
@@ -16,4 +16,5 @@ export class SecurityConfiguration { ipdataApiKey: string | null = "eca677b284b3bac29eb72f5e496aa9047f26543605efe99ff2ce35c9"; mfaBackupCodeCount: number = 10; mfaBackupCodeBytes: number = 4; + statsWorldReadable: boolean = true; } diff --git a/src/util/entities/Guild.ts b/src/util/entities/Guild.ts
index 015c6d04..cd7fa561 100644 --- a/src/util/entities/Guild.ts +++ b/src/util/entities/Guild.ts
@@ -241,7 +241,7 @@ export class Guild extends BaseClass { welcome_channels: { description: string; emoji_id?: string; - emoji_name: string; + emoji_name?: string; channel_id: string; }[]; }; diff --git a/src/util/entities/Member.ts b/src/util/entities/Member.ts
index f67ec02c..9e9131fe 100644 --- a/src/util/entities/Member.ts +++ b/src/util/entities/Member.ts
@@ -94,7 +94,19 @@ export class Member extends BaseClassWithoutId { // do not auto-kick force-joined members just because their joiners left the server }) **/ @Column({ nullable: true }) - joined_by?: string; + joined_by: string; + + @Column({ nullable: true }) + avatar: string; + + @Column({ nullable: true }) + banner: string; + + @Column() + bio: string; + + @Column({ nullable: true }) + communication_disabled_until: Date; // TODO: add this when we have proper read receipts // @Column({ type: "simple-json" }) @@ -243,7 +255,11 @@ export class Member extends BaseClassWithoutId { premium_since: null, deaf: false, mute: false, - pending: false + pending: false, + avatar: null, + banner: null, + bio: "", + communication_disabled_until: null }; //TODO: check for bugs if (guild.member_count) guild.member_count++; @@ -284,7 +300,8 @@ export class Member extends BaseClassWithoutId { joined_at: member.joined_at, presences: [], stage_instances: [], - threads: [] + threads: [], + embedded_activities: [], }, user_id } as GuildCreateEvent) diff --git a/src/util/interfaces/Event.ts b/src/util/interfaces/Event.ts
index f97f4615..2631ef13 100644 --- a/src/util/interfaces/Event.ts +++ b/src/util/interfaces/Event.ts
@@ -155,6 +155,7 @@ export interface GuildCreateEvent extends Event { presences: never[]; stage_instances: never[]; threads: never[]; + embedded_activities: never[]; }; } diff --git a/src/util/migrations/mariadb/1661273147273-test.ts b/src/util/migrations/mariadb/1661273147273-test.ts
index 4e077a11..0090e2aa 100644 --- a/src/util/migrations/mariadb/1661273147273-test.ts +++ b/src/util/migrations/mariadb/1661273147273-test.ts
@@ -1,23 +1,23 @@ import { MigrationInterface, QueryRunner } from "typeorm"; export class test1661273147273 implements MigrationInterface { - name = 'test1661273147273' + name = "test1661273147273"; - public async up(queryRunner: QueryRunner): Promise<void> { - await queryRunner.query(` + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` ALTER TABLE \`invites\` DROP FOREIGN KEY \`FK_15c35422032e0b22b4ada95f48f\` `); - await queryRunner.query(` + await queryRunner.query(` DROP INDEX \`IDX_2ce5a55796fe4c2f77ece57a64\` ON \`applications\` `); - await queryRunner.query(` + await queryRunner.query(` CREATE TABLE \`plugin_config\` ( \`key\` varchar(255) NOT NULL, \`value\` text NULL, PRIMARY KEY (\`key\`) ) ENGINE = InnoDB `); - await queryRunner.query(` + await queryRunner.query(` CREATE TABLE \`user_settings\` ( \`id\` varchar(255) NOT NULL, \`afk_timeout\` int NULL, @@ -54,96 +54,95 @@ export class test1661273147273 implements MigrationInterface { PRIMARY KEY (\`id\`) ) ENGINE = InnoDB `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` DROP COLUMN \`settings\` `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` ADD \`settingsId\` varchar(255) NULL `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` ADD UNIQUE INDEX \`IDX_76ba283779c8441fd5ff819c8c\` (\`settingsId\`) `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`channels\` ADD \`flags\` int NULL `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`channels\` ADD \`default_thread_rate_limit_per_user\` int NULL `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`guilds\` ADD \`premium_progress_bar_enabled\` tinyint NULL `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` CHANGE \`bio\` \`bio\` varchar(255) NULL `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` CHANGE \`mfa_enabled\` \`mfa_enabled\` tinyint NULL `); - await queryRunner.query(` + await queryRunner.query(` CREATE UNIQUE INDEX \`REL_76ba283779c8441fd5ff819c8c\` ON \`users\` (\`settingsId\`) `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` ADD CONSTRAINT \`FK_76ba283779c8441fd5ff819c8cf\` FOREIGN KEY (\`settingsId\`) REFERENCES \`user_settings\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`invites\` ADD CONSTRAINT \`FK_15c35422032e0b22b4ada95f48f\` FOREIGN KEY (\`inviter_id\`) REFERENCES \`users\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION `); - } + } - public async down(queryRunner: QueryRunner): Promise<void> { - await queryRunner.query(` + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` ALTER TABLE \`invites\` DROP FOREIGN KEY \`FK_15c35422032e0b22b4ada95f48f\` `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` DROP FOREIGN KEY \`FK_76ba283779c8441fd5ff819c8cf\` `); - await queryRunner.query(` + await queryRunner.query(` DROP INDEX \`REL_76ba283779c8441fd5ff819c8c\` ON \`users\` `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` CHANGE \`mfa_enabled\` \`mfa_enabled\` tinyint NOT NULL `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` CHANGE \`bio\` \`bio\` varchar(255) NOT NULL `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`guilds\` DROP COLUMN \`premium_progress_bar_enabled\` `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`channels\` DROP COLUMN \`default_thread_rate_limit_per_user\` `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`channels\` DROP COLUMN \`flags\` `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` DROP INDEX \`IDX_76ba283779c8441fd5ff819c8c\` `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` DROP COLUMN \`settingsId\` `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` ADD \`settings\` text NOT NULL `); - await queryRunner.query(` + await queryRunner.query(` DROP TABLE \`user_settings\` `); - await queryRunner.query(` + await queryRunner.query(` DROP TABLE \`plugin_config\` `); - await queryRunner.query(` + await queryRunner.query(` CREATE UNIQUE INDEX \`IDX_2ce5a55796fe4c2f77ece57a64\` ON \`applications\` (\`bot_user_id\`) `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`invites\` ADD CONSTRAINT \`FK_15c35422032e0b22b4ada95f48f\` FOREIGN KEY (\`inviter_id\`) REFERENCES \`users\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION `); - } - + } } diff --git a/src/util/migrations/mariadb/1661273179287-test2.ts b/src/util/migrations/mariadb/1661273179287-test2.ts
index 0f77f284..973d8124 100644 --- a/src/util/migrations/mariadb/1661273179287-test2.ts +++ b/src/util/migrations/mariadb/1661273179287-test2.ts
@@ -1,18 +1,17 @@ import { MigrationInterface, QueryRunner } from "typeorm"; export class test21661273179287 implements MigrationInterface { - name = 'test21661273179287' + name = "test21661273179287"; - public async up(queryRunner: QueryRunner): Promise<void> { - await queryRunner.query(` + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` DROP INDEX \`IDX_76ba283779c8441fd5ff819c8c\` ON \`users\` `); - } + } - public async down(queryRunner: QueryRunner): Promise<void> { - await queryRunner.query(` + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` CREATE UNIQUE INDEX \`IDX_76ba283779c8441fd5ff819c8c\` ON \`users\` (\`settingsId\`) `); - } - + } } diff --git a/src/util/migrations/mariadb/1661885910534-guild-member-profiles.ts b/src/util/migrations/mariadb/1661885910534-guild-member-profiles.ts new file mode 100644
index 00000000..6e1ac3f8 --- /dev/null +++ b/src/util/migrations/mariadb/1661885910534-guild-member-profiles.ts
@@ -0,0 +1,40 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class guildMemberProfiles1661885910534 implements MigrationInterface { + name = 'guildMemberProfiles1661885910534' + + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` + ALTER TABLE \`members\` + ADD \`avatar\` varchar(255) NULL + `); + await queryRunner.query(` + ALTER TABLE \`members\` + ADD \`banner\` varchar(255) NULL + `); + await queryRunner.query(` + ALTER TABLE \`members\` + ADD \`bio\` varchar(255) NOT NULL + `); + await queryRunner.query(` + ALTER TABLE \`members\` + ADD \`communication_disabled_until\` datetime NULL + `); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` + ALTER TABLE \`members\` DROP COLUMN \`communication_disabled_until\` + `); + await queryRunner.query(` + ALTER TABLE \`members\` DROP COLUMN \`bio\` + `); + await queryRunner.query(` + ALTER TABLE \`members\` DROP COLUMN \`banner\` + `); + await queryRunner.query(` + ALTER TABLE \`members\` DROP COLUMN \`avatar\` + `); + } + +} diff --git a/src/util/migrations/postgres/1661885830688-guild-member-profiles.ts b/src/util/migrations/postgres/1661885830688-guild-member-profiles.ts new file mode 100644
index 00000000..81671250 --- /dev/null +++ b/src/util/migrations/postgres/1661885830688-guild-member-profiles.ts
@@ -0,0 +1,40 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class guildMemberProfiles1661885830688 implements MigrationInterface { + name = 'guildMemberProfiles1661885830688' + + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` + ALTER TABLE "members" + ADD "avatar" character varying + `); + await queryRunner.query(` + ALTER TABLE "members" + ADD "banner" character varying + `); + await queryRunner.query(` + ALTER TABLE "members" + ADD "bio" character varying NOT NULL default '' + `); + await queryRunner.query(` + ALTER TABLE "members" + ADD "communication_disabled_until" TIMESTAMP + `); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` + ALTER TABLE "members" DROP COLUMN "communication_disabled_until" + `); + await queryRunner.query(` + ALTER TABLE "members" DROP COLUMN "bio" + `); + await queryRunner.query(` + ALTER TABLE "members" DROP COLUMN "banner" + `); + await queryRunner.query(` + ALTER TABLE "members" DROP COLUMN "avatar" + `); + } + +} diff --git a/src/util/migrations/sqlite/1661885742207-guild-member-profiles.ts b/src/util/migrations/sqlite/1661885742207-guild-member-profiles.ts new file mode 100644
index 00000000..24ec9c72 --- /dev/null +++ b/src/util/migrations/sqlite/1661885742207-guild-member-profiles.ts
@@ -0,0 +1,136 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class guildMemberProfiles1661885742207 implements MigrationInterface { + name = 'guildMemberProfiles1661885742207' + + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` + DROP INDEX "IDX_bb2bf9386ac443afbbbf9f12d3" + `); + await queryRunner.query(` + CREATE TABLE "temporary_members" ( + "index" integer PRIMARY KEY AUTOINCREMENT NOT NULL, + "id" varchar NOT NULL, + "guild_id" varchar NOT NULL, + "nick" varchar, + "joined_at" datetime NOT NULL, + "premium_since" datetime, + "deaf" boolean NOT NULL, + "mute" boolean NOT NULL, + "pending" boolean NOT NULL, + "settings" text NOT NULL, + "last_message_id" varchar, + "joined_by" varchar, + "avatar" varchar, + "banner" varchar, + "bio" varchar NOT NULL, + "communication_disabled_until" datetime, + CONSTRAINT "FK_28b53062261b996d9c99fa12404" FOREIGN KEY ("id") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, + CONSTRAINT "FK_16aceddd5b89825b8ed6029ad1c" FOREIGN KEY ("guild_id") REFERENCES "guilds" ("id") ON DELETE CASCADE ON UPDATE NO ACTION + ) + `); + await queryRunner.query(` + INSERT INTO "temporary_members"( + "index", + "id", + "guild_id", + "nick", + "joined_at", + "premium_since", + "deaf", + "mute", + "pending", + "settings", + "last_message_id", + "joined_by" + ) + SELECT "index", + "id", + "guild_id", + "nick", + "joined_at", + "premium_since", + "deaf", + "mute", + "pending", + "settings", + "last_message_id", + "joined_by" + FROM "members" + `); + await queryRunner.query(` + DROP TABLE "members" + `); + await queryRunner.query(` + ALTER TABLE "temporary_members" + RENAME TO "members" + `); + await queryRunner.query(` + CREATE UNIQUE INDEX "IDX_bb2bf9386ac443afbbbf9f12d3" ON "members" ("id", "guild_id") + `); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` + DROP INDEX "IDX_bb2bf9386ac443afbbbf9f12d3" + `); + await queryRunner.query(` + ALTER TABLE "members" + RENAME TO "temporary_members" + `); + await queryRunner.query(` + CREATE TABLE "members" ( + "index" integer PRIMARY KEY AUTOINCREMENT NOT NULL, + "id" varchar NOT NULL, + "guild_id" varchar NOT NULL, + "nick" varchar, + "joined_at" datetime NOT NULL, + "premium_since" datetime, + "deaf" boolean NOT NULL, + "mute" boolean NOT NULL, + "pending" boolean NOT NULL, + "settings" text NOT NULL, + "last_message_id" varchar, + "joined_by" varchar, + CONSTRAINT "FK_28b53062261b996d9c99fa12404" FOREIGN KEY ("id") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, + CONSTRAINT "FK_16aceddd5b89825b8ed6029ad1c" FOREIGN KEY ("guild_id") REFERENCES "guilds" ("id") ON DELETE CASCADE ON UPDATE NO ACTION + ) + `); + await queryRunner.query(` + INSERT INTO "members"( + "index", + "id", + "guild_id", + "nick", + "joined_at", + "premium_since", + "deaf", + "mute", + "pending", + "settings", + "last_message_id", + "joined_by" + ) + SELECT "index", + "id", + "guild_id", + "nick", + "joined_at", + "premium_since", + "deaf", + "mute", + "pending", + "settings", + "last_message_id", + "joined_by" + FROM "temporary_members" + `); + await queryRunner.query(` + DROP TABLE "temporary_members" + `); + await queryRunner.query(` + CREATE UNIQUE INDEX "IDX_bb2bf9386ac443afbbbf9f12d3" ON "members" ("id", "guild_id") + `); + } + +} diff --git a/src/util/schemas/BackupCodesChallengeSchema.ts b/src/util/schemas/BackupCodesChallengeSchema.ts new file mode 100644
index 00000000..d6b519b7 --- /dev/null +++ b/src/util/schemas/BackupCodesChallengeSchema.ts
@@ -0,0 +1,3 @@ +export interface BackupCodesChallengeSchema { + password: string; +} \ No newline at end of file diff --git a/src/util/schemas/CodesVerificationSchema.ts b/src/util/schemas/CodesVerificationSchema.ts new file mode 100644
index 00000000..e8e2e7b4 --- /dev/null +++ b/src/util/schemas/CodesVerificationSchema.ts
@@ -0,0 +1,5 @@ +export interface CodesVerificationSchema { + key: string; + nonce: string; + regenerate?: boolean; +} \ No newline at end of file diff --git a/src/util/schemas/MemberChangeProfileSchema.ts b/src/util/schemas/MemberChangeProfileSchema.ts new file mode 100644
index 00000000..3e85174d --- /dev/null +++ b/src/util/schemas/MemberChangeProfileSchema.ts
@@ -0,0 +1,5 @@ +export interface MemberChangeProfileSchema { + banner?: string | null; + nick?: string; + bio?: string; +} diff --git a/src/util/schemas/MemberChangeSchema.ts b/src/util/schemas/MemberChangeSchema.ts
index db434538..0cbab4a3 100644 --- a/src/util/schemas/MemberChangeSchema.ts +++ b/src/util/schemas/MemberChangeSchema.ts
@@ -1,3 +1,5 @@ export interface MemberChangeSchema { roles?: string[]; + nick?: string; + avatar?: string | null; } diff --git a/src/util/schemas/UserProfileModifySchema.ts b/src/util/schemas/UserProfileModifySchema.ts new file mode 100644
index 00000000..33a372c9 --- /dev/null +++ b/src/util/schemas/UserProfileModifySchema.ts
@@ -0,0 +1,5 @@ +export interface UserProfileModifySchema { + bio?: string; + accent_color?: number | null; + banner?: string | null; +} diff --git a/src/util/schemas/index.ts b/src/util/schemas/index.ts
index a15ab4b0..320593ec 100644 --- a/src/util/schemas/index.ts +++ b/src/util/schemas/index.ts
@@ -1,4 +1,5 @@ export * from "./ActivitySchema"; +export * from "./BackupCodesChallengeSchema"; export * from "./BanCreateSchema"; export * from "./BanModeratorSchema"; export * from "./BanRegistrySchema"; @@ -6,6 +7,7 @@ export * from "./BulkDeleteSchema"; export * from "./ChannelModifySchema"; export * from "./ChannelPermissionOverwriteSchema"; export * from "./ChannelReorderSchema"; +export * from "./CodesVerificationSchema"; export * from "./DmChannelCreateSchema"; export * from "./EmojiCreateSchema"; export * from "./EmojiModifySchema"; @@ -17,6 +19,7 @@ export * from "./IdentifySchema"; export * from "./InviteCreateSchema"; export * from "./LazyRequestSchema"; export * from "./LoginSchema"; +export * from "./MemberChangeProfileSchema"; export * from "./MemberChangeSchema"; export * from "./MemberNickChangeSchema"; export * from "./MessageAcknowledgeSchema"; @@ -36,8 +39,9 @@ export * from "./TotpDisableSchema"; export * from "./TotpEnableSchema"; export * from "./TotpSchema"; export * from "./UserModifySchema"; +export * from "./UserProfileModifySchema"; export * from "./UserSettingsSchema"; export * from "./VanityUrlSchema"; export * from "./VoiceStateUpdateSchema"; export * from "./WebhookCreateSchema"; -export * from "./WidgetModifySchema"; +export * from "./WidgetModifySchema"; \ No newline at end of file diff --git a/src/util/util/Database.ts b/src/util/util/Database.ts
index b9f8365e..647de26a 100644 --- a/src/util/util/Database.ts +++ b/src/util/util/Database.ts
@@ -96,7 +96,7 @@ function getDataSourceOptions(): DataSourceOptions { name: "default", migrations: synchronizeInsteadOfMigrations ? [] : [path.join(__dirname, "..", "migrations", type, "*.js")], migrationsRun: !synchronizeInsteadOfMigrations, - applicationName: `Fosscord Server`, + applicationName: `Fosscord Server` } as DataSourceOptions; } diff --git a/src/util/util/Rights.ts b/src/util/util/Rights.ts
index 51bb098c..236bfea7 100644 --- a/src/util/util/Rights.ts +++ b/src/util/util/Rights.ts
@@ -63,7 +63,11 @@ export class Rights extends BitField { RESPOND_TO_INTERACTIONS: BitFlag(41), // can respond to interactions SEND_BACKDATED_EVENTS: BitFlag(42), // can send backdated events USE_MASS_INVITES: BitFlag(43), // added per @xnacly's request — can accept mass invites - ACCEPT_INVITES: BitFlag(44) // added per @xnacly's request — can accept user-specific invites and DM requests + ACCEPT_INVITES: BitFlag(44), // added per @xnacly's request — can accept user-specific invites and DM requests + SELF_EDIT_FLAGS: BitFlag(45), // can modify own flags + EDIT_FLAGS: BitFlag(46), // can set others' flags + MANAGE_GROUPS: BitFlag(47), // can manage others' groups + VIEW_SERVER_STATS: BitFlag(48) // added per @chrischrome's request — can view server stats) }; any(permission: RightResolvable, checkOperator = true) {