summary refs log tree commit diff
diff options
context:
space:
mode:
authorPuyodead1 <puyodead@protonmail.com>2023-05-05 11:09:05 -0400
committerRory& <root@rory.gay>2026-01-21 07:10:10 +0100
commitcca6cb32107fa143928f5b88e3a25d8ccc01f695 (patch)
treeaa48aa1f006233e3a061c0ecc983810b4e031875
parentQuick and dirty util scripts, trim node modules (diff)
downloadserver-ts-cca6cb32107fa143928f5b88e3a25d8ccc01f695.tar.xz
initial pomelo implementation
-rw-r--r--src/api/routes/auth/forgot.ts16
-rw-r--r--src/api/routes/channels/#channel_id/messages/#message_id/crosspost.ts1
-rw-r--r--src/api/routes/channels/#channel_id/messages/index.ts5
-rw-r--r--src/api/routes/guilds/#guild_id/bans.ts2
-rw-r--r--src/api/routes/guilds/#guild_id/messages/search.ts2
-rw-r--r--src/api/routes/oauth2/authorize.ts8
-rw-r--r--src/api/routes/users/#user_id/relationships.ts2
-rw-r--r--src/api/routes/users/@me/index.ts2
-rw-r--r--src/api/routes/users/@me/relationships.ts18
-rw-r--r--src/connections/Discord/index.ts3
-rw-r--r--src/schemas/responses/GuildBansResponse.ts2
-rw-r--r--src/schemas/responses/GuildWidgetJsonResponse.ts36
-rw-r--r--src/schemas/responses/UserRelationsResponse.ts8
-rw-r--r--src/schemas/uncategorised/RelationshipPostSchema.ts5
-rw-r--r--src/util/config/types/GeneralConfiguration.ts1
-rw-r--r--src/util/dtos/UserDTO.ts30
-rw-r--r--src/util/entities/User.ts62
-rw-r--r--src/util/util/email/index.ts3
18 files changed, 149 insertions, 57 deletions
diff --git a/src/api/routes/auth/forgot.ts b/src/api/routes/auth/forgot.ts

