Merge branch 'master' into pr/Thesourtimes/430

This commit is contained in:
Flam3rboy
2021-10-11 15:13:16 +02:00
77 changed files with 973 additions and 611 deletions

View File

@@ -1,19 +1,17 @@
import { OptionsJson } from "body-parser";
import "missing-native-js-functions";
import { Connection } from "mongoose";
import { Server, ServerOptions } from "lambert-server";
import { Authentication, CORS } from "./middlewares/";
import { Config, initDatabase, initEvent } from "@fosscord/util";
import { ErrorHandler } from "./middlewares/ErrorHandler";
import { BodyParser } from "./middlewares/BodyParser";
import { Router, Request, Response, NextFunction } from "express";
import mongoose from "mongoose";
import path from "path";
import { initRateLimits } from "./middlewares/RateLimit";
import TestClient from "./middlewares/TestClient";
import { initTranslation } from "./middlewares/Translation";
import morgan from "morgan";
import { initInstance } from "./util/Instance";
import { registerRoutes } from "@fosscord/util";
export interface FosscordServerOptions extends ServerOptions {}
@@ -75,7 +73,7 @@ export class FosscordServer extends Server {
await initRateLimits(api);
await initTranslation(api);
this.routes = await this.registerRoutes(path.join(__dirname, "routes", "/"));
this.routes = await registerRoutes(this, path.join(__dirname, "routes", "/"));
api.use("*", (error: any, req: Request, res: Response, next: NextFunction) => {
if (error) return next(error);

View File

@@ -2,7 +2,7 @@ import { Router, Request, Response } from "express";
import { HTTPError } from "lambert-server";
import { route } from "@fosscord/api";
import { random } from "@fosscord/api";
import { getPermission, Channel, Invite, InviteCreateEvent, emitEvent, User, Guild, PublicInviteRelation } from "@fosscord/util";
import { Channel, Invite, InviteCreateEvent, emitEvent, User, Guild, PublicInviteRelation } from "@fosscord/util";
import { isTextChannel } from "./messages";
const router: Router = Router();

View File

@@ -1,4 +1,4 @@
import { emitEvent, getPermission, MessageAckEvent, ReadState } from "@fosscord/util";
import { emitEvent, getPermission, MessageAckEvent, ReadState, Snowflake } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api";
@@ -18,7 +18,11 @@ router.post("/", route({ body: "MessageAcknowledgeSchema" }), async (req: Reques
const permission = await getPermission(req.user_id, undefined, channel_id);
permission.hasThrow("VIEW_CHANNEL");
await ReadState.update({ user_id: req.user_id, channel_id }, { user_id: req.user_id, channel_id, last_message_id: message_id });
let read_state = await ReadState.findOne({ user_id: req.user_id, channel_id });
if (!read_state) read_state = new ReadState({ user_id: req.user_id, channel_id });
read_state.last_message_id = message_id;
await read_state.save();
await emitEvent({
event: "MESSAGE_ACK",

View File

@@ -22,7 +22,7 @@ const router: Router = Router();
export default router;
function isTextChannel(type: ChannelType): boolean {
export function isTextChannel(type: ChannelType): boolean {
switch (type) {
case ChannelType.GUILD_STORE:
case ChannelType.GUILD_VOICE:
@@ -39,7 +39,6 @@ function isTextChannel(type: ChannelType): boolean {
return true;
}
}
module.exports.isTextChannel = isTextChannel;
export interface MessageCreateSchema {
content?: string;
@@ -103,6 +102,7 @@ router.get("/", async (req: Request, res: Response) => {
}
const messages = await Message.find(query);
const endpoint = Config.get().cdn.endpointPublic;
return res.json(
messages.map((x) => {
@@ -115,7 +115,9 @@ router.get("/", async (req: Request, res: Response) => {
// @ts-ignore
if (!x.author) x.author = { discriminator: "0000", username: "Deleted User", public_flags: "0", avatar: null };
x.attachments?.forEach((x) => {
x.proxy_url = `${Config.get().cdn.endpointPublic || "http://localhost:3003"}${new URL(x.proxy_url).pathname}`;
// dynamically set attachment proxy_url in case the endpoint changed
const uri = x.proxy_url.startsWith("http") ? x.proxy_url : `https://example.org${x.proxy_url}`;
x.proxy_url = `${endpoint == null ? "http://localhost:3003" : endpoint}${new URL(uri).pathname}`;
});
return x;

View File

@@ -44,8 +44,8 @@ router.put(
};
channel.permission_overwrites!.push(overwrite);
}
overwrite.allow = String(req.permission!.bitfield & (BigInt(body.allow) || 0n));
overwrite.deny = String(req.permission!.bitfield & (BigInt(body.deny) || 0n));
overwrite.allow = String(req.permission!.bitfield & (BigInt(body.allow) || BigInt("0")));
overwrite.deny = String(req.permission!.bitfield & (BigInt(body.deny) || BigInt("0")));
await Promise.all([
channel.save(),

View File

@@ -0,0 +1,121 @@
import { Router, Request, Response } from "express";
import { Config, DiscordApiErrors, emitEvent, Emoji, GuildEmojisUpdateEvent, handleFile, Member, Snowflake, User } from "@fosscord/util";
import { route } from "@fosscord/api";
const router = Router();
export interface EmojiCreateSchema {
name?: string;
image: string;
require_colons?: boolean | null;
roles?: string[];
}
export interface EmojiModifySchema {
name?: string;
roles?: string[];
}
router.get("/", route({}), async (req: Request, res: Response) => {
const { guild_id } = req.params;
await Member.IsInGuildOrFail(req.user_id, guild_id);
const emojis = await Emoji.find({ where: { guild_id: guild_id }, relations: ["user"] });
return res.json(emojis);
});
router.get("/:emoji_id", route({}), async (req: Request, res: Response) => {
const { guild_id, emoji_id } = req.params;
await Member.IsInGuildOrFail(req.user_id, guild_id);
const emoji = await Emoji.findOneOrFail({ where: { guild_id: guild_id, id: emoji_id }, relations: ["user"] });
return res.json(emoji);
});
router.post("/", route({ body: "EmojiCreateSchema", permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => {
const { guild_id } = req.params;
const body = req.body as EmojiCreateSchema;
const emoji_count = await Emoji.count({ guild_id: guild_id });
const { maxEmojis } = Config.get().limits.guild;
if (emoji_count >= maxEmojis) throw DiscordApiErrors.MAXIMUM_NUMBER_OF_EMOJIS_REACHED.withParams(maxEmojis);
const id = Snowflake.generate();
if (body.require_colons == null) body.require_colons = true;
const user = await User.findOneOrFail({ id: req.user_id });
body.image = (await handleFile(`/emojis/${id}`, body.image)) as string;
const emoji = await new Emoji({
id: id,
guild_id: guild_id,
...body,
user: user,
managed: false,
animated: false, // TODO: Add support animated emojis
available: true,
roles: []
}).save();
await emitEvent({
event: "GUILD_EMOJIS_UPDATE",
guild_id: guild_id,
data: {
guild_id: guild_id,
emojis: await Emoji.find({ guild_id: guild_id })
}
} as GuildEmojisUpdateEvent);
return res.status(201).json(emoji);
});
router.patch(
"/:emoji_id",
route({ body: "EmojiModifySchema", permission: "MANAGE_EMOJIS_AND_STICKERS" }),
async (req: Request, res: Response) => {
const { emoji_id, guild_id } = req.params;
const body = req.body as EmojiModifySchema;
const emoji = await new Emoji({ ...body, id: emoji_id, guild_id: guild_id }).save();
await emitEvent({
event: "GUILD_EMOJIS_UPDATE",
guild_id: guild_id,
data: {
guild_id: guild_id,
emojis: await Emoji.find({ guild_id: guild_id })
}
} as GuildEmojisUpdateEvent);
return res.json(emoji);
}
);
router.delete("/:emoji_id", route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => {
const { emoji_id, guild_id } = req.params;
await Emoji.delete({
id: emoji_id,
guild_id: guild_id
});
await emitEvent({
event: "GUILD_EMOJIS_UPDATE",
guild_id: guild_id,
data: {
guild_id: guild_id,
emojis: await Emoji.find({ guild_id: guild_id })
}
} as GuildEmojisUpdateEvent);
res.sendStatus(204);
});
export default router;

View File

@@ -17,7 +17,7 @@ const router: Router = Router();
export interface RoleModifySchema {
name?: string;
permissions?: bigint;
permissions?: string;
color?: number;
hoist?: boolean; // whether the role should be displayed separately in the sidebar
mentionable?: boolean; // whether the role should be mentionable
@@ -57,7 +57,7 @@ router.post("/", route({ body: "RoleModifySchema", permission: "MANAGE_ROLES" })
...body,
guild_id: guild_id,
managed: false,
permissions: String(req.permission!.bitfield & (body.permissions || 0n)),
permissions: String(req.permission!.bitfield & BigInt(body.permissions || "0")),
tags: undefined
});
@@ -105,7 +105,12 @@ router.patch("/:role_id", route({ body: "RoleModifySchema", permission: "MANAGE_
const { role_id, guild_id } = req.params;
const body = req.body as RoleModifySchema;
const role = new Role({ ...body, id: role_id, guild_id, permissions: String(req.permission!.bitfield & (body.permissions || 0n)) });
const role = new Role({
...body,
id: role_id,
guild_id,
permissions: String(req.permission!.bitfield & BigInt(body.permissions || "0"))
});
await Promise.all([
role.save(),

View File

@@ -10,10 +10,10 @@ const InviteRegex = /\W/g;
router.get("/", route({ permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => {
const { guild_id } = req.params;
const guild = await Guild.findOneOrFail({ where: { id: guild_id }, relations: ["vanity_url"] });
if (!guild.vanity_url) return res.json({ code: null });
const invite = await Invite.findOne({ where: { guild_id: guild_id, vanity_url: true } });
if (!invite) return res.json({ code: null });
return res.json({ code: guild.vanity_url_code, uses: guild.vanity_url.uses });
return res.json({ code: invite.code, uses: invite.uses });
});
export interface VanityUrlSchema {
@@ -33,20 +33,9 @@ router.patch("/", route({ body: "VanityUrlSchema", permission: "MANAGE_GUILD" })
const invite = await Invite.findOne({ code });
if (invite) throw new HTTPError("Invite already exists");
const guild = await Guild.findOneOrFail({ id: guild_id });
const { id } = await Channel.findOneOrFail({ guild_id, type: ChannelType.GUILD_TEXT });
Promise.all([
Guild.update({ id: guild_id }, { vanity_url_code: code }),
Invite.delete({ code: guild.vanity_url_code }),
new Invite({
code: code,
uses: 0,
created_at: new Date(),
guild_id,
channel_id: id
}).save()
]);
await Invite.update({ vanity_url: true, guild_id }, { code: code, channel_id: id });
return res.json({ code: code });
});

View File

@@ -47,7 +47,7 @@ router.post("/:code", route({ body: "GuildTemplateCreateSchema" }), async (req:
managed: true,
mentionable: true,
name: "@everyone",
permissions: 2251804225n,
permissions: BigInt("2251804225"),
position: 0,
tags: null
}).save()

View File

@@ -33,7 +33,6 @@ router.delete("/:code", route({}), async (req: Request, res: Response) => {
await Promise.all([
Invite.delete({ code }),
Guild.update({ vanity_url_code: code }, { vanity_url_code: undefined }),
emitEvent({
event: "INVITE_DELETE",
guild_id: guild_id,

View File

@@ -1,37 +0,0 @@
const jwa = require("jwa");
var STR64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".split("");
function base64url(string: string, encoding: string) {
// @ts-ignore
return Buffer.from(string, encoding).toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
}
function to64String(input: number, current = ""): string {
if (input < 0 && current.length == 0) {
input = input * -1;
}
var modify = input % 64;
var remain = Math.floor(input / 64);
var result = STR64[modify] + current;
return remain <= 0 ? result : to64String(remain, result);
}
function to64Parse(input: string) {
var result = 0;
var toProc = input.split("");
var e;
for (e in toProc) {
result = result * 64 + STR64.indexOf(toProc[e]);
}
return result;
}
// @ts-ignore
const start = `${base64url("311129357362135041")}.${to64String(Date.now())}`;
const signature = jwa("HS256").sign(start, `test`);
const token = `${start}.${signature}`;
console.log(token);
// MzExMTI5MzU3MzYyMTM1MDQx.XdQb_rA.907VgF60kocnOTl32MSUWGSSzbAytQ0jbt36KjLaxuY
// MzExMTI5MzU3MzYyMTM1MDQx.XdQbaPy.4vGx4L7IuFJGsRe6IL3BeybLIvbx4Vauvx12pwNsy2U

View File

@@ -1,13 +0,0 @@
import jwt from "jsonwebtoken";
const algorithm = "HS256";
const iat = Math.floor(Date.now() / 1000);
// @ts-ignore
const token = jwt.sign({ id: "311129357362135041" }, "secret", {
algorithm,
});
console.log(token);
const decoded = jwt.verify(token, "secret", { algorithms: [algorithm] });
console.log(decoded);

View File

@@ -1,12 +0,0 @@
import { checkPassword } from "@fosscord/api";
console.log(checkPassword("123456789012345"));
// -> 0.25
console.log(checkPassword("ABCDEFGHIJKLMOPQ"));
// -> 0.25
console.log(checkPassword("ABC123___...123"));
// ->
console.log(checkPassword(""));
// ->
// console.log(checkPassword(""));
// // ->