From 16caeea7445b4649225a6237d61a8521a1ccdf58 Mon Sep 17 00:00:00 2001 From: CyberL1 Date: Thu, 4 Dec 2025 19:32:50 +0100 Subject: [PATCH 1/9] fix: include `user` object in `guild_member` --- src/api/routes/users/#user_id/profile.ts | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/api/routes/users/#user_id/profile.ts b/src/api/routes/users/#user_id/profile.ts index 456f0cb74..a5ea3e9c4 100644 --- a/src/api/routes/users/#user_id/profile.ts +++ b/src/api/routes/users/#user_id/profile.ts @@ -17,17 +17,7 @@ */ import { route } from "@spacebar/api"; -import { - Badge, - Config, - emitEvent, - FieldErrors, - handleFile, - Member, - Relationship, - User, - UserUpdateEvent, -} from "@spacebar/util"; +import { Badge, Config, emitEvent, FieldErrors, handleFile, Member, Relationship, User, UserUpdateEvent } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { In } from "typeorm"; import { PrivateUserProjection, PublicUser, PublicUserProjection, RelationshipType, UserProfileModifySchema } from "@spacebar/schemas"; @@ -127,7 +117,7 @@ router.get("/", route({ responses: { 200: { body: "UserProfileResponse" } } }), premium_type: user.premium_type, profile_themes_experiment_bucket: 4, // TODO: This doesn't make it available, for some reason? user_profile: userProfile, - guild_member: guild_member?.toPublicMember(), + guild_member: { ...guild_member?.toPublicMember(), user: user.toPublicUser() }, guild_member_profile: guild_id && guildMemberProfile, badges: badges.filter((x) => user.badge_ids?.includes(x.id)), }); From 1838829716787e1a4fad540cc867f9f3c454d028 Mon Sep 17 00:00:00 2001 From: CyberL1 Date: Fri, 5 Dec 2025 11:14:31 +0100 Subject: [PATCH 2/9] fix: only `guild_ids` is required in guild folder --- assets/openapi.json | Bin 856601 -> 856518 bytes assets/schemas.json | Bin 380213 -> 380154 bytes src/schemas/api/users/UserSettings.ts | 6 +++--- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/openapi.json b/assets/openapi.json index 5d73449d2ca0367103f39f941831459302b60123..ec14fb550c4c13222b30c863d588efd2eb29e2f0 100644 GIT binary patch delta 64 zcmbQa#N^mwlZFFEiCV+On#8bF?uLb~N=NfFEiCV+OurDr$~Il#8jJYk2bmm`4Npr>pD=?(YPx_5BkS~p jL}vYVjj1d^%nHP8K+F!r96-zo#9Tnky9u-~+eW-~=J;Yt<47 diff --git a/src/schemas/api/users/UserSettings.ts b/src/schemas/api/users/UserSettings.ts index 410c35df5..c38e393a0 100644 --- a/src/schemas/api/users/UserSettings.ts +++ b/src/schemas/api/users/UserSettings.ts @@ -62,10 +62,10 @@ export interface CustomStatus { } export interface GuildFolder { - color: number; + color?: number; guild_ids: string[]; - id: number; - name: string; + id?: number; + name?: string; } export interface FriendSourceFlags { From ae20c6dce451674dc87a8bab9e19f11cff32aa3a Mon Sep 17 00:00:00 2001 From: CyberL1 Date: Fri, 5 Dec 2025 17:13:40 +0100 Subject: [PATCH 3/9] fix: add `| null` to guild folder types --- assets/openapi.json | Bin 856518 -> 856644 bytes assets/schemas.json | Bin 380154 -> 380358 bytes src/schemas/api/users/UserSettings.ts | 6 +++--- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/openapi.json b/assets/openapi.json index ec14fb550c4c13222b30c863d588efd2eb29e2f0..ee7a4e8a56ec1352e9da43f6501f03d560444d36 100644 GIT binary patch delta 87 zcmX@M*yP9(lZFFEi7DFEi7DcpoIHoV*WtE@)Ae<#~`U5={ n8&n0;3%pqrr#lERMNR)Ohc#?^JUbIdd(#8v?M)9@D&7D9ZsQ`u delta 49 zcmX>$Tm08d@rD-07N#xCPw!32RGZvzfMa_2edaAd_Vft?EasCxJcw@he#pGt`yos9 F8vr;e7X1JK diff --git a/src/schemas/api/users/UserSettings.ts b/src/schemas/api/users/UserSettings.ts index c38e393a0..f91a35d7f 100644 --- a/src/schemas/api/users/UserSettings.ts +++ b/src/schemas/api/users/UserSettings.ts @@ -62,10 +62,10 @@ export interface CustomStatus { } export interface GuildFolder { - color?: number; + color?: number | null; guild_ids: string[]; - id?: number; - name?: string; + id?: number | null; + name?: string | null; } export interface FriendSourceFlags { From 84b4888e9b25a4a83977da34142457da5a579c04 Mon Sep 17 00:00:00 2001 From: CyberL1 Date: Fri, 5 Dec 2025 21:52:18 +0100 Subject: [PATCH 4/9] fix: check if `body` is not `null` --- src/api/routes/webhooks/#webhook_id/#token/github.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/api/routes/webhooks/#webhook_id/#token/github.ts b/src/api/routes/webhooks/#webhook_id/#token/github.ts index 419b8d23a..256130b9c 100644 --- a/src/api/routes/webhooks/#webhook_id/#token/github.ts +++ b/src/api/routes/webhooks/#webhook_id/#token/github.ts @@ -168,7 +168,9 @@ const parseGitHubWebhook = (req: Request, res: Response, next: NextFunction) => ]; if (req.body.action === "opened") { - discordPayload.embeds[0].description = req.body.pull_request.body.length > 500 ? `${req.body.pull_request.body.slice(0, 497)}...` : req.body.pull_request.body; + if (req.body.pull_request.body != null) { + discordPayload.embeds[0].description = req.body.pull_request.body.length > 500 ? `${req.body.pull_request.body.slice(0, 497)}...` : req.body.pull_request.body; + } discordPayload.embeds[0].color = 38912; } break; From de1ebc99a7788ab5767e3e803067dec8921f2a39 Mon Sep 17 00:00:00 2001 From: Rory& Date: Sun, 7 Dec 2025 22:32:37 +0100 Subject: [PATCH 5/9] Perhaps, dont IP ban moderators --- assets/openapi.json | Bin 856644 -> 856618 bytes assets/schemas.json | Bin 380358 -> 380340 bytes src/api/routes/guilds/#guild_id/bans.ts | 1 - src/util/entities/Ban.ts | 4 ++-- .../postgres/1765143034407-DontIpBanBanner.ts | 16 ++++++++++++++++ 5 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 src/util/migration/postgres/1765143034407-DontIpBanBanner.ts diff --git a/assets/openapi.json b/assets/openapi.json index ee7a4e8a56ec1352e9da43f6501f03d560444d36..e502af61c79f8dbc67f1737ccd1347040b5b01a3 100644 GIT binary patch delta 95 zcmX@I#AMYHlZFFEi4-jOcw}Y4W2H4o{4R`x-DzWbb%t4_~{LCObOEiOqr~w qFYsqInl8Y_a<#qTFbfc~0x=s9vjZ^)5OV@C7Z7uAZ#c|z#18FEi4-jOkaPHO{#sv0Tv);1!6WJW(Q&pAm#*OE+FRKzTp5* GRUiOUP8@Oo diff --git a/assets/schemas.json b/assets/schemas.json index 576a9c608ded6236737421a1aa14010c4d3104e7..d5b4835ef2fae21b650726a5e2eeeec4a12d1736 100644 GIT binary patch delta 36 scmX>$TYSrG@rD-07N!>FEi7l4CohO$oxb4-b56TI3(IzY7S{5U01Mp>(*OVf delta 46 zcmV+}0MY-nn-|8L7l4ETgaU*Ev;@opm%p_I5|f}@1(&eo18kSRJOKuWLj(l3Lj(nJ E$+CJ8ZvX%Q diff --git a/src/api/routes/guilds/#guild_id/bans.ts b/src/api/routes/guilds/#guild_id/bans.ts index e253bef9f..1274c195d 100644 --- a/src/api/routes/guilds/#guild_id/bans.ts +++ b/src/api/routes/guilds/#guild_id/bans.ts @@ -215,7 +215,6 @@ router.put( const ban = Ban.create({ user_id: banned_user_id, guild_id: guild_id, - ip: getIpAdress(req), executor_id: req.user_id, reason: req.body.reason, // || otherwise empty }); diff --git a/src/util/entities/Ban.ts b/src/util/entities/Ban.ts index d4a7bc1c3..c3c473f47 100644 --- a/src/util/entities/Ban.ts +++ b/src/util/entities/Ban.ts @@ -53,8 +53,8 @@ export class Ban extends BaseClass { @ManyToOne(() => User) executor: User; - @Column() - ip: string; + @Column({ nullable: true }) + ip?: string; @Column({ nullable: true }) reason?: string; diff --git a/src/util/migration/postgres/1765143034407-DontIpBanBanner.ts b/src/util/migration/postgres/1765143034407-DontIpBanBanner.ts new file mode 100644 index 000000000..6e8089efe --- /dev/null +++ b/src/util/migration/postgres/1765143034407-DontIpBanBanner.ts @@ -0,0 +1,16 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class DontIpBanBanner1765143034407 implements MigrationInterface { + name = 'DontIpBanBanner1765143034407' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "bans" ALTER COLUMN "ip" DROP NOT NULL`); + await queryRunner.query(`UPDATE "bans" SET "ip" = NULL`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`UPDATE "bans" SET "ip" = '0.0.0.0' WHERE "ip" IS NULL`); + await queryRunner.query(`ALTER TABLE "bans" ALTER COLUMN "ip" SET NOT NULL`); + } + +} From f78f3abe320df1c3f4ef7916c9c36e12bcd72f2a Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Thu, 11 Dec 2025 22:03:01 -0600 Subject: [PATCH 6/9] fix read del bug --- src/api/routes/channels/#channel_id/messages/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/routes/channels/#channel_id/messages/index.ts b/src/api/routes/channels/#channel_id/messages/index.ts index 8790241d8..43c4984e0 100644 --- a/src/api/routes/channels/#channel_id/messages/index.ts +++ b/src/api/routes/channels/#channel_id/messages/index.ts @@ -548,7 +548,7 @@ router.delete( // TODO: handle other read state types if (body.read_state_type != ReadStateType.CHANNEL) return res.status(204).send(); - const readState = await ReadState.findOne({where: {channel_id}}); + const readState = await ReadState.findOne({ where: { channel_id, user_id: req.user_id } }); if (readState) { await readState.remove(); } From d054febcf17e246bf0ccfbf7f244b6ad20a6965b Mon Sep 17 00:00:00 2001 From: dank074 Date: Tue, 9 Dec 2025 01:17:45 -0600 Subject: [PATCH 7/9] fix: prevent race condition in rabbitmq --- src/gateway/listener/listener.ts | 112 +++++++++++-------------------- src/gateway/util/WebSocket.ts | 4 +- src/util/util/Event.ts | 44 ++++-------- src/util/util/RabbitMQ.ts | 17 +++++ 4 files changed, 70 insertions(+), 107 deletions(-) diff --git a/src/gateway/listener/listener.ts b/src/gateway/listener/listener.ts index d7c3d23e5..75cb6bc61 100644 --- a/src/gateway/listener/listener.ts +++ b/src/gateway/listener/listener.ts @@ -36,7 +36,7 @@ import { WebSocket } from "@spacebar/gateway"; import { Channel as AMQChannel } from "amqplib"; import { Recipient } from "@spacebar/util"; import * as console from "node:console"; -import { PublicMember, RelationshipType } from "@spacebar/schemas" +import { PublicMember, RelationshipType } from "@spacebar/schemas"; import { bgRedBright } from "picocolors"; // TODO: close connection on Invalidated Token @@ -46,10 +46,7 @@ import { bgRedBright } from "picocolors"; // Sharding: calculate if the current shard id matches the formula: shard_id = (guild_id >> 22) % num_shards // https://discord.com/developers/docs/topics/gateway#sharding -export function handlePresenceUpdate( - this: WebSocket, - { event, acknowledge, data }: EventOpts, -) { +export function handlePresenceUpdate(this: WebSocket, { event, acknowledge, data }: EventOpts) { acknowledge?.(); if (event === EVENTEnum.PresenceUpdate) { return Send(this, { @@ -92,32 +89,24 @@ export async function setupListener(this: WebSocket) { this.listen_options = opts; const consumer = consume.bind(this); + const handleChannelError = (err: unknown) => { + console.error(`[RabbitMQ] [user-${this.user_id}] Channel Error (Handled):`, err); + }; + console.log("[RabbitMQ] setupListener: open for ", this.user_id); if (RabbitMQ.connection) { - console.log( - "[RabbitMQ] setupListener: opts.channel = ", - typeof opts.channel, - "with channel id", - opts.channel?.ch, - ); + console.log("[RabbitMQ] setupListener: opts.channel = ", typeof opts.channel, "with channel id", opts.channel?.ch); opts.channel = await RabbitMQ.connection.createChannel(); + + opts.channel.on("error", handleChannelError); opts.channel.queues = {}; - console.log( - "[RabbitMQ] channel created: ", - typeof opts.channel, - "with channel id", - opts.channel?.ch, - ); + console.log("[RabbitMQ] channel created: ", typeof opts.channel, "with channel id", opts.channel?.ch); } this.events[this.user_id] = await listenEvent(this.user_id, consumer, opts); relationships.forEach(async (relationship) => { - this.events[relationship.to_id] = await listenEvent( - relationship.to_id, - handlePresenceUpdate.bind(this), - opts, - ); + this.events[relationship.to_id] = await listenEvent(relationship.to_id, handlePresenceUpdate.bind(this), opts); }); dm_channels.forEach(async (channel) => { @@ -130,33 +119,27 @@ export async function setupListener(this: WebSocket) { this.events[guild.id] = await listenEvent(guild.id, consumer, opts); guild.channels.forEach(async (channel) => { - if ( - permission - .overwriteChannel(channel.permission_overwrites ?? []) - .has("VIEW_CHANNEL") - ) { - this.events[channel.id] = await listenEvent( - channel.id, - consumer, - opts, - ); + if (permission.overwriteChannel(channel.permission_overwrites ?? []).has("VIEW_CHANNEL")) { + this.events[channel.id] = await listenEvent(channel.id, consumer, opts); } }); }); - this.once("close", () => { - console.log( - "[RabbitMQ] setupListener: close for", - this.user_id, - "=", - typeof opts.channel, - "with channel id", - opts.channel?.ch, + this.once("close", async () => { + console.log("[RabbitMQ] setupListener: close for", this.user_id, "=", typeof opts.channel, "with channel id", opts.channel?.ch); + + // wait for event consumer cancellation + await Promise.all( + Object.values(this.events).map((x) => { + if (x) return x(); + else return Promise.resolve(); + }), ); - if (opts.channel) opts.channel.close(); - else { - Object.values(this.events).forEach((x) => x?.()); - Object.values(this.member_events).forEach((x) => x()); + await Promise.all(Object.values(this.member_events).map((x) => x())); + + if (opts.channel) { + await opts.channel.close(); + opts.channel.off("error", handleChannelError); } }); } @@ -180,11 +163,7 @@ async function consume(this: WebSocket, opts: EventOpts) { break; case "GUILD_MEMBER_ADD": if (this.member_events[data.user.id]) break; // already subscribed - this.member_events[data.user.id] = await listenEvent( - data.user.id, - handlePresenceUpdate.bind(this), - this.listen_options, - ); + this.member_events[data.user.id] = await listenEvent(data.user.id, handlePresenceUpdate.bind(this), this.listen_options); break; case "GUILD_MEMBER_UPDATE": if (!this.member_events[data.user.id]) break; @@ -197,32 +176,20 @@ async function consume(this: WebSocket, opts: EventOpts) { delete this.events[id]; break; case "CHANNEL_CREATE": - if ( - !permission - .overwriteChannel(data.permission_overwrites) - .has("VIEW_CHANNEL") - ) { + if (!permission.overwriteChannel(data.permission_overwrites).has("VIEW_CHANNEL")) { return; } this.events[id] = await listenEvent(id, consumer, listenOpts); break; case "RELATIONSHIP_ADD": - this.events[data.user.id] = await listenEvent( - data.user.id, - handlePresenceUpdate.bind(this), - this.listen_options, - ); + this.events[data.user.id] = await listenEvent(data.user.id, handlePresenceUpdate.bind(this), this.listen_options); break; case "GUILD_CREATE": this.events[id] = await listenEvent(id, consumer, listenOpts); break; case "CHANNEL_UPDATE": { const exists = this.events[id]; - if ( - permission - .overwriteChannel(data.permission_overwrites) - .has("VIEW_CHANNEL") - ) { + if (permission.overwriteChannel(data.permission_overwrites).has("VIEW_CHANNEL")) { if (exists) break; this.events[id] = await listenEvent(id, consumer, listenOpts); } else { @@ -294,20 +261,19 @@ async function consume(this: WebSocket, opts: EventOpts) { case "MESSAGE_UPDATE": // console.log(this.request) if (data["attachments"]) - data["attachments"] = - Message.prototype.withSignedAttachments.call( - data, - new NewUrlUserSignatureData({ - ip: this.ipAddress, - userAgent: this.userAgent, - }), - ).attachments; + data["attachments"] = Message.prototype.withSignedAttachments.call( + data, + new NewUrlUserSignatureData({ + ip: this.ipAddress, + userAgent: this.userAgent, + }), + ).attachments; break; default: break; } - if(event === "GUILD_MEMBER_ADD") { + if (event === "GUILD_MEMBER_ADD") { if ((data as PublicMember).roles === undefined || (data as PublicMember).roles === null) { console.log(bgRedBright("[Gateway]"), "[GUILD_MEMBER_ADD] roles is undefined, setting to empty array!", opts.origin ?? "(Event origin not defined)", data); (data as PublicMember).roles = []; diff --git a/src/gateway/util/WebSocket.ts b/src/gateway/util/WebSocket.ts index ea6f0701e..00ef9e98f 100644 --- a/src/gateway/util/WebSocket.ts +++ b/src/gateway/util/WebSocket.ts @@ -44,8 +44,8 @@ export interface WebSocket extends WS { intents: Intents; sequence: number; permissions: Record; - events: Record unknown)>; - member_events: Record unknown>; + events: Record Promise)>; + member_events: Record Promise>; listen_options: ListenEventOpts; capabilities?: Capabilities; large_threshold: number; diff --git a/src/util/util/Event.ts b/src/util/util/Event.ts index f56d66646..ce5c25d25 100644 --- a/src/util/util/Event.ts +++ b/src/util/util/Event.ts @@ -20,30 +20,21 @@ import { Channel } from "amqplib"; import { RabbitMQ } from "./RabbitMQ"; import EventEmitter from "events"; import { EVENT, Event } from "../interfaces"; +import { randomUUID } from "crypto"; export const events = new EventEmitter(); export async function emitEvent(payload: Omit) { - const id = (payload.guild_id || - payload.channel_id || - payload.user_id) as string; + const id = (payload.guild_id || payload.channel_id || payload.user_id) as string; if (!id) return console.error("event doesn't contain any id", payload); if (RabbitMQ.connection) { - const data = - typeof payload.data === "object" - ? JSON.stringify(payload.data) - : payload.data; // use rabbitmq for event transmission + const data = typeof payload.data === "object" ? JSON.stringify(payload.data) : payload.data; // use rabbitmq for event transmission await RabbitMQ.channel?.assertExchange(id, "fanout", { durable: false, }); // assertQueue isn't needed, because a queue will automatically created if it doesn't exist - const successful = RabbitMQ.channel?.publish( - id, - "", - Buffer.from(`${data}`), - { type: payload.event }, - ); + const successful = RabbitMQ.channel?.publish(id, "", Buffer.from(`${data}`), { type: payload.event }); if (!successful) throw new Error("failed to send event"); } else if (process.env.EVENT_TRANSMISSION === "process") { process.send?.({ type: "event", event: payload, id } as ProcessEvent); @@ -79,17 +70,10 @@ export interface ProcessEvent { id: string; } -export async function listenEvent( - event: string, - callback: (event: EventOpts) => unknown, - opts?: ListenEventOpts, -) { +export async function listenEvent(event: string, callback: (event: EventOpts) => unknown, opts?: ListenEventOpts): Promise<() => Promise> { if (RabbitMQ.connection) { const channel = opts?.channel || RabbitMQ.channel; - if (!channel) - throw new Error( - "[Events] An event was sent without an associated channel", - ); + if (!channel) throw new Error("[Events] An event was sent without an associated channel"); return await rabbitListen(channel, event, callback, { acknowledge: opts?.acknowledge, }); @@ -101,9 +85,7 @@ export async function listenEvent( const listener = (msg: ProcessEvent) => { // eslint-disable-next-line @typescript-eslint/no-unused-expressions - msg.type === "event" && - msg.id === event && - callback({ ...msg.event, cancel }); + msg.type === "event" && msg.id === event && callback({ ...msg.event, cancel }); }; // TODO: assert the type is correct? @@ -124,20 +106,17 @@ export async function listenEvent( } } -async function rabbitListen( - channel: Channel, - id: string, - callback: (event: EventOpts) => unknown, - opts?: { acknowledge?: boolean }, -) { +async function rabbitListen(channel: Channel, id: string, callback: (event: EventOpts) => unknown, opts?: { acknowledge?: boolean }): Promise<() => Promise> { await channel.assertExchange(id, "fanout", { durable: false }); const q = await channel.assertQueue("", { exclusive: true, autoDelete: true, }); + const consumerTag = randomUUID(); + const cancel = async () => { - await channel.cancel(q.queue); + await channel.cancel(consumerTag); await channel.unbindQueue(q.queue, id, ""); }; @@ -163,6 +142,7 @@ async function rabbitListen( }, { noAck: !opts?.acknowledge, + consumerTag: consumerTag, }, ); diff --git a/src/util/util/RabbitMQ.ts b/src/util/util/RabbitMQ.ts index 1a61aee93..89e8e140c 100644 --- a/src/util/util/RabbitMQ.ts +++ b/src/util/util/RabbitMQ.ts @@ -34,7 +34,24 @@ export const RabbitMQ: { timeout: 1000 * 60, }); console.log(`[RabbitMQ] connected`); + + // log connection errors + this.connection.on("error", (err) => { + console.error("[RabbitMQ] Connection Error:", err); + }); + + this.connection.on("close", () => { + console.error("[RabbitMQ] connection closed"); + // TODO: Add reconnection logic here if the connection crashes?? + // will be a pain since we will have to reconstruct entire state + }); + this.channel = await this.connection.createChannel(); console.log(`[RabbitMQ] channel created`); + + // log channel errors + this.channel.on("error", (err) => { + console.error("[RabbitMQ] Channel Error:", err); + }); }, }; From bf8add14c1ac4576f2ac6ad781d0d8b4cc207385 Mon Sep 17 00:00:00 2001 From: CyberL1 Date: Fri, 12 Dec 2025 22:19:10 +0100 Subject: [PATCH 8/9] fix: show compare url on multiple commits --- src/api/routes/webhooks/#webhook_id/#token/github.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/routes/webhooks/#webhook_id/#token/github.ts b/src/api/routes/webhooks/#webhook_id/#token/github.ts index 256130b9c..ef47c1d9e 100644 --- a/src/api/routes/webhooks/#webhook_id/#token/github.ts +++ b/src/api/routes/webhooks/#webhook_id/#token/github.ts @@ -243,7 +243,7 @@ const parseGitHubWebhook = (req: Request, res: Response, next: NextFunction) => url: req.body.sender.html_url, }, title: `[${req.body.repository.name}:${req.body.ref.slice(11)}] ${req.body.commits.length} new commit${req.body.commits.length > 1 ? "s" : ""}`, - url: req.body.head_commit.url, + url: req.body.commits.length > 1 ? req.body.compare : req.body.head_commit.url, description: req.body.commits .slice(0, 5) // Discord only shows 5 first commits .map( From 16df560edde1ec212f5a0023145d830342ee6e32 Mon Sep 17 00:00:00 2001 From: CyberL1 Date: Fri, 12 Dec 2025 22:27:57 +0100 Subject: [PATCH 9/9] fix: some config stuff --- src/util/util/Config.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/util/util/Config.ts b/src/util/util/Config.ts index a79c908d7..496f21cd4 100644 --- a/src/util/util/Config.ts +++ b/src/util/util/Config.ts @@ -194,9 +194,9 @@ function validateFinalConfig(config: ConfigValue) { } assertConfig("api_endpointPublic", v => v != null, "A valid public API endpoint URL, ex. \"http://localhost:3001/api/v9\""); - assertConfig("cdn_endpointPublic", v => v != null, "A valid public CDN endpoint URL, ex. \"http://localhost:3002/\""); - assertConfig("cdn_endpointPrivate", v => v != null, "A valid private CDN endpoint URL, ex. \"http://localhost:3002/\" - must be routable from the API server!"); - assertConfig("gateway_endpointPublic", v => v != null, "A valid public gateway endpoint URL, ex. \"ws://localhost:3003/\""); + assertConfig("cdn_endpointPublic", v => v != null, "A valid public CDN endpoint URL, ex. \"http://localhost:3003/\""); + assertConfig("cdn_endpointPrivate", v => v != null, "A valid private CDN endpoint URL, ex. \"http://localhost:3003/\" - must be routable from the API server!"); + assertConfig("gateway_endpointPublic", v => v != null, "A valid public gateway endpoint URL, ex. \"ws://localhost:3002/\""); if (hasErrors) { console.error(