index 26e1b353..69ca582b 100644 --- a/src/api/routes/auth/forgot.ts +++ b/src/api/routes/auth/forgot.ts
@@ -20,6 +20,7 @@ import { route, verifyCaptcha } from "@spacebar/api"; import { Config, Email, User } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { ForgotPasswordSchema } from "@spacebar/schemas"; +import { HTTPError } from "lambert-server*"; const router = Router({ mergeParams: true }); router.post( @@ -66,11 +67,16 @@ router.post( select: { username: true, id: true, email: true }, }).catch(() => {}); - if (user && user.email) { - Email.sendResetPassword(user, user.email).catch((e) => { - console.error(`Failed to send password reset email to ${user.username}#${user.discriminator} (${user.id}): ${e}`); - }); - } + if (user && user.email) + return await Email.sendResetPassword(user, user.email) + .then(() => { + return res.sendStatus(204); + }) + .catch((e) => { + console.error(`Failed to send password reset email to ${user.handle}: ${e}`); + throw new HTTPError("Failed to send password reset email", 500); + }); + else throw new HTTPError("No user or email", 500); }, ); diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/crosspost.ts b/src/api/routes/channels/#channel_id/messages/#message_id/crosspost.ts
index 2aad157d..bf1f708d 100644 --- a/src/api/routes/channels/#channel_id/messages/#message_id/crosspost.ts +++ b/src/api/routes/channels/#channel_id/messages/#message_id/crosspost.ts
@@ -43,6 +43,7 @@ router.post( username: "", avatar: "", discriminator: "", + global_name: "", public_flags: 64, }, attachments: [], diff --git a/src/api/routes/channels/#channel_id/messages/index.ts b/src/api/routes/channels/#channel_id/messages/index.ts
index 6b18e2c9..0ad522fc 100644 --- a/src/api/routes/channels/#channel_id/messages/index.ts +++ b/src/api/routes/channels/#channel_id/messages/index.ts
@@ -192,11 +192,14 @@ router.get( if ((y.user_ids || []).includes(req.user_id)) y.me = true; delete y.user_ids; }); + const { pomeloEnabled } = Config.get().general; if (!x.author) x.author = User.create({ id: "4", - discriminator: "0000", + discriminator: pomeloEnabled ? "0" : "0000", username: "Spacebar Ghost", + global_name: "spacebarghost", + display_name: "Spacebar Ghost", public_flags: 0, }); x.attachments?.forEach((y: Attachment) => { diff --git a/src/api/routes/guilds/#guild_id/bans.ts b/src/api/routes/guilds/#guild_id/bans.ts
index 2bd28ace..8f1b4444 100644 --- a/src/api/routes/guilds/#guild_id/bans.ts +++ b/src/api/routes/guilds/#guild_id/bans.ts
@@ -61,6 +61,8 @@ router.get( user: { username: user.username, discriminator: user.discriminator, + global_name: user.global_name, + display_name: user.display_name, id: user.id, avatar: user.avatar ?? null, public_flags: user.public_flags, diff --git a/src/api/routes/guilds/#guild_id/messages/search.ts b/src/api/routes/guilds/#guild_id/messages/search.ts
index 51c9b5a2..700071ef 100644 --- a/src/api/routes/guilds/#guild_id/messages/search.ts +++ b/src/api/routes/guilds/#guild_id/messages/search.ts
@@ -123,6 +123,8 @@ router.get( avatar: x.author?.avatar, avatar_decoration: null, discriminator: x.author?.discriminator, + global_name: x.author?.global_name, + display_name: x.author?.display_name, public_flags: x.author?.public_flags, }, attachments: x.attachments, diff --git a/src/api/routes/oauth2/authorize.ts b/src/api/routes/oauth2/authorize.ts
index b989899a..ee84ccf5 100644 --- a/src/api/routes/oauth2/authorize.ts +++ b/src/api/routes/oauth2/authorize.ts
@@ -75,7 +75,7 @@ router.get( id: req.user_id, bot: false, }, - select: { id: true, username: true, avatar: true, discriminator: true, public_flags: true }, + select: { id: true, username: true, avatar: true, discriminator: true, public_flags: true, global_name: true, display_name: true, public_flags: true }, }); const guilds = await Member.find({ @@ -122,6 +122,8 @@ router.get( avatar: user.avatar, avatar_decoration: null, // TODO discriminator: user.discriminator, + global_name: user.global_name, + display_name: user.display_name, public_flags: user.public_flags, }, application: { @@ -144,6 +146,8 @@ router.get( avatar: bot.avatar, avatar_decoration: null, // TODO discriminator: bot.discriminator, + global_name: bot.global_name, + display_name: bot.display_name, public_flags: bot.public_flags, bot: true, approximated_guild_count: 0, // TODO @@ -220,4 +224,4 @@ router.post( }, ); -export default router; +export default router; \ No newline at end of file diff --git a/src/api/routes/users/#user_id/relationships.ts b/src/api/routes/users/#user_id/relationships.ts
index 93f11296..a9b91817 100644 --- a/src/api/routes/users/#user_id/relationships.ts +++ b/src/api/routes/users/#user_id/relationships.ts
@@ -55,6 +55,8 @@ router.get( username: relation_user.username, avatar: relation_user.avatar, discriminator: relation_user.discriminator, + global_name: relation_user.global_name, + display_name: relation_user.display_name, public_flags: relation_user.public_flags, }); } diff --git a/src/api/routes/users/@me/index.ts b/src/api/routes/users/@me/index.ts
index bd1fa077..628dddd4 100644 --- a/src/api/routes/users/@me/index.ts +++ b/src/api/routes/users/@me/index.ts
@@ -120,6 +120,7 @@ router.patch( newToken = (await generateToken(user.id)) as string; } + // TODO: pomelo: disallow if pomelo is enabled if (body.username) { const check_username = body?.username?.replace(/\s/g, "").trim(); if (!check_username) { @@ -151,6 +152,7 @@ router.patch( } } + // TODO: pomelo: disallow if pomelo is enabled if (body.discriminator) { if ( await User.findOne({ diff --git a/src/api/routes/users/@me/relationships.ts b/src/api/routes/users/@me/relationships.ts
index 37d5e222..9cd138af 100644 --- a/src/api/routes/users/@me/relationships.ts +++ b/src/api/routes/users/@me/relationships.ts
@@ -131,16 +131,26 @@ router.post( }, }), async (req: Request, res: Response) => { + const { pomeloEnabled } = Config.get().general; + const where = pomeloEnabled + ? { + // TODO: pomelo: should we use username or add global_name property to the request? + global_name: req.body.username, + } + : { + discriminator: String(req.body.discriminator).padStart( + 4, + "0", + ), //Discord send the discriminator as integer, we need to add leading zeroes + username: req.body.username, + }; return await updateRelationship( req, res, await User.findOneOrFail({ relations: { relationships: { to: true } }, select: userProjection, - where: { - discriminator: String(req.body.discriminator).padStart(4, "0"), //Discord send the discriminator as integer, we need to add leading zeroes - username: req.body.username, - }, + where, }), req.body.type, ); diff --git a/src/connections/Discord/index.ts b/src/connections/Discord/index.ts
index ab44f4b3..c3d26119 100644 --- a/src/connections/Discord/index.ts +++ b/src/connections/Discord/index.ts
@@ -25,6 +25,8 @@ interface UserResponse { id: string; username: string; discriminator: string; + global_name: string; + display_name?: string; avatar_url: string | null; } @@ -113,6 +115,7 @@ export default class DiscordConnection extends Connection { if (exists) return null; + // TODO: pomelo return await this.createConnection({ user_id: userId, external_id: userInfo.id, diff --git a/src/schemas/responses/GuildBansResponse.ts b/src/schemas/responses/GuildBansResponse.ts
index 5e3c3ed4..24f99ed8 100644 --- a/src/schemas/responses/GuildBansResponse.ts +++ b/src/schemas/responses/GuildBansResponse.ts
@@ -21,6 +21,8 @@ export interface GuildBansResponse { user: { username: string; discriminator: string; + global_name: string; + display_name: string | null; id: string; avatar: string | null; public_flags: number; diff --git a/src/schemas/responses/GuildWidgetJsonResponse.ts b/src/schemas/responses/GuildWidgetJsonResponse.ts
index a1c99dde..60e8e7cc 100644 --- a/src/schemas/responses/GuildWidgetJsonResponse.ts +++ b/src/schemas/responses/GuildWidgetJsonResponse.ts
@@ -19,21 +19,23 @@ import { ClientStatus } from "@spacebar/util"; export interface GuildWidgetJsonResponse { - id: string; - name: string; - instant_invite: string; - channels: { - id: string; - name: string; - position: number; - }[]; - members: { - id: string; - username: string; - discriminator: string; - avatar: string | null; - status: ClientStatus; - avatar_url: string; - }[]; - presence_count: number; + id: string; + name: string; + instant_invite: string; + channels: { + id: string; + name: string; + position: number; + }[]; + members: { + id: string; + username: string; + global_name: string; + display_name: string | null; + discriminator: string; + avatar: string | null; + status: ClientStatus; + avatar_url: string; + }[]; + presence_count: number; } diff --git a/src/schemas/responses/UserRelationsResponse.ts b/src/schemas/responses/UserRelationsResponse.ts
index 377cab09..59db277f 100644 --- a/src/schemas/responses/UserRelationsResponse.ts +++ b/src/schemas/responses/UserRelationsResponse.ts
@@ -17,4 +17,10 @@ */ import { User } from "@spacebar/util"; -export type UserRelationsResponse = (Pick<User, "id"> & Pick<User, "username"> & Pick<User, "discriminator"> & Pick<User, "avatar"> & Pick<User, "public_flags">)[]; +export type UserRelationsResponse = (Pick<User, "id"> & + Pick<User, "username"> & + Pick<User, "global_name"> & + Pick<User, "display_name"> & + Pick<User, "discriminator"> & + Pick<User, "avatar"> & + Pick<User, "public_flags">)[]; diff --git a/src/schemas/uncategorised/RelationshipPostSchema.ts b/src/schemas/uncategorised/RelationshipPostSchema.ts
index 5f2d9437..f0a5fc32 100644 --- a/src/schemas/uncategorised/RelationshipPostSchema.ts +++ b/src/schemas/uncategorised/RelationshipPostSchema.ts
@@ -16,7 +16,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ +// TODO: pomelo? export interface RelationshipPostSchema { - discriminator: string; - username: string; + discriminator: string; + username: string; } diff --git a/src/util/config/types/GeneralConfiguration.ts b/src/util/config/types/GeneralConfiguration.ts
index f4f76441..1db15dfa 100644 --- a/src/util/config/types/GeneralConfiguration.ts +++ b/src/util/config/types/GeneralConfiguration.ts
@@ -29,4 +29,5 @@ export class GeneralConfiguration { image: string | null = null; instanceId: string = Snowflake.generate(); autoCreateBotUsers: boolean = false; + pomeloEnabled: boolean = false; } diff --git a/src/util/dtos/UserDTO.ts b/src/util/dtos/UserDTO.ts
index 2a79f8f1..b34d6d8a 100644 --- a/src/util/dtos/UserDTO.ts +++ b/src/util/dtos/UserDTO.ts
@@ -19,19 +19,21 @@ import { User } from "../entities"; export class MinimalPublicUserDTO { - avatar?: string | null; - discriminator: string; - id: string; - public_flags: number; - username: string; - badge_ids?: string[] | null; + avatar?: string | null; + discriminator: string; + id: string; + public_flags: number; + username: string; + global_name: string; + display_name?: string; - constructor(user: User) { - this.avatar = user.avatar; - this.discriminator = user.discriminator; - this.id = user.id; - this.public_flags = user.public_flags; - this.username = user.username; - this.badge_ids = user.badge_ids; - } + constructor(user: User) { + this.avatar = user.avatar; + this.discriminator = user.discriminator; + this.id = user.id; + this.public_flags = user.public_flags; + this.username = user.username; + this.global_name = user.global_name; + this.display_name = user.display_name; + } } diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts
index 84471e0d..e527598f 100644 --- a/src/util/entities/User.ts +++ b/src/util/entities/User.ts
@@ -29,6 +29,25 @@ import { Session } from "./Session"; import { UserSettings } from "./UserSettings"; import { ChannelType, PrivateUserProjection, PublicUser, PublicUserProjection, UserPrivate } from "@spacebar/schemas"; +export enum PublicUserEnum { + username, + global_name, + display_name, + discriminator, + id, + public_flags, + avatar, + accent_color, + banner, + bio, + bot, + premium_since, + premium_type, + theme_colors, + pronouns, +} +export type PublicUserKeys = keyof typeof PublicUserEnum; + @Entity({ name: "users", }) @@ -36,8 +55,14 @@ export class User extends BaseClass { @Column() username: string; // username max length 32, min 2 (should be configurable) + @Column({nullable: true}) + global_name: string; // puyo: pomelo + + @Column({nullable: true}) + display_name?: string; // puyo: pomelo + @Column() - discriminator: string; // opaque string: 4 digits on discord.com + discriminator: string; // opaque string: 4 digits on discord.com, 0 for pomelo @Column({ nullable: true }) avatar?: string; // hash of the user avatar @@ -245,6 +270,13 @@ export class User extends BaseClass { } } + public get handle(): string { + const {pomeloEnabled} = Config.get().general; + + // if pomelo is enabled, global_name should be set + return pomeloEnabled ? this.global_name as string : `${this.username}#${this.discriminator}`; + } + static async register({ email, username, @@ -261,19 +293,25 @@ export class User extends BaseClass { req?: Request; bot?: boolean; }) { + const {pomeloEnabled} = Config.get().general; + // trim special uf8 control characters -> Backspace, Newline, ... username = trimSpecial(username); - const discriminator = await User.generateDiscriminator(username); - if (!discriminator) { - // We've failed to generate a valid and unused discriminator - throw FieldErrors({ - username: { - code: "USERNAME_TOO_MANY_USERS", + let discriminator: string | undefined; + if(pomeloEnabled) discriminator = "0"; + else { + discriminator = await User.generateDiscriminator(username); + if (!discriminator) { + // We've failed to generate a valid and unused discriminator + throw FieldErrors({ + username: { + code: "USERNAME_TOO_MANY_USERS", message: req?.t("auth:register.USERNAME_TOO_MANY_USERS") || "", - }, - }); - } + }, + }); + } + } // TODO: save date_of_birth // apparently discord doesn't save the date of birth and just calculate if nsfw is allowed @@ -286,6 +324,8 @@ export class User extends BaseClass { const user = User.create({ username: username, + global_name: username, // TODO: convert to lowercase, strip special characters,etc??? + // display_name: username, // TODO: how should we do this? discriminator, id: id || Snowflake.generate(), email: email, @@ -310,7 +350,7 @@ export class User extends BaseClass { // send verification email if users aren't verified by default and we have an email if (!Config.get().defaults.user.verified && email) { await Email.sendVerifyEmail(user, email).catch((e) => { - console.error(`Failed to send verification email to ${user.username}#${user.discriminator}: ${e}`); + console.error(`Failed to send verification email to ${user.handle}: ${e}`); }); } diff --git a/src/util/util/email/index.ts b/src/util/util/email/index.ts
index ca87e279..08fd1478 100644 --- a/src/util/util/email/index.ts +++ b/src/util/util/email/index.ts
@@ -100,9 +100,12 @@ export const Email: { ) { const { instanceName } = Config.get().general; + // TODO: pomelo: display_name should take precedence over username if pomelo is enabled. maybe we should use global_name as the username? const replacements = [ ["{instanceName}", instanceName], ["{userUsername}", user.username], + ["{userGlobalName}", user.global_name], + ["{userDisplayName}", user.display_name], ["{userDiscriminator}", user.discriminator], ["{userId}", user.id], ["{phoneNumber}", user.phone?.slice(-4)],