Post-rebase fixes

This commit is contained in:
Rory&
2025-12-17 22:25:08 +01:00
parent 3670664080
commit 6034e3059a
7 changed files with 85 additions and 268 deletions

View File

@@ -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;

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -16,15 +16,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
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,
});
}

View File

@@ -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,

View File

@@ -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";