1
0
mirror of https://github.com/spacebarchat/server.git synced 2024-11-26 04:03:03 +01:00

Refactor applications

This commit is contained in:
Madeline 2022-12-18 23:45:29 +11:00
parent b9bd5c5ac4
commit 970e67fe65
No known key found for this signature in database
GPG Key ID: 1958E017C36F2E47
11 changed files with 1701 additions and 85 deletions

View File

@ -18,7 +18,7 @@
API_VERSION: 9, API_VERSION: 9,
API_ENDPOINT: "/api", API_ENDPOINT: "/api",
WEBAPP_ENDPOINT: "", WEBAPP_ENDPOINT: "",
CDN_HOST: `${location.hostname}:3003`, CDN_HOST: `${location.host}`, // TODO: make this file auto populate from config?
BRAINTREE_KEY: "production_5st77rrc_49pp2rp4phym7387", BRAINTREE_KEY: "production_5st77rrc_49pp2rp4phym7387",
STRIPE_KEY: "pk_live_CUQtlpQUF0vufWpnpUmQvcdi", STRIPE_KEY: "pk_live_CUQtlpQUF0vufWpnpUmQvcdi",

File diff suppressed because it is too large Load Diff

View File

@ -1,81 +1,78 @@
import { Request, Response, Router } from "express"; import { Request, Response, Router } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Application, Config, FieldErrors, generateToken, OrmUtils, Snowflake, trimSpecial, User } from "@fosscord/util"; import { Application, generateToken, User, BotModifySchema, handleFile, DiscordApiErrors } from "@fosscord/util";
import { HTTPError } from "lambert-server"; import { HTTPError } from "lambert-server";
import { verifyToken } from "node-2fa"; import { verifyToken } from "node-2fa";
const router: Router = Router(); const router: Router = Router();
router.post("/", route({}), async (req: Request, res: Response) => { router.post("/", route({}), async (req: Request, res: Response) => {
const app = await Application.findOne({where: {id: req.params.id}}); const app = await Application.findOneOrFail({ where: { id: req.params.id }, relations: ["owner"] });
if(!app) return res.status(404);
const username = trimSpecial(app.name);
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",
message: req?.t("auth:register.USERNAME_TOO_MANY_USERS"),
},
});
}
const user = OrmUtils.mergeDeep(new User(), { if (app.owner.id != req.user_id)
created_at: new Date(), throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
username: username,
discriminator, const user = await User.register({
id: app.id, username: app.name,
bot: true, password: undefined,
system: false, req,
premium_since: new Date(),
desktop: false,
mobile: false,
premium: true,
premium_type: 2,
bio: app.description,
mfa_enabled: false,
totp_secret: "",
totp_backup_codes: [],
verified: true,
disabled: false,
deleted: false,
email: null,
rights: Config.get().security.defaultRights,
nsfw_allowed: true,
public_flags: "0",
flags: "0",
data: {
hash: null,
valid_tokens_since: new Date(),
},
settings: {},
extended_settings: {},
fingerprints: [],
notes: {},
}); });
user.id = app.id;
user.premium_since = new Date();
user.bot = true;
await user.save(); await user.save();
app.bot = user;
// flags is NaN here?
app.assign({ bot: user, flags: app.flags || 0 });
await app.save(); await app.save();
res.send().status(204)
res.send().status(204);
}); });
router.post("/reset", route({}), async (req: Request, res: Response) => { router.post("/reset", route({}), async (req: Request, res: Response) => {
let bot = await User.findOne({where: {id: req.params.id}}); let bot = await User.findOneOrFail({ where: { id: req.params.id } });
let owner = await User.findOne({where: {id: req.user_id}}); let owner = await User.findOneOrFail({ where: { id: req.user_id } });
if(!bot) return res.status(404);
if(owner?.totp_secret && (!req.body.code || verifyToken(owner.totp_secret, req.body.code))) { if (owner.id != req.user_id)
throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
if (owner.totp_secret && (!req.body.code || verifyToken(owner.totp_secret, req.body.code)))
throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008); throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
}
bot.data = { hash: undefined, valid_tokens_since: new Date() }; bot.data = { hash: undefined, valid_tokens_since: new Date() };
await bot.save(); await bot.save();
let token = await generateToken(bot.id); let token = await generateToken(bot.id);
res.json({token}).status(200);
res.json({ token }).status(200);
}); });
router.patch("/", route({}), async (req: Request, res: Response) => { router.patch("/", route({ body: "BotModifySchema" }), async (req: Request, res: Response) => {
delete req.body.avatar; const body = req.body as BotModifySchema;
let app = OrmUtils.mergeDeep(await User.findOne({where: {id: req.params.id}}), req.body); if (!body.avatar?.trim()) delete body.avatar;
const app = await Application.findOneOrFail({ where: { id: req.params.id }, relations: ["bot", "owner"] });
if (!app.bot)
throw DiscordApiErrors.BOT_ONLY_ENDPOINT;
if (app.owner.id != req.user_id)
throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
if (body.avatar)
body.avatar = await handleFile(
`/avatars/${app.id}`,
body.avatar as string,
);
app.bot.assign(body);
app.bot.save();
await app.save(); await app.save();
res.json(app).status(200); res.json(app).status(200);
}); });

View File

