summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/api/routes/applications/#id/bot/index.ts113
-rw-r--r--src/api/routes/applications/#id/index.ts51
-rw-r--r--src/api/routes/applications/index.ts24
-rw-r--r--src/util/entities/Application.ts1
-rw-r--r--src/util/schemas/ApplicationCreateSchema.ts4
-rw-r--r--src/util/schemas/ApplicationModifySchema.ts14
-rw-r--r--src/util/schemas/BotModifySchema.ts4
-rw-r--r--src/util/schemas/Validator.ts1
-rw-r--r--src/util/schemas/index.ts5
9 files changed, 132 insertions, 85 deletions
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