Application tester routes

This commit is contained in:
Rory& committed 2026-11-02 20:01:59 +01:00
1 parent 0c7106a578
commit 249afd11d5
7 files changed
+195 -1

No files matched your search

BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
@@ -0,0 +1,114 @@
/*
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 { Request, Response, Router } from "express";
import { route } from "@spacebar/api/middlewares";
import { Application, User } from "@spacebar/database";
import { Config, DiscordApiErrors } from "@spacebar/util";
import { AllowlistAddRequestSchema, AllowlistedUser, ApplicationAllowlistResponse, ApplicationMembershipState, PublicUserProjection } from "@spacebar/schemas";
const router: Router = Router({ mergeParams: true });
router.get(
"/",
route({
responses: {
200: {
body: "ApplicationAllowlistResponse",
},
},
}),
async (req: Request, res: Response) => {
// TODO: stub
const app = await Application.findOneOrFail({
where: { id: req.params.id as string },
relations: { bot: true, owner: true },
select: {
owner: { id: true },
},
});
if (req.user_id != app.owner.id) throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
return res.json([] satisfies ApplicationAllowlistResponse);
},
);
router.post(
"/",
route({
requestBody: "AllowlistAddRequestSchema",
responses: {
200: {
body: "AllowlistedUser",
},
},
}),
async (req: Request, res: Response) => {
// TODO: stub
const app = await Application.findOneOrFail({
where: { id: req.params.id as string },
relations: { bot: true, owner: true },
select: {
owner: { id: true },
},
});
if (req.user_id != app.owner.id) throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
if (Number(1) >= Config.get().limits.application.maxTesters) throw DiscordApiErrors.MAXIMUM_OAUTH2_APPLICATIONS; // TODO: correct error
const body = req.body as AllowlistAddRequestSchema;
const targetUser = await User.findOneOrFail({
where: {
username: body.username,
discriminator: body.discriminator ?? "0",
},
});
return res.json({
user: targetUser.toPartialUser(),
state: ApplicationMembershipState.INVITED,
} satisfies AllowlistedUser);
},
);
router.delete(
"/:user_id",
route({
responses: {
204: {},
},
}),
async (req: Request, res: Response) => {
// TODO: stub
const app = await Application.findOneOrFail({
where: { id: req.params.id as string },
relations: { bot: true, owner: true },
select: {
owner: Object.fromEntries(PublicUserProjection.map((x) => [x, true])),
},
});
if (req.user_id != app.owner.id) throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
return res.status(204).json({});
},
);
export default router;
+1 -1
View File
@@ -35,7 +35,7 @@ router.get(
}),
async (req: Request, res: Response) => {
const app = await Application.findOneOrFail({
where: { id: req.params.id as string }, // ...huh? there's no ID in the path...
where: { id: req.user_id as string },
relations: { bot: true, owner: true },
select: {
owner: Object.fromEntries(PublicUserProjection.map((x) => [x, true])),
@@ -0,0 +1,62 @@
/*
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 { Request, Response, Router } from "express";
import { route } from "@spacebar/api/middlewares";
import { Application, User } from "@spacebar/database";
import { Config, DiscordApiErrors } from "@spacebar/util";
import {
AllowlistAcceptSchema,
AllowlistAddRequestSchema,
AllowlistedUser,
ApplicationAllowlistResponse,
ApplicationMembershipState,
PublicUserProjection,
} from "@spacebar/schemas";
const router: Router = Router({ mergeParams: true });
router.post(
"/accept",
route({
requestBody: "AllowlistAcceptSchema",
responses: {
200: {
body: "AllowlistedUser",
},
},
}),
async (req: Request, res: Response) => {
// TODO: stub
const app = await Application.findOneOrFail({
where: { id: req.params.id as string },
relations: { bot: true, owner: true },
select: {
owner: { id: true },
},
});
if (req.user_id != app.owner.id) throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
const body = req.body as AllowlistAcceptSchema;
return res.status(204).json({});
},
);
export default router;
+17
View File
@@ -344,3 +344,20 @@ export enum EmbeddedActivityReleasePhase {
SOFT_LAUNCH_MULTI_GEO = "soft_launch_multi_geo",
GLOBAL_LAUNCH = "global_launch",
}
export type ApplicationAllowlistResponse = AllowlistedUser[];
export interface AllowlistedUser {
user: PartialUser;
state: ApplicationMembershipState;
}
export enum ApplicationMembershipState {
INVITED = 1,
ACCEPTED = 2,
}
export interface AllowlistAddRequestSchema {
username: string;
discriminator?: string | null;
}
export interface AllowlistAcceptSchema {
token: string;
}
@@ -18,6 +18,7 @@
export class ApplicationLimits {
maxEmojis: number = 1000;
maxTesters: number = 50;
commandLimits: CommandLimits = new CommandLimits();
}