From a9bff7554a4c2508b55b04302681b4874d138f43 Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Wed, 30 Apr 2025 15:07:34 -0500 Subject: [PATCH 01/11] fix moving channels --- src/api/routes/guilds/#guild_id/channels.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/routes/guilds/#guild_id/channels.ts b/src/api/routes/guilds/#guild_id/channels.ts index 3488b64d..5b07ae81 100644 --- a/src/api/routes/guilds/#guild_id/channels.ts +++ b/src/api/routes/guilds/#guild_id/channels.ts @@ -150,7 +150,7 @@ router.patch( }), Channel.findOneOrFail({ where: { id: opt.parent_id as string }, - select: { permission_overwrites: true }, + select: { permission_overwrites: true, id: true }, }), ]); From a4c50ae922e018227c731c0ff9895159546e9b6a Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Wed, 30 Apr 2025 15:17:45 -0500 Subject: [PATCH 02/11] forgot to test on rebuild --- src/api/routes/guilds/#guild_id/channels.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/api/routes/guilds/#guild_id/channels.ts b/src/api/routes/guilds/#guild_id/channels.ts index 5b07ae81..179128eb 100644 --- a/src/api/routes/guilds/#guild_id/channels.ts +++ b/src/api/routes/guilds/#guild_id/channels.ts @@ -163,6 +163,8 @@ router.patch( const parentPos = notMentioned.indexOf(parent.id); notMentioned.splice(parentPos + 1, 0, channel.id); channel.position = (parentPos + 1) as number; + channel.parent = parent; + await channel.save(); await emitEvent({ event: "CHANNEL_UPDATE", From af70a7f21bb0c0629f56d8590feddc3adde85580 Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Wed, 30 Apr 2025 19:10:07 -0500 Subject: [PATCH 03/11] channel is nullable fixes --- assets/openapi.json | 5 +++- assets/schemas.json | 5 +++- src/api/routes/guilds/#guild_id/channels.ts | 33 +++++++++++++-------- src/util/schemas/ChannelReorderSchema.ts | 2 +- 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/assets/openapi.json b/assets/openapi.json index c3124e1e..09be25e6 100644 --- a/assets/openapi.json +++ b/assets/openapi.json @@ -5171,7 +5171,10 @@ "type": "boolean" }, "parent_id": { - "type": "string" + "type": [ + "null", + "string" + ] } }, "additionalProperties": false, diff --git a/assets/schemas.json b/assets/schemas.json index 44c77a77..e95b0ba7 100755 --- a/assets/schemas.json +++ b/assets/schemas.json @@ -59954,7 +59954,10 @@ "type": "boolean" }, "parent_id": { - "type": "string" + "type": [ + "null", + "string" + ] } }, "additionalProperties": false, diff --git a/src/api/routes/guilds/#guild_id/channels.ts b/src/api/routes/guilds/#guild_id/channels.ts index 179128eb..a38b9ae7 100644 --- a/src/api/routes/guilds/#guild_id/channels.ts +++ b/src/api/routes/guilds/#guild_id/channels.ts @@ -120,8 +120,9 @@ router.patch( (x) => !body.find((c) => c.id == x), ); - const withParents = body.filter((x) => x.parent_id != undefined); - const withPositions = body.filter((x) => x.position != undefined); + const withParents = body.filter((x) => x.parent_id !== undefined); + console.log(body); + const withPositions = body.filter((x) => x.position !== undefined); await Promise.all( withPositions.map(async (opt) => { @@ -148,22 +149,30 @@ router.patch( Channel.findOneOrFail({ where: { id: opt.id }, }), - Channel.findOneOrFail({ - where: { id: opt.parent_id as string }, - select: { permission_overwrites: true, id: true }, - }), + opt.parent_id + ? Channel.findOneOrFail({ + where: { id: opt.parent_id }, + select: { + permission_overwrites: true, + id: true, + }, + }) + : null, ]); - if (opt.lock_permissions) + if (opt.lock_permissions && parent) await Channel.update( { id: channel.id }, { permission_overwrites: parent.permission_overwrites }, ); - - const parentPos = notMentioned.indexOf(parent.id); - notMentioned.splice(parentPos + 1, 0, channel.id); - channel.position = (parentPos + 1) as number; - channel.parent = parent; + if (parent) { + const parentPos = notMentioned.indexOf(parent.id); + notMentioned.splice(parentPos + 1, 0, channel.id); + channel.position = (parentPos + 1) as number; + } + channel.parent = parent || undefined; + channel.parent_id = null; + console.log(channel.parent); await channel.save(); await emitEvent({ diff --git a/src/util/schemas/ChannelReorderSchema.ts b/src/util/schemas/ChannelReorderSchema.ts index a026608a..657ed420 100644 --- a/src/util/schemas/ChannelReorderSchema.ts +++ b/src/util/schemas/ChannelReorderSchema.ts @@ -20,5 +20,5 @@ export type ChannelReorderSchema = { id: string; position?: number; lock_permissions?: boolean; - parent_id?: string; + parent_id?: null | string; }[]; From 5488874476cb31a8d57b75a09282f6edb524807e Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Wed, 30 Apr 2025 19:11:43 -0500 Subject: [PATCH 04/11] delete the null eater --- src/api/util/handlers/route.ts | 3 +-- src/util/schemas/Validator.ts | 39 +--------------------------------- 2 files changed, 2 insertions(+), 40 deletions(-) diff --git a/src/api/util/handlers/route.ts b/src/api/util/handlers/route.ts index 2c98783a..d09bbe55 100644 --- a/src/api/util/handlers/route.ts +++ b/src/api/util/handlers/route.ts @@ -28,7 +28,6 @@ import { ajv, getPermission, getRights, - normalizeBody, } from "@spacebar/util"; import { AnyValidateFunction } from "ajv/dist/core"; import { NextFunction, Request, Response } from "express"; @@ -121,7 +120,7 @@ export function route(opts: RouteOptions) { } if (validate) { - const valid = validate(normalizeBody(req.body)); + const valid = validate(req.body); if (!valid) { const fields: Record< string, diff --git a/src/util/schemas/Validator.ts b/src/util/schemas/Validator.ts index 1de511d3..9a7c1ebb 100644 --- a/src/util/schemas/Validator.ts +++ b/src/util/schemas/Validator.ts @@ -46,44 +46,7 @@ export const ajv = new Ajv({ addFormats(ajv); export function validateSchema(schema: string, data: G): G { - const valid = ajv.validate(schema, normalizeBody(data)); + const valid = ajv.validate(schema, data); if (!valid) throw ajv.errors; return data; } - -// Normalizer is introduced to workaround https://github.com/ajv-validator/ajv/issues/1287 -// this removes null values as ajv doesn't treat them as undefined -// normalizeBody allows to handle circular structures without issues -// taken from https://github.com/serverless/serverless/blob/master/lib/classes/ConfigSchemaHandler/index.js#L30 (MIT license) -export const normalizeBody = (body: object = {}) => { - const normalizedObjectsSet = new WeakSet(); - const normalizeObject = (object: object) => { - if (normalizedObjectsSet.has(object)) return; - normalizedObjectsSet.add(object); - if (Array.isArray(object)) { - for (const [, value] of object.entries()) { - if (typeof value === "object") normalizeObject(value); - } - } else { - for (const [key, value] of Object.entries(object)) { - if (value == null) { - if ( - key === "icon" || - key === "avatar" || - key === "banner" || - key === "splash" || - key === "discovery_splash" - ) - continue; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - delete object[key]; - } else if (typeof value === "object") { - normalizeObject(value); - } - } - } - }; - normalizeObject(body); - return body; -}; From 246b493d768c71414276921052e3e86bdf0ea046 Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Wed, 30 Apr 2025 21:23:33 -0500 Subject: [PATCH 05/11] remove logs --- src/api/routes/guilds/#guild_id/channels.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/api/routes/guilds/#guild_id/channels.ts b/src/api/routes/guilds/#guild_id/channels.ts index a38b9ae7..fe1b8d4b 100644 --- a/src/api/routes/guilds/#guild_id/channels.ts +++ b/src/api/routes/guilds/#guild_id/channels.ts @@ -121,7 +121,6 @@ router.patch( ); const withParents = body.filter((x) => x.parent_id !== undefined); - console.log(body); const withPositions = body.filter((x) => x.position !== undefined); await Promise.all( @@ -172,7 +171,6 @@ router.patch( } channel.parent = parent || undefined; channel.parent_id = null; - console.log(channel.parent); await channel.save(); await emitEvent({ From 1550ccd099791dd1efb182215c354d832803350f Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Wed, 30 Apr 2025 22:05:36 -0500 Subject: [PATCH 06/11] don't set possition if sent --- src/api/routes/guilds/#guild_id/channels.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/routes/guilds/#guild_id/channels.ts b/src/api/routes/guilds/#guild_id/channels.ts index fe1b8d4b..bcf6ae27 100644 --- a/src/api/routes/guilds/#guild_id/channels.ts +++ b/src/api/routes/guilds/#guild_id/channels.ts @@ -164,7 +164,7 @@ router.patch( { id: channel.id }, { permission_overwrites: parent.permission_overwrites }, ); - if (parent) { + if (parent && opt.position === undefined) { const parentPos = notMentioned.indexOf(parent.id); notMentioned.splice(parentPos + 1, 0, channel.id); channel.position = (parentPos + 1) as number; From 91361ea9c6d4734805adc5298e0b868d200ce4d0 Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Wed, 30 Apr 2025 22:18:42 -0500 Subject: [PATCH 07/11] fix another stupid bug --- src/api/routes/guilds/#guild_id/channels.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/routes/guilds/#guild_id/channels.ts b/src/api/routes/guilds/#guild_id/channels.ts index bcf6ae27..a2add70f 100644 --- a/src/api/routes/guilds/#guild_id/channels.ts +++ b/src/api/routes/guilds/#guild_id/channels.ts @@ -170,7 +170,7 @@ router.patch( channel.position = (parentPos + 1) as number; } channel.parent = parent || undefined; - channel.parent_id = null; + channel.parent_id = parent?.id || null; await channel.save(); await emitEvent({ From 04ddfadbb6287097c26037a4a151bdb18c2fc49e Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Wed, 30 Apr 2025 22:49:40 -0500 Subject: [PATCH 08/11] sort array to make algorithm happy --- src/api/routes/guilds/#guild_id/channels.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/api/routes/guilds/#guild_id/channels.ts b/src/api/routes/guilds/#guild_id/channels.ts index a2add70f..2eccb3e1 100644 --- a/src/api/routes/guilds/#guild_id/channels.ts +++ b/src/api/routes/guilds/#guild_id/channels.ts @@ -108,13 +108,32 @@ router.patch( async (req: Request, res: Response) => { // changes guild channel position const { guild_id } = req.params; - const body = req.body as ChannelReorderSchema; + let body = req.body as ChannelReorderSchema; const guild = await Guild.findOneOrFail({ where: { id: guild_id }, select: { channel_ordering: true }, }); + body = body.sort((a, b) => { + const apos = + a.position || + (a.parent_id + ? guild.channel_ordering.findIndex( + (_) => _ === a.parent_id, + ) + 1 + : 0); + const bpos = + b.position || + (b.parent_id + ? guild.channel_ordering.findIndex( + (_) => _ === b.parent_id, + ) + 1 + : 0); + console.log(apos, bpos); + return apos - bpos; + }); + // The channels not listed for this query const notMentioned = guild.channel_ordering.filter( (x) => !body.find((c) => c.id == x), From 4f6211ce50529b3fbf19c246182d7b3d752c8133 Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Thu, 1 May 2025 20:45:44 -0500 Subject: [PATCH 09/11] remove missed log --- src/api/routes/guilds/#guild_id/channels.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/api/routes/guilds/#guild_id/channels.ts b/src/api/routes/guilds/#guild_id/channels.ts index 2eccb3e1..86f94e12 100644 --- a/src/api/routes/guilds/#guild_id/channels.ts +++ b/src/api/routes/guilds/#guild_id/channels.ts @@ -130,7 +130,6 @@ router.patch( (_) => _ === b.parent_id, ) + 1 : 0); - console.log(apos, bpos); return apos - bpos; }); From e219c1a33afefce8a875e8c320b0b0866baa85d6 Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Thu, 1 May 2025 22:50:01 -0500 Subject: [PATCH 10/11] code relies on things done in a certain order This makes sure that the code gets done in the order that is expected --- src/api/routes/guilds/#guild_id/channels.ts | 104 ++++++++++---------- 1 file changed, 50 insertions(+), 54 deletions(-) diff --git a/src/api/routes/guilds/#guild_id/channels.ts b/src/api/routes/guilds/#guild_id/channels.ts index 86f94e12..0160641a 100644 --- a/src/api/routes/guilds/#guild_id/channels.ts +++ b/src/api/routes/guilds/#guild_id/channels.ts @@ -140,65 +140,61 @@ router.patch( const withParents = body.filter((x) => x.parent_id !== undefined); const withPositions = body.filter((x) => x.position !== undefined); + // You can't do it with Promise.all or the way this is being done is super incorrect + for await (const opt of withPositions) { + const channel = await Channel.findOneOrFail({ + where: { id: opt.id }, + }); - await Promise.all( - withPositions.map(async (opt) => { - const channel = await Channel.findOneOrFail({ - where: { id: opt.id }, - }); - - channel.position = opt.position as number; - notMentioned.splice(opt.position as number, 0, channel.id); - - await emitEvent({ - event: "CHANNEL_UPDATE", - data: channel, - channel_id: channel.id, - guild_id, - } as ChannelUpdateEvent); - }), - ); + channel.position = opt.position as number; + notMentioned.splice(opt.position as number, 0, channel.id); + await emitEvent({ + event: "CHANNEL_UPDATE", + data: channel, + channel_id: channel.id, + guild_id, + } as ChannelUpdateEvent); + } + // Due to this also being able to change the order, this needs to be done in order // have to do the parents after the positions - await Promise.all( - withParents.map(async (opt) => { - const [channel, parent] = await Promise.all([ - Channel.findOneOrFail({ - where: { id: opt.id }, - }), - opt.parent_id - ? Channel.findOneOrFail({ - where: { id: opt.parent_id }, - select: { - permission_overwrites: true, - id: true, - }, - }) - : null, - ]); + for await (const opt of withParents) { + const [channel, parent] = await Promise.all([ + Channel.findOneOrFail({ + where: { id: opt.id }, + }), + opt.parent_id + ? Channel.findOneOrFail({ + where: { id: opt.parent_id }, + select: { + permission_overwrites: true, + id: true, + }, + }) + : null, + ]); - if (opt.lock_permissions && parent) - await Channel.update( - { id: channel.id }, - { permission_overwrites: parent.permission_overwrites }, - ); - if (parent && opt.position === undefined) { - const parentPos = notMentioned.indexOf(parent.id); - notMentioned.splice(parentPos + 1, 0, channel.id); - channel.position = (parentPos + 1) as number; - } - channel.parent = parent || undefined; - channel.parent_id = parent?.id || null; - await channel.save(); + if (opt.lock_permissions && parent) + await Channel.update( + { id: channel.id }, + { permission_overwrites: parent.permission_overwrites }, + ); + if (parent && opt.position === undefined) { + const parentPos = notMentioned.indexOf(parent.id); + notMentioned.splice(parentPos + 1, 0, channel.id); + channel.position = (parentPos + 1) as number; + } + channel.parent = parent || undefined; + channel.parent_id = parent?.id || null; + await channel.save(); - await emitEvent({ - event: "CHANNEL_UPDATE", - data: channel, - channel_id: channel.id, - guild_id, - } as ChannelUpdateEvent); - }), - ); + await emitEvent({ + event: "CHANNEL_UPDATE", + data: channel, + channel_id: channel.id, + guild_id, + } as ChannelUpdateEvent); + } await Guild.update( { id: guild_id }, From f03c6209a42ae3e922bdb86954de04f1ae6daaa8 Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Fri, 2 May 2025 11:35:07 -0500 Subject: [PATCH 11/11] send the real index --- src/api/routes/guilds/#guild_id/channels.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/routes/guilds/#guild_id/channels.ts b/src/api/routes/guilds/#guild_id/channels.ts index 0160641a..8f8ee3af 100644 --- a/src/api/routes/guilds/#guild_id/channels.ts +++ b/src/api/routes/guilds/#guild_id/channels.ts @@ -146,8 +146,8 @@ router.patch( where: { id: opt.id }, }); - channel.position = opt.position as number; notMentioned.splice(opt.position as number, 0, channel.id); + channel.position = notMentioned.findIndex((_) => _ === channel.id); await emitEvent({ event: "CHANNEL_UPDATE",