Merge branch 'master' into feat/local-image-proxy

This commit is contained in:
TomatoCake authored and GitHub committed 2024-07-18 16:08:06 +02:00
commit d79669f771
69 files changed
+929 -282

No files matched your search

+11 -7
View File
@@ -18,15 +18,15 @@
import {
Config,
Email,
initDatabase,
initEvent,
JSONReplacer,
registerRoutes,
Sentry,
WebAuthn,
ConnectionConfig,
ConnectionLoader,
Email,
JSONReplacer,
Sentry,
WebAuthn,
initDatabase,
initEvent,
registerRoutes,
} from "@spacebar/util";
import { Request, Response, Router } from "express";
import { Server, ServerOptions } from "lambert-server";
@@ -143,6 +143,10 @@ export class SpacebarServer extends Server {
res.sendFile(path.join(PUBLIC_ASSETS_FOLDER, "index.html")),
);
app.get("/verify", (req, res) =>
res.sendFile(path.join(PUBLIC_ASSETS_FOLDER, "verify.html")),
);
this.app.use(ErrorHandler);
Sentry.errorHandler(this.app);
+1 -1
View File
@@ -85,7 +85,7 @@ router.post(
user = userTokenData.user;
} catch {
throw FieldErrors({
password: {
token: {
message: req.t("auth:password_reset.INVALID_TOKEN"),
code: "INVALID_TOKEN",
},
@@ -50,7 +50,13 @@ router.get(
const channel = await Channel.findOneOrFail({
where: { id: channel_id },
});
if (!channel.guild_id) return res.send(channel);
channel.position = await Channel.calculatePosition(
channel_id,
channel.guild_id,
channel.guild,
);
return res.send(channel);
},
);
@@ -1,17 +1,17 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -56,6 +56,7 @@ router.post(
edited_timestamp: null,
flags: 1,
components: [],
poll: {},
}).status(200);
},
);
@@ -1,17 +1,17 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -91,11 +91,10 @@ router.patch(
}
} else rights.hasThrow("SELF_EDIT_MESSAGES");
// @ts-expect-error Something is wrong with message_reference here, TS complains since "channel_id" is optional in MessageCreateSchema
const new_message = await handleMessage({
...message,
// TODO: should message_reference be overridable?
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
message_reference: message.message_reference,
...body,
author_id: message.author_id,
@@ -1,17 +1,17 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -183,9 +183,17 @@ router.get(
const uri = y.proxy_url.startsWith("http")
? y.proxy_url
: `https://example.org${y.proxy_url}`;
y.proxy_url = `${endpoint == null ? "" : endpoint}${
new URL(uri).pathname
}`;
let pathname = new URL(uri).pathname;
while (
pathname.split("/")[0] != "attachments" &&
pathname.length > 30
) {
pathname = pathname.split("/").slice(1).join("/");
}
if (!endpoint?.endsWith("/")) pathname = "/" + pathname;
y.proxy_url = `${endpoint == null ? "" : endpoint}${pathname}`;
});
/**
@@ -1,17 +1,17 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -53,6 +53,11 @@ router.put(
where: { id: channel_id },
});
if (!channel.guild_id) throw new HTTPError("Channel not found", 404);
channel.position = await Channel.calculatePosition(
channel_id,
channel.guild_id,
channel.guild,
);
if (body.type === 0) {
if (!(await Role.count({ where: { id: overwrite_id } })))
+45
View File
@@ -0,0 +1,45 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { route } from "@spacebar/api";
import { ConnectionConfig } from "@spacebar/util";
import { Request, Response, Router } from "express";
const router = Router();
router.get(
"/",
route({
responses: {
200: {
body: "APIConnectionsConfiguration",
},
},
}),
async (req: Request, res: Response) => {
const config = ConnectionConfig.get();
Object.keys(config).forEach((key) => {
delete config[key].clientId;
delete config[key].clientSecret;
});
res.json(config);
},
);
export default router;
+17 -3
View File
@@ -1,17 +1,17 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -41,6 +41,15 @@ router.get(
const { guild_id } = req.params;
const channels = await Channel.find({ where: { guild_id } });
for await (const channel of channels) {
channel.position = await Channel.calculatePosition(
channel.id,
guild_id,
channel.guild,
);
}
channels.sort((a, b) => a.position - b.position);
res.json(channels);
},
);
@@ -71,6 +80,11 @@ router.post(
{ ...body, guild_id },
req.user_id,
);
channel.position = await Channel.calculatePosition(
channel.id,
guild_id,
channel.guild,
);
res.status(201).json(channel);
},
+8 -4
View File
@@ -1,17 +1,17 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -125,6 +125,7 @@ router.post(
const user = await User.findOneOrFail({ where: { id: req.user_id } });
body.image = (await handleFile(`/emojis/${id}`, body.image)) as string;
const mimeType = body.image.split(":")[1].split(";")[0];
const emoji = await Emoji.create({
id: id,
guild_id: guild_id,
@@ -132,7 +133,10 @@ router.post(
require_colons: body.require_colons ?? undefined, // schema allows nulls, db does not
user: user,
managed: false,
animated: false, // TODO: Add support animated emojis
animated:
mimeType == "image/gif" ||
mimeType == "image/apng" ||
mimeType == "video/webm",
available: true,
roles: [],
}).save();
@@ -1,17 +1,17 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -162,6 +162,7 @@ router.get(
edited_timestamp: x.edited_timestamp,
flags: x.flags,
components: x.components,
poll: x.poll,
hit: true,
},
]);
+5
View File
@@ -18,6 +18,7 @@
import { route } from "@spacebar/api";
import {
Badge,
Member,
PrivateUserProjection,
User,
@@ -98,6 +99,9 @@ router.get(
bio: guild_member?.bio || "",
guild_id,
};
const badges = await Badge.find();
res.json({
connected_accounts: user.connected_accounts.filter(
(x) => x.visibility != 0,
@@ -111,6 +115,7 @@ router.get(
user_profile: userProfile,
guild_member: guild_member?.toPublicMember(),
guild_member_profile: guild_id && guildMemberProfile,
badges: badges.filter((x) => user.badge_ids?.includes(x.id)),
});
},
);
+37 -23
View File
@@ -1,57 +1,57 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import * as Sentry from "@sentry/node";
import { EmbedHandlers } from "@spacebar/api";
import {
Application,
Attachment,
Channel,
Config,
Embed,
EmbedCache,
emitEvent,
Guild,
Message,
MessageCreateEvent,
MessageUpdateEvent,
EVERYONE_MENTION,
getPermission,
getRights,
Guild,
HERE_MENTION,
Message,
MessageCreateEvent,
MessageCreateSchema,
MessageType,
MessageUpdateEvent,
Role,
ROLE_MENTION,
Sticker,
User,
//CHANNEL_MENTION,
USER_MENTION,
ROLE_MENTION,
Role,
EVERYONE_MENTION,
HERE_MENTION,
MessageType,
User,
Application,
Webhook,
Attachment,
Config,
Sticker,
MessageCreateSchema,
EmbedCache,
} from "@spacebar/util";
import { HTTPError } from "lambert-server";
import { In } from "typeorm";
import { EmbedHandlers } from "@spacebar/api";
import * as Sentry from "@sentry/node";
const allow_empty = false;
// TODO: check webhook, application, system author, stickers
// TODO: embed gifs/videos/images
const LINK_REGEX =
/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/g;
/<?https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)>?/g;
export async function handleMessage(opts: MessageOptions): Promise<Message> {
const channel = await Channel.findOneOrFail({
@@ -66,6 +66,7 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
: undefined;
const message = Message.create({
...opts,
poll: opts.poll,
sticker_items: stickers,
guild_id: channel.guild_id,
channel_id: opts.channel_id,
@@ -116,6 +117,12 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
const guild = await Guild.findOneOrFail({
where: { id: channel.guild_id },
});
if (!opts.message_reference.guild_id)
opts.message_reference.guild_id = channel.guild_id;
if (!opts.message_reference.channel_id)
opts.message_reference.channel_id = opts.channel_id;
if (!guild.features.includes("CROSS_CHANNEL_REPLIES")) {
if (opts.message_reference.guild_id !== channel.guild_id)
throw new HTTPError(
@@ -126,6 +133,8 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
"You can only reference messages from this channel",
);
}
message.message_reference = opts.message_reference;
}
/** Q: should be checked if the referenced message exists? ANSWER: NO
otherwise backfilling won't work **/
@@ -138,7 +147,9 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
!opts.content &&
!opts.embeds?.length &&
!opts.attachments?.length &&
!opts.sticker_ids?.length
!opts.sticker_ids?.length &&
!opts.poll &&
!opts.components?.length
) {
throw new HTTPError("Empty messages are not allowed", 50006);
}
@@ -213,6 +224,9 @@ export async function postHandleMessage(message: Message) {
const cachePromises = [];
for (const link of links) {
// Don't embed links in <>
if (link.startsWith("<") && link.endsWith(">")) continue;
const url = new URL(link);
const cached = await EmbedCache.findOne({ where: { url: link } });