From 6034e3059ad6354b7ca78beaab7e5f93db5224ef Mon Sep 17 00:00:00 2001 From: Rory& Date: Wed, 17 Dec 2025 22:25:08 +0100 Subject: [PATCH] Post-rebase fixes --- .../username-attempt-unauthed.ts | 45 +++-- src/api/routes/users/#id/profile.ts | 158 ------------------ src/api/routes/users/@me/pomelo-attempt.ts | 45 +++-- src/api/routes/users/@me/pomelo.ts | 83 ++++----- src/connections/Discord/index.ts | 18 +- src/schemas/api/users/User.ts | 2 + src/schemas/uncategorised/index.ts | 2 - 7 files changed, 85 insertions(+), 268 deletions(-) delete mode 100644 src/api/routes/users/#id/profile.ts diff --git a/src/api/routes/unique-username/username-attempt-unauthed.ts b/src/api/routes/unique-username/username-attempt-unauthed.ts index b225a2996..d1768ee00 100644 --- a/src/api/routes/unique-username/username-attempt-unauthed.ts +++ b/src/api/routes/unique-username/username-attempt-unauthed.ts @@ -1,34 +1,31 @@ import { route } from "@spacebar/api"; -import { Config, UniqueUsernameAttemptSchema, User } from "@spacebar/util"; +import { Config, User } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; +import { UniqueUsernameAttemptSchema } from "@spacebar/schemas"; const router = Router(); router.post( - "/", - route({ - requestBody: "UniqueUsernameAttemptSchema", - responses: { - 200: { body: "UniqueUsernameAttemptResponse" }, - 400: { body: "APIErrorResponse" }, - }, - description: - "Checks whether a unique username is available for the user to claim.", - }), - async (req: Request, res: Response) => { - const body = req.body as UniqueUsernameAttemptSchema; - const { uniqueUsernames } = Config.get().general; - if (!uniqueUsernames) { - throw new HTTPError( - "Unique Usernames feature is not enabled on this instance.", - 400, - ); - } + "/", + route({ + requestBody: "UniqueUsernameAttemptSchema", + responses: { + 200: { body: "UniqueUsernameAttemptResponse" }, + 400: { body: "APIErrorResponse" }, + }, + description: "Checks whether a unique username is available for the user to claim.", + }), + async (req: Request, res: Response) => { + const body = req.body as UniqueUsernameAttemptSchema; + const { uniqueUsernames } = Config.get().general; + if (!uniqueUsernames) { + throw new HTTPError("Unique Usernames feature is not enabled on this instance.", 400); + } - res.json({ - taken: !(await User.isUsernameAvailable(body.username)), - }); - }, + res.json({ + taken: !(await User.isUsernameAvailable(body.username)), + }); + }, ); export default router; diff --git a/src/api/routes/users/#id/profile.ts b/src/api/routes/users/#id/profile.ts deleted file mode 100644 index eecec0f35..000000000 --- a/src/api/routes/users/#id/profile.ts +++ /dev/null @@ -1,158 +0,0 @@ -/* - Spacebar: A FOSS re-implementation and extension of the Discord.com backend. - Copyright (C) 2023 Spacebar and Spacebar Contributors - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -import { route } from "@spacebar/api"; -import { - Member, - PrivateUserProjection, - User, - UserProfileModifySchema, - UserUpdateEvent, - emitEvent, - handleFile, -} from "@spacebar/util"; -import { Request, Response, Router } from "express"; - -const router: Router = Router(); - -router.get( - "/", - route({ responses: { 200: { body: "UserProfileResponse" } } }), - async (req: Request, res: Response) => { - if (req.params.id === "@me") req.params.id = req.user_id; - - const { guild_id, with_mutual_guilds } = req.query; - - const user = await User.getPublicUser(req.params.id, { - relations: ["connected_accounts"], - }); - - const mutual_guilds: object[] = []; - let premium_guild_since; - - if (with_mutual_guilds == "true") { - const requested_member = await Member.find({ - where: { id: req.params.id }, - }); - const self_member = await Member.find({ - where: { id: req.user_id }, - }); - - for (const rmem of requested_member) { - if (rmem.premium_since) { - if (premium_guild_since) { - if (premium_guild_since > rmem.premium_since) { - premium_guild_since = rmem.premium_since; - } - } else { - premium_guild_since = rmem.premium_since; - } - } - for (const smem of self_member) { - if (smem.guild_id === rmem.guild_id) { - mutual_guilds.push({ - id: rmem.guild_id, - nick: rmem.nick, - }); - } - } - } - } - - const guild_member = - guild_id && typeof guild_id == "string" - ? await Member.findOneOrFail({ - where: { id: req.params.id, guild_id: guild_id }, - relations: ["roles"], - }) - : undefined; - - // TODO: make proper DTO's in util? - - const userProfile = { - bio: req.user_bot ? null : user.bio, - accent_color: user.accent_color, - banner: user.banner, - pronouns: user.pronouns, - theme_colors: user.theme_colors, - }; - - const guildMemberProfile = { - accent_color: null, - banner: guild_member?.banner || null, - bio: guild_member?.bio || "", - guild_id, - }; - res.json({ - connected_accounts: user.connected_accounts.filter( - (x) => x.visibility != 0, - ), - 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: user.toPublicUser(), - premium_type: user.premium_type, - profile_themes_experiment_bucket: 4, // TODO: This doesn't make it available, for some reason? - user_profile: userProfile, - guild_member: guild_member?.toPublicMember(), - guild_member_profile: guild_id && guildMemberProfile, - }); - }, -); - -router.patch( - "/", - route({ requestBody: "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, - ); - const user = await User.findOneOrFail({ - where: { id: req.user_id }, - select: [...PrivateUserProjection, "data"], - }); - - user.assign(body); - await user.save(); - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @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, - theme_colors: user.theme_colors, - pronouns: user.pronouns, - }); - }, -); - -export default router; diff --git a/src/api/routes/users/@me/pomelo-attempt.ts b/src/api/routes/users/@me/pomelo-attempt.ts index d4327f618..69438c300 100644 --- a/src/api/routes/users/@me/pomelo-attempt.ts +++ b/src/api/routes/users/@me/pomelo-attempt.ts @@ -1,35 +1,32 @@ import { route } from "@spacebar/api"; -import { Config, UniqueUsernameAttemptSchema, User } from "@spacebar/util"; +import { Config, User } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; +import { UniqueUsernameAttemptSchema } from "@spacebar/schemas"; const router = Router(); // https://discord-userdoccers.vercel.app/resources/user#get-pomelo-eligibility router.post( - "/", - route({ - requestBody: "UniqueUsernameAttemptSchema", - responses: { - 200: { body: "UniqueUsernameAttemptResponse" }, - 400: { body: "APIErrorResponse" }, - }, - description: - "Checks whether a unique username is available for the user to claim.", - }), - async (req: Request, res: Response) => { - const body = req.body as UniqueUsernameAttemptSchema; - const { uniqueUsernames } = Config.get().general; - if (!uniqueUsernames) { - throw new HTTPError( - "Unique Usernames feature is not enabled on this instance.", - 400, - ); - } + "/", + route({ + requestBody: "UniqueUsernameAttemptSchema", + responses: { + 200: { body: "UniqueUsernameAttemptResponse" }, + 400: { body: "APIErrorResponse" }, + }, + description: "Checks whether a unique username is available for the user to claim.", + }), + async (req: Request, res: Response) => { + const body = req.body as UniqueUsernameAttemptSchema; + const { uniqueUsernames } = Config.get().general; + if (!uniqueUsernames) { + throw new HTTPError("Unique Usernames feature is not enabled on this instance.", 400); + } - res.json({ - taken: !(await User.isUsernameAvailable(body.username)), - }); - }, + res.json({ + taken: !(await User.isUsernameAvailable(body.username)), + }); + }, ); export default router; diff --git a/src/api/routes/users/@me/pomelo.ts b/src/api/routes/users/@me/pomelo.ts index e10beaa41..11e0517d3 100644 --- a/src/api/routes/users/@me/pomelo.ts +++ b/src/api/routes/users/@me/pomelo.ts @@ -1,61 +1,52 @@ import { route } from "@spacebar/api"; -import { - Config, - FieldErrors, - UniqueUsernameAttemptSchema, - User, -} from "@spacebar/util"; +import { Config, FieldErrors, User } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; +import { UniqueUsernameAttemptSchema } from "@spacebar/schemas"; const router = Router(); // https://discord-userdoccers.vercel.app/resources/user#create-pomelo-migration router.post( - "/", - route({ - description: - "Claims a unique username for the user. Returns the updated user object on success. Fires a User Update Gateway event.", - requestBody: "UniqueUsernameAttemptSchema", - responses: { - 200: { body: "PrivateUserResponse" }, - 400: { body: "APIErrorResponse" }, - }, - }), - async (req: Request, res: Response) => { - const body = req.body as UniqueUsernameAttemptSchema; - const { uniqueUsernames } = Config.get().general; - if (!uniqueUsernames) { - throw new HTTPError( - "Unique Usernames feature is not enabled on this instance.", - 400, - ); - } + "/", + route({ + description: "Claims a unique username for the user. Returns the updated user object on success. Fires a User Update Gateway event.", + requestBody: "UniqueUsernameAttemptSchema", + responses: { + 200: { body: "PrivateUserResponse" }, + 400: { body: "APIErrorResponse" }, + }, + }), + async (req: Request, res: Response) => { + const body = req.body as UniqueUsernameAttemptSchema; + const { uniqueUsernames } = Config.get().general; + if (!uniqueUsernames) { + throw new HTTPError("Unique Usernames feature is not enabled on this instance.", 400); + } - const isAvailable = await User.isUsernameAvailable(body.username); + const isAvailable = await User.isUsernameAvailable(body.username); - if (!isAvailable) { - throw FieldErrors({ - username: { - code: "USERNAME_TOO_MANY_USERS", - message: - req?.t("auth:register.USERNAME_TOO_MANY_USERS") || "", - }, - }); - } + if (!isAvailable) { + throw FieldErrors({ + username: { + code: "USERNAME_TOO_MANY_USERS", + message: req?.t("auth:register.USERNAME_TOO_MANY_USERS") || "", + }, + }); + } - const user = await User.findOneOrFail({ - where: { - id: req.user_id, - }, - }); + const user = await User.findOneOrFail({ + where: { + id: req.user_id, + }, + }); - user.legacy_username = user.username; - user.username = body.username; - user.discriminator = "0"; - const newUser = await user.save(); + user.legacy_username = user.username; + user.username = body.username; + user.discriminator = "0"; + const newUser = await user.save(); - res.json(newUser.toPrivateUser()); - }, + res.json(newUser.toPrivateUser()); + }, ); export default router; diff --git a/src/connections/Discord/index.ts b/src/connections/Discord/index.ts index 3f5585580..9661ba1cc 100644 --- a/src/connections/Discord/index.ts +++ b/src/connections/Discord/index.ts @@ -16,15 +16,7 @@ along with this program. If not, see . */ -import { - Config, - ConnectedAccount, - ConnectedAccountCommonOAuthTokenResponse, - Connection, - ConnectionCallbackSchema, - ConnectionLoader, - DiscordApiErrors, -} from "@spacebar/util"; +import { Config, ConnectedAccount, ConnectedAccountCommonOAuthTokenResponse, Connection, ConnectionLoader, DiscordApiErrors } from "@spacebar/util"; import wretch from "wretch"; import { DiscordSettings } from "./DiscordSettings"; import { ConnectionCallbackSchema } from "@spacebar/schemas"; @@ -33,7 +25,7 @@ interface UserResponse { id: string; username: string; discriminator: string; - global_name: string | null; + global_name: string | null; avatar_url: string | null; } @@ -122,14 +114,12 @@ export default class DiscordConnection extends Connection { if (exists) return null; - const { uniqueUsernames } = Config.get().general; + const { uniqueUsernames } = Config.get().general; return await this.createConnection({ user_id: userId, external_id: userInfo.id, friend_sync: params.friend_sync, - name: uniqueUsernames - ? userInfo.username - : `${userInfo.username}#${userInfo.discriminator}`, + name: uniqueUsernames ? userInfo.username : `${userInfo.username}#${userInfo.discriminator}`, type: this.id, }); } diff --git a/src/schemas/api/users/User.ts b/src/schemas/api/users/User.ts index 41d606730..df3fa3433 100644 --- a/src/schemas/api/users/User.ts +++ b/src/schemas/api/users/User.ts @@ -57,6 +57,7 @@ interface UserEntityPleaseRewriteThankYou { fingerprints: string[]; settings?: UserSettingsSchema; badge_ids?: string[]; + global_name?: string; } export interface PartialUser { @@ -109,6 +110,7 @@ export interface PrimaryGuild { export enum PublicUserEnum { username, + global_name, discriminator, id, public_flags, diff --git a/src/schemas/uncategorised/index.ts b/src/schemas/uncategorised/index.ts index 2d6779032..99c3a20a6 100644 --- a/src/schemas/uncategorised/index.ts +++ b/src/schemas/uncategorised/index.ts @@ -84,8 +84,6 @@ export * from "./UserGuildSettingsSchema"; export * from "./UserModifySchema"; export * from "./UserNoteUpdateSchema"; export * from "./UserProfileModifySchema"; -export * from "./UserSettingsSchema"; -export * from "./Validator"; export * from "./VanityUrlSchema"; export * from "./VerifyEmailSchema"; export * from "./VoiceStateUpdateSchema";