diff --git a/src/api/routes/applications/#id/bot/index.ts b/src/api/routes/applications/#id/bot/index.ts
index ad2399b8..ed5d6a70 100644
--- a/src/api/routes/applications/#id/bot/index.ts
+++ b/src/api/routes/applications/#id/bot/index.ts
@@ -1,81 +1,78 @@
import { Request, Response, Router } from "express";
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 { verifyToken } from "node-2fa";
const router: Router = Router();
router.post("/", route({}), async (req: Request, res: Response) => {
- const app = await Application.findOne({where: {id: req.params.id}});
- 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(), {
- created_at: new Date(),
- username: username,
- discriminator,
- id: app.id,
- bot: true,
- system: false,
- 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: {},
+ const app = await Application.findOneOrFail({ where: { id: req.params.id }, relations: ["owner"] });
+
+ if (app.owner.id != req.user_id)
+ throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
+
+ const user = await User.register({
+ username: app.name,
+ password: undefined,
+ req,
});
+
+ user.id = app.id;
+ user.premium_since = new Date();
+ user.bot = true;
+
await user.save();
- app.bot = user;
+
+ // flags is NaN here?
+ app.assign({ bot: user, flags: app.flags || 0 });
+
await app.save();
- res.send().status(204)
+
+ res.send().status(204);
});
router.post("/reset", route({}), async (req: Request, res: Response) => {
- let bot = await User.findOne({where: {id: req.params.id}});
- let owner = await User.findOne({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))) {
+ let bot = await User.findOneOrFail({ where: { id: req.params.id } });
+ let owner = await User.findOneOrFail({ where: { id: req.user_id } });
+
+ 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);
- }
+
bot.data = { hash: undefined, valid_tokens_since: new Date() };
+
await bot.save();
+
let token = await generateToken(bot.id);
- res.json({token}).status(200);
+
+ res.json({ token }).status(200);
});
-router.patch("/", route({}), async (req: Request, res: Response) => {
- delete req.body.avatar;
- let app = OrmUtils.mergeDeep(await User.findOne({where: {id: req.params.id}}), req.body);
+router.patch("/", route({ body: "BotModifySchema" }), async (req: Request, res: Response) => {
+ const body = req.body as BotModifySchema;
+ 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();
res.json(app).status(200);
});
diff --git a/src/api/routes/applications/#id/index.ts b/src/api/routes/applications/#id/index.ts
index 0aced582..79df256a 100644
--- a/src/api/routes/applications/#id/index.ts
+++ b/src/api/routes/applications/#id/index.ts
@@ -1,28 +1,55 @@
import { Request, Response, Router } from "express";
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();
router.get("/", route({}), async (req: Request, res: Response) => {
- let results = await Application.findOne({where: {id: req.params.id}, relations: ["owner", "bot"] });
- res.json(results).status(200);
+ const app = await Application.findOneOrFail({ where: { id: req.params.id }, relations: ["owner", "bot"] });
+ 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) => {
- delete req.body.icon;
- let app = OrmUtils.mergeDeep(await Application.findOne({where: {id: req.params.id}, relations: ["owner", "bot"]}), req.body);
- if(app.bot) {
- app.bot.bio = req.body.description
- app.bot?.save();
+router.patch("/", route({ body: "ApplicationModifySchema" }), async (req: Request, res: Response) => {
+ const body = req.body as ApplicationModifySchema;
+
+ const app = await Application.findOneOrFail({ where: { id: req.params.id }, relations: ["owner", "bot"] });
+
+ 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();
- res.json(app).status(200);
+
+ return res.json(app);
});
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);
});
diff --git a/src/api/routes/applications/index.ts b/src/api/routes/applications/index.ts
index 41ce35b5..70af84e6 100644
--- a/src/api/routes/applications/index.ts
+++ b/src/api/routes/applications/index.ts
@@ -1,35 +1,31 @@
import { Request, Response, Router } from "express";
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();
-export interface ApplicationCreateSchema {
- name: string;
- team_id?: string | number;
-}
-
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);
});
-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 user = await User.findOne({where: {id: req.user_id}})
- if(!user) res.status(420);
- let app = OrmUtils.mergeDeep(new Application(), {
+ const user = await User.findOneOrFail({ where: { id: req.user_id } });
+
+ const app = Application.create({
name: trimSpecial(body.name),
description: "",
bot_public: true,
bot_require_code_grant: false,
owner: user,
verify_key: "IMPLEMENTME",
- flags: ""
+ flags: 0,
});
+
await app.save();
- res.json(app).status(200);
+
+ res.json(app);
});
export default router;
\ No newline at end of file
diff --git a/src/util/entities/Application.ts b/src/util/entities/Application.ts
index 28381579..861c5bdd 100644
--- a/src/util/entities/Application.ts
+++ b/src/util/entities/Application.ts
@@ -37,6 +37,7 @@ export class Application extends BaseClass {
@ManyToOne(() => User)
owner: User;
+ // TODO: enum this? https://discord.com/developers/docs/resources/application#application-object-application-flags
@Column()
flags: number = 0;
diff --git a/src/util/schemas/ApplicationCreateSchema.ts b/src/util/schemas/ApplicationCreateSchema.ts
new file mode 100644
index 00000000..6a021b46
--- /dev/null
+++ b/src/util/schemas/ApplicationCreateSchema.ts
@@ -0,0 +1,4 @@
+export interface ApplicationCreateSchema {
+ name: string;
+ team_id?: string | number;
+}
\ No newline at end of file
diff --git a/src/util/schemas/ApplicationModifySchema.ts b/src/util/schemas/ApplicationModifySchema.ts
new file mode 100644
index 00000000..ab23d57e
--- /dev/null
+++ b/src/util/schemas/ApplicationModifySchema.ts
@@ -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;
+}
\ No newline at end of file
diff --git a/src/util/schemas/BotModifySchema.ts b/src/util/schemas/BotModifySchema.ts
new file mode 100644
index 00000000..b801ab27
--- /dev/null
+++ b/src/util/schemas/BotModifySchema.ts
@@ -0,0 +1,4 @@
+export interface BotModifySchema {
+ avatar?: string;
+ username?: string;
+}
\ No newline at end of file
diff --git a/src/util/schemas/Validator.ts b/src/util/schemas/Validator.ts
index e85cdf7b..9b7f0eca 100644
--- a/src/util/schemas/Validator.ts
+++ b/src/util/schemas/Validator.ts
@@ -22,6 +22,7 @@ export const ajv = new Ajv({
messages: true,
strict: true,
strictRequired: true,
+ allowUnionTypes: true,
});
addFormats(ajv);
diff --git a/src/util/schemas/index.ts b/src/util/schemas/index.ts
index 780022c6..58565496 100644
--- a/src/util/schemas/index.ts
+++ b/src/util/schemas/index.ts
@@ -45,4 +45,7 @@ export * from "./UserGuildSettingsSchema";
export * from "./GatewayPayloadSchema";
export * from "./RolePositionUpdateSchema";
export * from "./ChannelReorderSchema";
-export * from "./UserSettingsSchema";
\ No newline at end of file
+export * from "./UserSettingsSchema";
+export * from "./BotModifySchema";
+export * from "./ApplicationModifySchema";
+export * from "./ApplicationCreateSchema";
\ No newline at end of file
|