@ -1,28 +1,55 @@
import { Request, Response, Router } from "express"; import { Request, Response, Router } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Application, OrmUtils, Team, trimSpecial, User } from "@fosscord/util"; import { Application, OrmUtils, DiscordApiErrors, ApplicationModifySchema, User } from "@fosscord/util";
import { verifyToken } from "node-2fa";
import { HTTPError } from "lambert-server";
const router: Router = Router(); const router: Router = Router();
router.get("/", route({}), async (req: Request, res: Response) => { router.get("/", route({}), async (req: Request, res: Response) => {
let results = await Application.findOne({where: {id: req.params.id}, relations: ["owner", "bot"] }); const app = await Application.findOneOrFail({ where: { id: req.params.id }, relations: ["owner", "bot"] });
res.json(results).status(200); if (app.owner.id != req.user_id)
throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
return res.json(app);
}); });
router.patch("/", route({}), async (req: Request, res: Response) => { router.patch("/", route({ body: "ApplicationModifySchema" }), async (req: Request, res: Response) => {
delete req.body.icon; const body = req.body as ApplicationModifySchema;
let app = OrmUtils.mergeDeep(await Application.findOne({where: {id: req.params.id}, relations: ["owner", "bot"]}), req.body);
if(app.bot) { const app = await Application.findOneOrFail({ where: { id: req.params.id }, relations: ["owner", "bot"] });
app.bot.bio = req.body.description
app.bot?.save(); if (app.owner.id != req.user_id)
throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
if (app.owner.totp_secret && (!req.body.code || verifyToken(app.owner.totp_secret, req.body.code)))
throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
if (app.bot) {
app.bot.assign({ bio: body.description });
await app.bot.save();
} }
if(req.body.tags) app.tags = req.body.tags;
app.assign(body);
await app.save(); await app.save();
res.json(app).status(200);
return res.json(app);
}); });
router.post("/delete", route({}), async (req: Request, res: Response) => { router.post("/delete", route({}), async (req: Request, res: Response) => {
await Application.delete(req.params.id); const app = await Application.findOneOrFail({ where: { id: req.params.id }, relations: ["bot", "owner"] });
if (app.owner.id != req.user_id)
throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
if (app.owner.totp_secret && (!req.body.code || verifyToken(app.owner.totp_secret, req.body.code)))
throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
if (app.bot)
await User.delete({ id: app.bot.id });
await Application.delete({ id: app.id });
res.send().status(200); res.send().status(200);
}); });

View File

@ -1,35 +1,31 @@
import { Request, Response, Router } from "express"; import { Request, Response, Router } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Application, OrmUtils, Team, trimSpecial, User } from "@fosscord/util"; import { Application, ApplicationCreateSchema, trimSpecial, User } from "@fosscord/util";
const router: Router = Router(); const router: Router = Router();
export interface ApplicationCreateSchema {
name: string;
team_id?: string | number;
}
router.get("/", route({}), async (req: Request, res: Response) => { router.get("/", route({}), async (req: Request, res: Response) => {
//TODO let results = await Application.find({ where: { owner: { id: req.user_id } }, relations: ["owner", "bot"] });
let results = await Application.find({where: {owner: {id: req.user_id}}, relations: ["owner", "bot"] });
res.json(results).status(200); res.json(results).status(200);
}); });
router.post("/", route({}), async (req: Request, res: Response) => { router.post("/", route({ body: "ApplicationCreateSchema" }), async (req: Request, res: Response) => {
const body = req.body as ApplicationCreateSchema; const body = req.body as ApplicationCreateSchema;
const user = await User.findOne({where: {id: req.user_id}}) const user = await User.findOneOrFail({ where: { id: req.user_id } });
if(!user) res.status(420);
let app = OrmUtils.mergeDeep(new Application(), { const app = Application.create({
name: trimSpecial(body.name), name: trimSpecial(body.name),
description: "", description: "",
bot_public: true, bot_public: true,
bot_require_code_grant: false, bot_require_code_grant: false,
owner: user, owner: user,
verify_key: "IMPLEMENTME", verify_key: "IMPLEMENTME",
flags: "" flags: 0,
}); });
await app.save(); await app.save();
res.json(app).status(200);
res.json(app);
}); });
export default router; export default router;

View File

@ -37,6 +37,7 @@ export class Application extends BaseClass {
@ManyToOne(() => User) @ManyToOne(() => User)
owner: User; owner: User;
// TODO: enum this? https://discord.com/developers/docs/resources/application#application-object-application-flags
@Column() @Column()
flags: number = 0; flags: number = 0;

View File

@ -0,0 +1,4 @@
export interface ApplicationCreateSchema {
name: string;
team_id?: string | number;
}

View File

@ -0,0 +1,14 @@
export interface ApplicationModifySchema {
description?: string;
icon?: string;
interactions_endpoint_url?: string;
max_participants?: number | null;
name?: string;
privacy_policy_url?: string;
role_connections_verification_url?: string;
tags?: string[];
terms_of_service_url?: string;
bot_public?: boolean;
bot_require_code_grant?: boolean;
flags?: number;
}

View File

@ -0,0 +1,4 @@
export interface BotModifySchema {
avatar?: string;
username?: string;
}

View File

@ -22,6 +22,7 @@ export const ajv = new Ajv({
messages: true, messages: true,
strict: true, strict: true,
strictRequired: true, strictRequired: true,
allowUnionTypes: true,
}); });
addFormats(ajv); addFormats(ajv);

View File

@ -45,4 +45,7 @@ export * from "./UserGuildSettingsSchema";
export * from "./GatewayPayloadSchema"; export * from "./GatewayPayloadSchema";
export * from "./RolePositionUpdateSchema"; export * from "./RolePositionUpdateSchema";
export * from "./ChannelReorderSchema"; export * from "./ChannelReorderSchema";
export * from "./UserSettingsSchema"; export * from "./UserSettingsSchema";
export * from "./BotModifySchema";
export * from "./ApplicationModifySchema";
export * from "./ApplicationCreateSchema";