mirror of
https://github.com/spacebarchat/server.git
synced 2026-08-28 23:00:36 +00:00
Merge branch 'master' of https://github.com/discord-open-source/discord-api
This commit is contained in:
+44
-5
@@ -10,6 +10,8 @@ import i18nextMiddleware, { I18next } from "i18next-http-middleware";
|
||||
import i18nextBackend from "i18next-node-fs-backend";
|
||||
import { ErrorHandler } from "./middlewares/ErrorHandler";
|
||||
import { BodyParser } from "./middlewares/BodyParser";
|
||||
import { Router } from "express";
|
||||
import fetch from "node-fetch";
|
||||
|
||||
export interface DiscordServerOptions extends ServerOptions {}
|
||||
|
||||
@@ -69,15 +71,52 @@ export class DiscordServer extends Server {
|
||||
});
|
||||
this.app.use(i18nextMiddleware.handle(i18next, {}));
|
||||
|
||||
const app = this.app;
|
||||
const prefix = Router();
|
||||
// @ts-ignore
|
||||
this.app = prefix;
|
||||
|
||||
this.routes = await this.registerRoutes(__dirname + "/routes/");
|
||||
app.use("/api/v8", prefix);
|
||||
this.app = app;
|
||||
this.app.use(ErrorHandler);
|
||||
const indexHTML = await fs.readFile(__dirname + "/../client_test/index.html");
|
||||
|
||||
// this.app.get("*", (req, res) => {
|
||||
// res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24);
|
||||
// res.set("content-type", "text/html");
|
||||
// res.send(indexHTML);
|
||||
// });
|
||||
this.app.get("/assets/:file", async (req, res) => {
|
||||
delete req.headers.host;
|
||||
const response = await fetch(`https://discord.com/assets/${req.params.file}`, {
|
||||
// @ts-ignore
|
||||
headers: {
|
||||
...req.headers,
|
||||
},
|
||||
});
|
||||
const buffer = await response.buffer();
|
||||
|
||||
response.headers.forEach((value, name) => {
|
||||
if (
|
||||
[
|
||||
"content-length",
|
||||
"content-security-policy",
|
||||
"strict-transport-security",
|
||||
"set-cookie",
|
||||
"transfer-encoding",
|
||||
"expect-ct",
|
||||
"access-control-allow-origin",
|
||||
"content-encoding",
|
||||
].includes(name.toLowerCase())
|
||||
) {
|
||||
return;
|
||||
}
|
||||
res.set(name, value);
|
||||
});
|
||||
|
||||
return res.send(buffer);
|
||||
});
|
||||
this.app.get("*", (req, res) => {
|
||||
res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24);
|
||||
res.set("content-type", "text/html");
|
||||
res.send(indexHTML);
|
||||
});
|
||||
return super.start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,13 @@ import { NextFunction, Request, Response } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { checkToken } from "fosscord-server-util";
|
||||
|
||||
export const NO_AUTHORIZATION_ROUTES = ["/api/v8/auth/login", "/api/v8/auth/register", "/api/v8/webhooks/"];
|
||||
export const NO_AUTHORIZATION_ROUTES = [
|
||||
"/api/v8/auth/login",
|
||||
"/api/v8/auth/register",
|
||||
"/api/v8/webhooks/",
|
||||
"/api/v8/gateway",
|
||||
"/api/v8/experiments",
|
||||
];
|
||||
|
||||
declare global {
|
||||
namespace Express {
|
||||
@@ -14,6 +20,8 @@ declare global {
|
||||
}
|
||||
|
||||
export async function Authentication(req: Request, res: Response, next: NextFunction) {
|
||||
if (!req.url.startsWith("/api")) return next();
|
||||
if (req.url.startsWith("/api/v8/invites") && req.method === "GET") return next();
|
||||
if (NO_AUTHORIZATION_ROUTES.some((x) => req.url.startsWith(x))) return next();
|
||||
if (!req.headers.authorization) return next(new HTTPError("Missing Authorization Header", 401));
|
||||
// TODO: check if user is banned/token expired
|
||||
@@ -22,7 +30,7 @@ export async function Authentication(req: Request, res: Response, next: NextFunc
|
||||
const decoded: any = await checkToken(req.headers.authorization);
|
||||
|
||||
req.token = decoded;
|
||||
req.user_id = BigInt(decoded.id);
|
||||
req.user_id = decoded.id;
|
||||
return next();
|
||||
} catch (error) {
|
||||
return next(new HTTPError(error.toString(), 400));
|
||||
|
||||
@@ -16,14 +16,15 @@ export function ErrorHandler(error: Error, req: Request, res: Response, next: Ne
|
||||
errors = error.errors;
|
||||
} else {
|
||||
console.error(error);
|
||||
if (req.server.options.production) {
|
||||
if (req.server?.options?.production) {
|
||||
message = "Internal Server Error";
|
||||
}
|
||||
code = httpcode = 500;
|
||||
}
|
||||
|
||||
res.status(httpcode).json({ code: code, message, errors });
|
||||
return next();
|
||||
|
||||
return;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.status(500).json({ code: 500, message: "Internal Server Error" });
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
import { Router, Request, Response } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
|
||||
import { check } from "@util/instanceOf";
|
||||
import { random } from "@util/RandomInviteID";
|
||||
import { emitEvent } from "@util/Event";
|
||||
|
||||
import { InviteCreateSchema } from "@schema/Invite";
|
||||
|
||||
import { getPermission, ChannelModel, InviteModel, InviteCreateEvent } from "fosscord-server-util";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.post("/", check(InviteCreateSchema), async (req: Request, res: Response) => {
|
||||
const usID = req.user_id;
|
||||
const chID = BigInt(req.params.channel_id);
|
||||
const channel = await ChannelModel.findOne({ id: chID }).exec();
|
||||
|
||||
if (!channel || !channel.guild_id) {
|
||||
throw new HTTPError("This channel doesn't exist", 404);
|
||||
}
|
||||
const { guild_id: guID } = channel;
|
||||
|
||||
const permission = await getPermission(usID, guID);
|
||||
|
||||
if (!permission.has("CREATE_INSTANT_INVITE")) {
|
||||
throw new HTTPError("You aren't authorised to access this endpoint", 401);
|
||||
}
|
||||
|
||||
const invite = {
|
||||
code: random(),
|
||||
temporary: req.body.temporary,
|
||||
uses: 0,
|
||||
max_uses: req.body.max_uses,
|
||||
max_age: req.body.max_age,
|
||||
created_at: new Date(),
|
||||
guild_id: guID,
|
||||
channel_id: chID,
|
||||
inviter_id: usID,
|
||||
};
|
||||
|
||||
await new InviteModel(invite).save();
|
||||
|
||||
await emitEvent({ event: "INVITE_CREATE", data: invite } as InviteCreateEvent);
|
||||
res.status(201).send(invite);
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -1,4 +0,0 @@
|
||||
import { Router } from "express";
|
||||
const router: Router = Router();
|
||||
|
||||
export default router;
|
||||
@@ -1,34 +0,0 @@
|
||||
/**
|
||||
* * patch to redirect requests from cloned client
|
||||
* (../../client/index.html)
|
||||
*/
|
||||
import { Router } from "express";
|
||||
import fetch, { Response } from "node-fetch";
|
||||
|
||||
const router: Router = Router();
|
||||
const cache = new Map<string, Response>();
|
||||
const assetEndpoint = "https://discord.com/assets/";
|
||||
|
||||
export async function getCache(key: string): Promise<Response> {
|
||||
let cachedRessource = cache.get(key);
|
||||
|
||||
if (!cachedRessource) {
|
||||
const res = await fetch(assetEndpoint + key);
|
||||
// @ts-ignore
|
||||
res.bufferResponse = await res.buffer();
|
||||
cache.set(key, res);
|
||||
cachedRessource = res;
|
||||
}
|
||||
|
||||
return cachedRessource;
|
||||
}
|
||||
|
||||
router.get("/:hash", async (req, res) => {
|
||||
res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24);
|
||||
const cache = await getCache(req.params.hash);
|
||||
res.set("content-type", <string>cache.headers.get("content-type"));
|
||||
// @ts-ignore
|
||||
res.send(cache.bufferResponse);
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Request, Response, Router } from "express";
|
||||
import { check, FieldErrors, Length } from "../../../../util/instanceOf";
|
||||
import { check, FieldErrors, Length } from "../../util/instanceOf";
|
||||
import bcrypt from "bcrypt";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { User, UserModel } from "fosscord-server-util";
|
||||
import Config from "../../../../util/Config";
|
||||
import Config from "../../util/Config";
|
||||
import { adjustEmail } from "./register";
|
||||
|
||||
const router: Router = Router();
|
||||
@@ -25,12 +25,11 @@ router.post(
|
||||
const query: any[] = [{ phone: login }];
|
||||
if (email) query.push({ email });
|
||||
|
||||
// * MongoDB Specific query for user with same email or phone number
|
||||
const user = await UserModel.findOne(
|
||||
{
|
||||
$or: query,
|
||||
},
|
||||
`hash id user_settings.locale user_settings.theme`
|
||||
`user_data.hash id user_settings.locale user_settings.theme`
|
||||
).exec();
|
||||
|
||||
if (!user) {
|
||||
@@ -40,7 +39,7 @@ router.post(
|
||||
}
|
||||
|
||||
// the salt is saved in the password refer to bcrypt docs
|
||||
const same_password = await bcrypt.compare(password, user.hash);
|
||||
const same_password = await bcrypt.compare(password, user.user_data.hash);
|
||||
if (!same_password) {
|
||||
throw FieldErrors({
|
||||
password: { message: req.t("auth:login.INVALID_PASSWORD"), code: "INVALID_PASSWORD" },
|
||||
@@ -57,13 +56,13 @@ router.post(
|
||||
}
|
||||
);
|
||||
|
||||
export async function generateToken(id: bigint) {
|
||||
export async function generateToken(id: string) {
|
||||
const iat = Math.floor(Date.now() / 1000);
|
||||
const algorithm = "HS256";
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
jwt.sign(
|
||||
{ id: `${id}`, iat },
|
||||
{ id: id, iat },
|
||||
Config.get().security.jwtSecret,
|
||||
{
|
||||
algorithm,
|
||||
@@ -80,7 +79,6 @@ export async function generateToken(id: bigint) {
|
||||
* POST /auth/login
|
||||
* @argument { login: "email@gmail.com", password: "cleartextpassword", undelete: false, captcha_key: null, login_source: null, gift_code_sku_id: null, }
|
||||
|
||||
|
||||
* MFA required:
|
||||
* @returns {"token": null, "mfa": true, "sms": true, "ticket": "SOME TICKET JWT TOKEN"}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Request, Response, Router } from "express";
|
||||
import Config from "../../../../util/Config";
|
||||
import Config from "../../util/Config";
|
||||
import { trimSpecial, User, Snowflake, UserModel } from "fosscord-server-util";
|
||||
import bcrypt from "bcrypt";
|
||||
import { check, Email, EMAIL_REGEX, FieldErrors, Length } from "../../../../util/instanceOf";
|
||||
import { check, Email, EMAIL_REGEX, FieldErrors, Length } from "../../util/instanceOf";
|
||||
import "missing-native-js-functions";
|
||||
import { generateToken } from "./login";
|
||||
|
||||
@@ -52,7 +52,7 @@ router.post(
|
||||
let discriminator = "";
|
||||
|
||||
// get register Config
|
||||
const { register } = Config.get();
|
||||
const { register, security } = Config.get();
|
||||
|
||||
// check if registration is allowed
|
||||
if (!register.allowNewRegistration) {
|
||||
@@ -129,9 +129,9 @@ router.post(
|
||||
}
|
||||
}
|
||||
|
||||
if (register.requireCaptcha) {
|
||||
if (register.requireCaptcha && security.captcha.enabled) {
|
||||
if (!captcha_key) {
|
||||
const { sitekey, service } = Config.get().security.captcha;
|
||||
const { sitekey, service } = security.captcha;
|
||||
return res.status(400).json({
|
||||
captcha_key: ["captcha-required"],
|
||||
captcha_sitekey: sitekey,
|
||||
@@ -165,9 +165,10 @@ router.post(
|
||||
});
|
||||
}
|
||||
|
||||
// constructing final user object
|
||||
// TODO fix:
|
||||
// @ts-ignore
|
||||
// TODO: save date_of_birth
|
||||
// appearently discord doesn't save the date of birth and just calculate if nsfw is allowed
|
||||
// if nsfw_allowed is null/undefined it'll require date_of_birth to set it to true/false
|
||||
|
||||
const user: User = {
|
||||
id: Snowflake.generate(),
|
||||
created_at: new Date(),
|
||||
@@ -176,13 +177,34 @@ router.post(
|
||||
avatar: null,
|
||||
bot: false,
|
||||
system: false,
|
||||
desktop: false,
|
||||
mobile: false,
|
||||
premium: false,
|
||||
premium_type: 0,
|
||||
phone: undefined,
|
||||
mfa_enabled: false,
|
||||
verified: false,
|
||||
presence: {
|
||||
activities: [],
|
||||
client_status: {
|
||||
desktop: undefined,
|
||||
mobile: undefined,
|
||||
web: undefined,
|
||||
},
|
||||
status: "offline",
|
||||
},
|
||||
email: adjusted_email,
|
||||
nsfw_allowed: true, // TODO: depending on age
|
||||
public_flags: 0n,
|
||||
flags: 0n, // TODO: generate default flags
|
||||
hash: adjusted_password,
|
||||
guilds: [],
|
||||
valid_tokens_since: new Date(),
|
||||
user_data: {
|
||||
hash: adjusted_password,
|
||||
valid_tokens_since: new Date(),
|
||||
relationships: [],
|
||||
connected_accounts: [],
|
||||
fingerprints: [],
|
||||
},
|
||||
user_settings: {
|
||||
afk_timeout: 300,
|
||||
allow_accessibility_detection: true,
|
||||
@@ -225,7 +247,7 @@ router.post(
|
||||
};
|
||||
|
||||
// insert user into database
|
||||
await new UserModel(user).save({});
|
||||
await new UserModel(user).save();
|
||||
|
||||
return res.json({ token: await generateToken(user.id) });
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { Router, Request, Response } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
|
||||
import { check } from "../../../util/instanceOf";
|
||||
import { random } from "../../../util/RandomInviteID";
|
||||
import { emitEvent } from "../../../util/Event";
|
||||
|
||||
import { InviteCreateSchema } from "../../../schema/Invite";
|
||||
|
||||
import { getPermission, ChannelModel, InviteModel, InviteCreateEvent, toObject } from "fosscord-server-util";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.post("/", check(InviteCreateSchema), async (req: Request, res: Response) => {
|
||||
const { user_id } = req;
|
||||
const { channel_id } = req.params;
|
||||
const channel = await ChannelModel.findOne({ id: channel_id }).exec();
|
||||
|
||||
if (!channel || !channel.guild_id) {
|
||||
throw new HTTPError("This channel doesn't exist", 404);
|
||||
}
|
||||
const { guild_id } = channel;
|
||||
|
||||
const permission = await getPermission(user_id, guild_id);
|
||||
|
||||
if (!permission.has("CREATE_INSTANT_INVITE")) {
|
||||
throw new HTTPError("You aren't authorised to access this endpoint", 401);
|
||||
}
|
||||
|
||||
const invite = {
|
||||
code: random(),
|
||||
temporary: req.body.temporary,
|
||||
uses: 0,
|
||||
max_uses: req.body.max_uses,
|
||||
max_age: req.body.max_age,
|
||||
created_at: new Date(),
|
||||
guild_id,
|
||||
channel_id: channel_id,
|
||||
inviter_id: user_id,
|
||||
};
|
||||
|
||||
await new InviteModel(invite).save();
|
||||
|
||||
await emitEvent({ event: "INVITE_CREATE", data: invite, guild_id } as InviteCreateEvent);
|
||||
res.status(201).send(invite);
|
||||
});
|
||||
|
||||
router.get("/", async (req: Request, res: Response) => {
|
||||
const { user_id } = req;
|
||||
const { channel_id } = req.params;
|
||||
const channel = await ChannelModel.findOne({ id: channel_id }).exec();
|
||||
|
||||
if (!channel || !channel.guild_id) {
|
||||
throw new HTTPError("This channel doesn't exist", 404);
|
||||
}
|
||||
const { guild_id } = channel;
|
||||
const permission = await getPermission(user_id, guild_id);
|
||||
|
||||
if (!permission.has("MANAGE_CHANNELS")) {
|
||||
throw new HTTPError("You aren't authorised to access this endpoint", 401);
|
||||
}
|
||||
|
||||
const invites = await InviteModel.find({ guild_id }).exec();
|
||||
|
||||
res.status(200).send(toObject(invites));
|
||||
});
|
||||
|
||||
export default router;
|
||||
+6
-6
@@ -1,9 +1,9 @@
|
||||
import { Router } from "express";
|
||||
import { ChannelModel, getPermission, MessageDeleteBulkEvent, MessageModel } from "fosscord-server-util";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import Config from "@util/Config";
|
||||
import { emitEvent } from "@util/Event";
|
||||
import { check } from "@util/instanceOf";
|
||||
import Config from "../../../../util/Config";
|
||||
import { emitEvent } from "../../../../util/Event";
|
||||
import { check } from "../../../../util/instanceOf";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
@@ -12,8 +12,8 @@ export default router;
|
||||
// TODO: should users be able to bulk delete messages or only bots?
|
||||
// TODO: should this request fail, if you provide messages older than 14 days/invalid ids?
|
||||
// https://discord.com/developers/docs/resources/channel#bulk-delete-messages
|
||||
router.post("/", check({ messages: [BigInt] }), async (req, res) => {
|
||||
const channel_id = BigInt(req.params.channel_id);
|
||||
router.post("/", check({ messages: [String] }), async (req, res) => {
|
||||
const channel_id = req.params.channel_id
|
||||
const channel = await ChannelModel.findOne({ id: channel_id }, { permission_overwrites: true, guild_id: true }).exec();
|
||||
if (!channel?.guild_id) throw new HTTPError("Can't bulk delete dm channel messages", 400);
|
||||
|
||||
@@ -22,7 +22,7 @@ router.post("/", check({ messages: [BigInt] }), async (req, res) => {
|
||||
|
||||
const { maxBulkDelete } = Config.get().limits.message;
|
||||
|
||||
const { messages } = req.body as { messages: bigint[] };
|
||||
const { messages } = req.body as { messages: string[] };
|
||||
if (messages.length < 2) throw new HTTPError("You must at least specify 2 messages to bulk delete");
|
||||
if (messages.length > maxBulkDelete) throw new HTTPError(`You cannot delete more than ${maxBulkDelete} messages`);
|
||||
|
||||
+48
-26
@@ -1,11 +1,23 @@
|
||||
import { Router } from "express";
|
||||
import { ChannelModel, ChannelType, getPermission, Message, MessageCreateEvent, MessageModel, Snowflake } from "fosscord-server-util";
|
||||
import {
|
||||
ChannelModel,
|
||||
ChannelType,
|
||||
getPermission,
|
||||
Message,
|
||||
MessageCreateEvent,
|
||||
MessageDocument,
|
||||
MessageModel,
|
||||
Snowflake,
|
||||
toObject,
|
||||
} from "fosscord-server-util";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { MessageCreateSchema } from "@schema/Message";
|
||||
import { check, instanceOf, Length } from "@util/instanceOf";
|
||||
import { PublicUserProjection } from "@util/User";
|
||||
import { MessageCreateSchema } from "../../../../schema/Message";
|
||||
import { check, instanceOf, Length } from "../../../../util/instanceOf";
|
||||
import { PublicUserProjection } from "../../../../util/User";
|
||||
import multer from "multer";
|
||||
import { emitEvent } from "@util/Event";
|
||||
import { emitEvent } from "../../../../util/Event";
|
||||
import { Query } from "mongoose";
|
||||
import { PublicMemberProjection } from "../../../../util/Member";
|
||||
const router: Router = Router();
|
||||
|
||||
export default router;
|
||||
@@ -27,23 +39,23 @@ function isTextChannel(type: ChannelType): boolean {
|
||||
// https://discord.com/developers/docs/resources/channel#create-message
|
||||
// get messages
|
||||
router.get("/", async (req, res) => {
|
||||
const channel_id = BigInt(req.params.channel_id);
|
||||
const channel_id = req.params.channel_id;
|
||||
const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true, type: true, permission_overwrites: true }).exec();
|
||||
if (!channel) throw new HTTPError("Channel not found", 404);
|
||||
|
||||
isTextChannel(channel.type);
|
||||
|
||||
try {
|
||||
instanceOf({ $around: BigInt, $after: BigInt, $before: BigInt, $limit: new Length(Number, 1, 100) }, req.query, {
|
||||
instanceOf({ $around: String, $after: String, $before: String, $limit: new Length(Number, 1, 100) }, req.query, {
|
||||
path: "query",
|
||||
req,
|
||||
});
|
||||
} catch (error) {
|
||||
return res.status(400).json({ code: 50035, message: "Invalid Query", success: false, errors: error });
|
||||
}
|
||||
var { around, after, before, limit }: { around?: bigint; after?: bigint; before?: bigint; limit?: number } = req.query;
|
||||
var { around, after, before, limit }: { around?: string; after?: string; before?: string; limit?: number } = req.query;
|
||||
if (!limit) limit = 50;
|
||||
var halfLimit = BigInt(Math.floor(limit / 2));
|
||||
var halfLimit = Math.floor(limit / 2);
|
||||
|
||||
if ([ChannelType.GUILD_VOICE, ChannelType.GUILD_CATEGORY, ChannelType.GUILD_STORE].includes(channel.type))
|
||||
throw new HTTPError("Not a text channel");
|
||||
@@ -57,24 +69,21 @@ router.get("/", async (req, res) => {
|
||||
if (!channel.recipients.includes(req.user_id)) throw new HTTPError("You don't have permission to view this channel", 401);
|
||||
}
|
||||
|
||||
var query: any;
|
||||
var query: Query<MessageDocument[], MessageDocument, {}>;
|
||||
if (after) query = MessageModel.find({ channel_id, id: { $gt: after } });
|
||||
else if (before) query = MessageModel.find({ channel_id, id: { $lt: before } });
|
||||
else if (around) query = MessageModel.find({ channel_id, id: { $gt: around - halfLimit, $lt: around + halfLimit } });
|
||||
else if (around)
|
||||
query = MessageModel.find({
|
||||
channel_id,
|
||||
id: { $gt: (BigInt(around) - BigInt(halfLimit)).toString(), $lt: (BigInt(around) + BigInt(halfLimit)).toString() },
|
||||
});
|
||||
else {
|
||||
query = MessageModel.find({ channel_id }).sort({ id: -1 });
|
||||
}
|
||||
|
||||
const messages = await query
|
||||
.limit(limit)
|
||||
.populate({ path: "author", select: PublicUserProjection })
|
||||
.populate({ path: "mentions", select: PublicUserProjection })
|
||||
.populate({ path: "mention_channels", select: { id: true, guild_id: true, type: true, name: true } })
|
||||
.populate("mention_roles")
|
||||
// .populate({ path: "member", select: PublicMemberProjection })
|
||||
.exec();
|
||||
const messages = await query.limit(limit).exec();
|
||||
|
||||
return res.json(messages);
|
||||
return res.json(toObject(messages));
|
||||
});
|
||||
|
||||
// TODO: config max upload size
|
||||
@@ -88,11 +97,12 @@ const messageUpload = multer({ limits: { fieldSize: 1024 * 1024 * 1024 * 50 } })
|
||||
// TODO: trim and replace message content and every embed field
|
||||
// Send message
|
||||
router.post("/", check(MessageCreateSchema), async (req, res) => {
|
||||
const channel_id = BigInt(req.params.channel_id);
|
||||
const channel_id = req.params.channel_id;
|
||||
const body = req.body as MessageCreateSchema;
|
||||
|
||||
const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true, type: true, permission_overwrites: true }).exec();
|
||||
if (!channel) throw new HTTPError("Channel not found", 404);
|
||||
// TODO: are tts messages allowed in dm channels? should permission be checked?
|
||||
|
||||
if (channel.guild_id) {
|
||||
const permissions = await getPermission(req.user_id, channel.guild_id, channel_id, { channel });
|
||||
@@ -108,24 +118,36 @@ router.post("/", check(MessageCreateSchema), async (req, res) => {
|
||||
|
||||
if (body.message_reference) {
|
||||
if (body.message_reference.channel_id !== channel_id) throw new HTTPError("You can only reference messages from this channel");
|
||||
// TODO: should it be checked if the message exists?
|
||||
// TODO: should be checked if the referenced message exists?
|
||||
}
|
||||
|
||||
const embeds = [];
|
||||
if (body.embed) embeds.push(body.embed);
|
||||
|
||||
// TODO: check and put all in body in it
|
||||
const message: Message = {
|
||||
id: Snowflake.generate(),
|
||||
channel_id,
|
||||
guild_id: channel.guild_id,
|
||||
author_id: req.user_id,
|
||||
content: req.body,
|
||||
content: body.content,
|
||||
timestamp: new Date(),
|
||||
embeds,
|
||||
mention_channels_ids: [],
|
||||
mention_role_ids: [],
|
||||
mention_user_ids: [],
|
||||
attachments: [],
|
||||
embeds: [],
|
||||
reactions: [],
|
||||
type: 0,
|
||||
tts: body.tts,
|
||||
nonce: body.nonce,
|
||||
pinned: false,
|
||||
};
|
||||
|
||||
await new MessageModel(message).save();
|
||||
const doc = await new MessageModel(message).populate({ path: "member", select: PublicMemberProjection }).save();
|
||||
const data = toObject(doc);
|
||||
|
||||
await emitEvent({ event: "MESSAGE_CREATE", channel_id, data: {} } as MessageCreateEvent);
|
||||
await emitEvent({ event: "MESSAGE_CREATE", channel_id, data, guild_id: channel.guild_id } as MessageCreateEvent);
|
||||
|
||||
return res.send(data);
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", (req, res) => {
|
||||
// TODO:
|
||||
res.send({ fingerprint: "", assignments: [] });
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", (req, res) => {
|
||||
res.send({ url: "ws://localhost:8080" });
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -1,28 +1,28 @@
|
||||
import { Request, Response, Router } from "express";
|
||||
import { BanModel, getPermission, GuildBanAddEvent, GuildBanRemoveEvent, GuildModel } from "fosscord-server-util";
|
||||
import { BanModel, getPermission, GuildBanAddEvent, GuildBanRemoveEvent, GuildModel, toObject } from "fosscord-server-util";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { getIpAdress } from "@middlewares/GlobalRateLimit";
|
||||
import { BanCreateSchema } from "@schema/Ban";
|
||||
import { emitEvent } from "@util/Event";
|
||||
import { check } from "@util/instanceOf";
|
||||
import { removeMember } from "@util/Member";
|
||||
import { getPublicUser } from "@util/User";
|
||||
import { getIpAdress } from "../../../middlewares/GlobalRateLimit";
|
||||
import { BanCreateSchema } from "../../../schema/Ban";
|
||||
import { emitEvent } from "../../../util/Event";
|
||||
import { check } from "../../../util/instanceOf";
|
||||
import { removeMember } from "../../../util/Member";
|
||||
import { getPublicUser } from "../../../util/User";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/", async (req: Request, res: Response) => {
|
||||
const guild_id = BigInt(req.params.id);
|
||||
const guild_id = req.params.id;
|
||||
|
||||
const guild = await GuildModel.findOne({ id: guild_id }).exec();
|
||||
const guild = await GuildModel.exists({ id: guild_id });
|
||||
if (!guild) throw new HTTPError("Guild not found", 404);
|
||||
|
||||
var bans = await BanModel.find({ guild_id: guild_id }).exec();
|
||||
return res.json(bans);
|
||||
return res.json(toObject(bans));
|
||||
});
|
||||
|
||||
router.get("/:user", async (req: Request, res: Response) => {
|
||||
const guild_id = BigInt(req.params.id);
|
||||
const user_id = BigInt(req.params.ban);
|
||||
const guild_id = req.params.id;
|
||||
const user_id = req.params.ban;
|
||||
|
||||
var ban = await BanModel.findOne({ guild_id: guild_id, user_id: user_id }).exec();
|
||||
if (!ban) throw new HTTPError("Ban not found", 404);
|
||||
@@ -30,8 +30,8 @@ router.get("/:user", async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
router.post("/:user_id", check(BanCreateSchema), async (req: Request, res: Response) => {
|
||||
const guild_id = BigInt(req.params.id);
|
||||
const banned_user_id = BigInt(req.params.user_id);
|
||||
const guild_id = req.params.id;
|
||||
const banned_user_id = req.params.user_id;
|
||||
|
||||
const banned_user = await getPublicUser(banned_user_id);
|
||||
const perms = await getPermission(req.user_id, guild_id);
|
||||
@@ -61,30 +61,30 @@ router.post("/:user_id", check(BanCreateSchema), async (req: Request, res: Respo
|
||||
});
|
||||
|
||||
router.delete("/:user_id", async (req: Request, res: Response) => {
|
||||
var guild_id = BigInt(req.params.id);
|
||||
var banned_user_id = BigInt(req.params.user_id);
|
||||
var { guild_id } = req.params;
|
||||
var banned_user_id = req.params.user_id;
|
||||
|
||||
const banned_user = await getPublicUser(banned_user_id);
|
||||
const guild = await GuildModel.findOne({ id: guild_id }, { id: true }).exec();
|
||||
const guild = await GuildModel.exists({ id: guild_id });
|
||||
if (!guild) throw new HTTPError("Guild not found", 404);
|
||||
|
||||
const perms = await getPermission(req.user_id, guild.id);
|
||||
const perms = await getPermission(req.user_id, guild_id);
|
||||
if (!perms.has("BAN_MEMBERS")) {
|
||||
throw new HTTPError("No permissions", 403);
|
||||
}
|
||||
|
||||
await BanModel.deleteOne({
|
||||
user_id: banned_user_id,
|
||||
guild_id: guild.id,
|
||||
guild_id,
|
||||
}).exec();
|
||||
|
||||
await emitEvent({
|
||||
event: "GUILD_BAN_REMOVE",
|
||||
data: {
|
||||
guild_id: guild.id,
|
||||
guild_id,
|
||||
user: banned_user,
|
||||
},
|
||||
guild_id: guild.id,
|
||||
guild_id,
|
||||
} as GuildBanRemoveEvent);
|
||||
|
||||
return res.status(204).send();
|
||||
@@ -1,19 +1,20 @@
|
||||
import { Router } from "express";
|
||||
import { ChannelModel, ChannelType, GuildModel, Snowflake } from "fosscord-server-util";
|
||||
import { ChannelCreateEvent, ChannelModel, ChannelType, GuildModel, Snowflake, toObject } from "fosscord-server-util";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { ChannelModifySchema } from "../../../../../schema/Channel";
|
||||
import { check } from "../../../../../util/instanceOf";
|
||||
import { ChannelModifySchema } from "../../../schema/Channel";
|
||||
import { emitEvent } from "../../../util/Event";
|
||||
import { check } from "../../../util/instanceOf";
|
||||
const router = Router();
|
||||
|
||||
router.get("/", async (req, res) => {
|
||||
const guild_id = BigInt(req.params.id);
|
||||
const { guild_id } = req.params;
|
||||
const channels = await ChannelModel.find({ guild_id }).exec();
|
||||
|
||||
res.json(channels);
|
||||
res.json(toObject(channels));
|
||||
});
|
||||
|
||||
router.post("/", check(ChannelModifySchema), async (req, res) => {
|
||||
const guild_id = BigInt(req.params.id);
|
||||
const { guild_id } = req.params;
|
||||
const body = req.body as ChannelModifySchema;
|
||||
if (!body.permission_overwrites) body.permission_overwrites = [];
|
||||
if (!body.topic) body.topic = "";
|
||||
@@ -45,6 +46,8 @@ router.post("/", check(ChannelModifySchema), async (req, res) => {
|
||||
};
|
||||
await new ChannelModel(channel).save();
|
||||
|
||||
await emitEvent({ event: "CHANNEL_CREATE", data: channel, guild_id } as ChannelCreateEvent);
|
||||
|
||||
res.json(channel);
|
||||
});
|
||||
|
||||
@@ -5,26 +5,31 @@ import {
|
||||
getPermission,
|
||||
GuildDeleteEvent,
|
||||
GuildModel,
|
||||
GuildUpdateEvent,
|
||||
InviteModel,
|
||||
MemberModel,
|
||||
MessageModel,
|
||||
RoleModel,
|
||||
toObject,
|
||||
UserModel,
|
||||
} from "fosscord-server-util";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { GuildUpdateSchema } from "../../../../../schema/Guild";
|
||||
import { emitEvent } from "../../../../../util/Event";
|
||||
import { check } from "../../../../../util/instanceOf";
|
||||
import { GuildUpdateSchema } from "../../../schema/Guild";
|
||||
import { emitEvent } from "../../../util/Event";
|
||||
import { check } from "../../../util/instanceOf";
|
||||
import "missing-native-js-functions";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", async (req: Request, res: Response) => {
|
||||
const guild_id = BigInt(req.params.id);
|
||||
const { guild_id } = req.params;
|
||||
|
||||
const guild = await GuildModel.findOne({ id: guild_id }).exec();
|
||||
const guild = await GuildModel.findOne({ id: guild_id })
|
||||
.populate({ path: "joined_at", match: { id: req.user_id } })
|
||||
.exec();
|
||||
if (!guild) throw new HTTPError("Guild does not exist", 404);
|
||||
|
||||
const member = await MemberModel.findOne({ guild_id: guild_id, id: req.user_id }, "id").exec();
|
||||
const member = await MemberModel.exists({ guild_id: guild_id, id: req.user_id });
|
||||
if (!member) throw new HTTPError("You are not a member of the guild you are trying to access", 401);
|
||||
|
||||
return res.json(guild);
|
||||
@@ -32,20 +37,27 @@ router.get("/", async (req: Request, res: Response) => {
|
||||
|
||||
router.patch("/", check(GuildUpdateSchema), async (req: Request, res: Response) => {
|
||||
const body = req.body as GuildUpdateSchema;
|
||||
const guild_id = BigInt(req.params.id);
|
||||
|
||||
const guild = await GuildModel.findOne({ id: guild_id }).exec();
|
||||
if (!guild) throw new HTTPError("This guild does not exist", 404);
|
||||
const { guild_id } = req.params;
|
||||
// TODO: guild update check image
|
||||
|
||||
const perms = await getPermission(req.user_id, guild_id);
|
||||
if (!perms.has("MANAGE_GUILD")) throw new HTTPError("You do not have the MANAGE_GUILD permission", 401);
|
||||
|
||||
await GuildModel.updateOne({ id: guild_id }, body).exec();
|
||||
return res.status(204);
|
||||
const guild = await GuildModel.findOneAndUpdate({ id: guild_id }, body)
|
||||
.populate({ path: "joined_at", match: { id: req.user_id } })
|
||||
.exec();
|
||||
|
||||
const data = toObject(guild);
|
||||
|
||||
emitEvent({ event: "GUILD_UPDATE", data: data, guild_id } as GuildUpdateEvent);
|
||||
|
||||
return res.send(data);
|
||||
});
|
||||
|
||||
router.delete("/", async (req: Request, res: Response) => {
|
||||
var guild_id = BigInt(req.params.id);
|
||||
// discord prefixes this route with /delete instead of using the delete method
|
||||
// docs are wrong https://discord.com/developers/docs/resources/guild#delete-guild
|
||||
router.post("/delete", async (req: Request, res: Response) => {
|
||||
var { guild_id } = req.params;
|
||||
|
||||
const guild = await GuildModel.findOne({ id: guild_id }, "owner_id").exec();
|
||||
if (!guild) throw new HTTPError("This guild does not exist", 404);
|
||||
@@ -67,7 +79,7 @@ router.delete("/", async (req: Request, res: Response) => {
|
||||
await InviteModel.deleteMany({ guild_id }).exec();
|
||||
await MessageModel.deleteMany({ guild_id }).exec();
|
||||
|
||||
return res.status(204).send();
|
||||
return res.sendStatus(204);
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -1,21 +1,21 @@
|
||||
import { Request, Response, Router } from "express";
|
||||
import { GuildModel, MemberModel } from "fosscord-server-util";
|
||||
import { GuildModel, MemberModel, toObject } from "fosscord-server-util";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { instanceOf, Length } from "../../../../../util/instanceOf";
|
||||
import { PublicMemberProjection } from "../../../../../util/Member";
|
||||
import { PublicUserProjection } from "../../../../../util/User";
|
||||
import { instanceOf, Length } from "../../../util/instanceOf";
|
||||
import { PublicMemberProjection } from "../../../util/Member";
|
||||
import { PublicUserProjection } from "../../../util/User";
|
||||
|
||||
const router = Router();
|
||||
|
||||
// TODO: not allowed for user -> only allowed for bots with privileged intents
|
||||
// TODO: send over websocket
|
||||
router.get("/", async (req: Request, res: Response) => {
|
||||
const guild_id = BigInt(req.params.id);
|
||||
const { guild_id } = req.params;
|
||||
const guild = await GuildModel.findOne({ id: guild_id }).exec();
|
||||
if (!guild) throw new HTTPError("Guild not found", 404);
|
||||
|
||||
try {
|
||||
instanceOf({ $limit: new Length(Number, 1, 1000), $after: BigInt }, req.query, {
|
||||
instanceOf({ $limit: new Length(Number, 1, 1000), $after: String }, req.query, {
|
||||
path: "query",
|
||||
req,
|
||||
ref: { obj: null, key: "" },
|
||||
@@ -26,22 +26,21 @@ router.get("/", async (req: Request, res: Response) => {
|
||||
|
||||
// @ts-ignore
|
||||
if (!req.query.limit) req.query.limit = 1;
|
||||
const { limit, after } = (<unknown>req.query) as { limit: number; after: bigint };
|
||||
const { limit, after } = (<unknown>req.query) as { limit: number; after: string };
|
||||
const query = after ? { id: { $gt: after } } : {};
|
||||
|
||||
var members = await MemberModel.find({ guild_id, ...query }, PublicMemberProjection)
|
||||
.limit(limit)
|
||||
.populate({ path: "user", select: PublicUserProjection })
|
||||
.exec();
|
||||
|
||||
return res.json(members);
|
||||
return res.json(toObject(members));
|
||||
});
|
||||
|
||||
router.get("/:member", async (req: Request, res: Response) => {
|
||||
const guild_id = BigInt(req.params.id);
|
||||
const user_id = BigInt(req.params.member);
|
||||
const { guild_id } = req.params;
|
||||
const user_id = req.params.member;
|
||||
|
||||
const member = await MemberModel.findOne({ id: user_id, guild_id }).populate({ path: "user", select: PublicUserProjection }).exec();
|
||||
const member = await MemberModel.findOne({ id: user_id, guild_id }).exec();
|
||||
if (!member) throw new HTTPError("Member not found", 404);
|
||||
|
||||
return res.json(member);
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Router, Request, Response } from "express";
|
||||
import { RoleModel, GuildModel, Snowflake, Guild } from "fosscord-server-util";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { check } from "./../../../../util/instanceOf";
|
||||
import { GuildCreateSchema } from "../../../../schema/Guild";
|
||||
import Config from "../../../../util/Config";
|
||||
import { getPublicUser } from "../../../../util/User";
|
||||
import { addMember } from "../../../../util/Member";
|
||||
import { check } from "./../../util/instanceOf";
|
||||
import { GuildCreateSchema } from "../../schema/Guild";
|
||||
import Config from "../../util/Config";
|
||||
import { getPublicUser } from "../../util/User";
|
||||
import { addMember } from "../../util/Member";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
@@ -29,11 +29,11 @@ router.post("/", check(GuildCreateSchema), async (req: Request, res: Response) =
|
||||
afk_timeout: 300,
|
||||
application_id: undefined,
|
||||
banner: undefined,
|
||||
default_message_notifications: undefined,
|
||||
default_message_notifications: 0,
|
||||
description: undefined,
|
||||
splash: undefined,
|
||||
discovery_splash: undefined,
|
||||
explicit_content_filter: undefined,
|
||||
explicit_content_filter: 0,
|
||||
features: [],
|
||||
id: guild_id,
|
||||
large: undefined,
|
||||
@@ -48,11 +48,11 @@ router.post("/", check(GuildCreateSchema), async (req: Request, res: Response) =
|
||||
premium_tier: 0,
|
||||
public_updates_channel_id: undefined,
|
||||
rules_channel_id: undefined,
|
||||
system_channel_flags: undefined,
|
||||
system_channel_flags: 0,
|
||||
system_channel_id: undefined,
|
||||
unavailable: false,
|
||||
vanity_url_code: undefined,
|
||||
verification_level: undefined,
|
||||
verification_level: 0,
|
||||
welcome_screen: [],
|
||||
widget_channel_id: undefined,
|
||||
widget_enabled: false,
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Router, Request, Response } from "express";
|
||||
import { getPermission, InviteModel, toObject } from "fosscord-server-util";
|
||||
import { HTTPError } from "lambert-server";
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/:invite_code", async (req: Request, res: Response) => {
|
||||
const { invite_code: code } = req.params;
|
||||
|
||||
const invite = await InviteModel.findOne({ code }).exec();
|
||||
|
||||
if (!invite) throw new HTTPError("Unknown Invite", 404);
|
||||
res.status(200).send({ invite: toObject(invite) });
|
||||
});
|
||||
|
||||
router.delete("/:invite_code", async (req: Request, res: Response) => {
|
||||
const { invite_code: code } = req.params;
|
||||
const invite = await InviteModel.findOne({ code }).exec();
|
||||
|
||||
if (!invite) throw new HTTPError("Unknown Invite", 404);
|
||||
|
||||
const { guild_id, channel_id } = invite;
|
||||
const perms = await getPermission(req.user_id, guild_id, channel_id);
|
||||
|
||||
if (!perms.has("MANAGE_GUILD") || !perms.has("MANAGE_CHANNELS")) throw new HTTPError("You aren't allow", 401);
|
||||
res.status(200).send({ invite: toObject(invite) });
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post("/", (req, res) => {
|
||||
// TODO:
|
||||
res.sendStatus(204);
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -1,14 +0,0 @@
|
||||
import { Router } from "express";
|
||||
import { getPermission, MemberModel, db } from "fosscord-server-util";
|
||||
import { Types } from "mongoose";
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/", async (req, res) => {
|
||||
// @ts-ignore
|
||||
const perm = await getPermission(813185668657184768n, 813189959920910336n);
|
||||
console.log(perm);
|
||||
if (perm.has("ADD_REACTIONS")) console.log("add");
|
||||
res.send("OK");
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", (req, res) => {
|
||||
// TODO:
|
||||
res.status(200).send({ guild_affinities: [] });
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Router, Request, Response } from "express";
|
||||
import { GuildModel, MemberModel, UserModel, GuildDeleteEvent, GuildMemberRemoveEvent } from "fosscord-server-util";
|
||||
import { GuildModel, MemberModel, UserModel, GuildDeleteEvent, GuildMemberRemoveEvent, toObject } from "fosscord-server-util";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { emitEvent } from "../../../../../util/Event";
|
||||
import { getPublicUser } from "../../../../../util/User";
|
||||
import { emitEvent } from "../../../util/Event";
|
||||
import { getPublicUser } from "../../../util/User";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
@@ -11,37 +11,42 @@ router.get("/", async (req: Request, res: Response) => {
|
||||
if (!user) throw new HTTPError("User not found", 404);
|
||||
|
||||
var guildIDs = user.guilds || [];
|
||||
var guild = await GuildModel.find({ id: { $in: guildIDs } }).exec();
|
||||
res.json(guild);
|
||||
var guild = await GuildModel.find({ id: { $in: guildIDs } })
|
||||
.populate({ path: "joined_at", match: { id: req.user_id } })
|
||||
.exec();
|
||||
|
||||
res.json(toObject(guild));
|
||||
});
|
||||
|
||||
// user send to leave a certain guild
|
||||
router.delete("/:id", async (req: Request, res: Response) => {
|
||||
const guildID = BigInt(req.params.id);
|
||||
const guild = await GuildModel.findOne({ id: guildID }).exec();
|
||||
const guild_id = req.params.id;
|
||||
const guild = await GuildModel.findOne({ id: guild_id }, { guild_id: true }).exec();
|
||||
|
||||
if (!guild) throw new HTTPError("Guild doesn't exist", 404);
|
||||
if (guild.owner_id === req.user_id) throw new HTTPError("You can't leave your own guild", 400);
|
||||
|
||||
await MemberModel.deleteOne({ id: req.user_id, guild_id: guildID }).exec();
|
||||
await UserModel.updateOne({ id: req.user_id }, { $pull: { guilds: guildID } }).exec();
|
||||
const user = await getPublicUser(req.user_id);
|
||||
await Promise.all([
|
||||
MemberModel.deleteOne({ id: req.user_id, guild_id: guild_id }).exec(),
|
||||
UserModel.updateOne({ id: req.user_id }, { $pull: { guilds: guild_id } }).exec(),
|
||||
emitEvent({
|
||||
event: "GUILD_DELETE",
|
||||
data: {
|
||||
id: guild_id,
|
||||
},
|
||||
user_id: req.user_id,
|
||||
} as GuildDeleteEvent),
|
||||
]);
|
||||
|
||||
await emitEvent({
|
||||
event: "GUILD_DELETE",
|
||||
data: {
|
||||
id: guildID,
|
||||
},
|
||||
user_id: req.user_id,
|
||||
} as GuildDeleteEvent);
|
||||
const user = await getPublicUser(req.user_id);
|
||||
|
||||
await emitEvent({
|
||||
event: "GUILD_MEMBER_REMOVE",
|
||||
data: {
|
||||
guild_id: guildID,
|
||||
guild_id: guild_id,
|
||||
user: user,
|
||||
},
|
||||
guild_id: guildID,
|
||||
guild_id: guild_id,
|
||||
} as GuildMemberRemoveEvent);
|
||||
|
||||
return res.status(204).send();
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", (req, res) => {
|
||||
// TODO:
|
||||
res.status(200).send([]);
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.patch("/", (req, res) => {
|
||||
// TODO:
|
||||
res.sendStatus(204);
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -10,7 +10,7 @@ export const ChannelModifySchema = {
|
||||
$position: Number,
|
||||
$permission_overwrites: [
|
||||
{
|
||||
id: BigInt,
|
||||
id: String,
|
||||
type: new Length(Number, 0, 1), // either 0 (role) or 1 (member)
|
||||
allow: BigInt,
|
||||
deny: BigInt,
|
||||
@@ -29,23 +29,23 @@ export interface ChannelModifySchema {
|
||||
rate_limit_per_user?: Number;
|
||||
position?: number;
|
||||
permission_overwrites?: {
|
||||
id: bigint;
|
||||
id: string;
|
||||
type: number;
|
||||
allow: bigint;
|
||||
deny: bigint;
|
||||
}[];
|
||||
parent_id?: bigint;
|
||||
parent_id?: string;
|
||||
nsfw?: boolean;
|
||||
}
|
||||
|
||||
export const ChannelGuildPositionUpdateSchema = [
|
||||
{
|
||||
id: BigInt,
|
||||
id: String,
|
||||
$position: Number,
|
||||
},
|
||||
];
|
||||
|
||||
export type ChannelGuildPositionUpdateSchema = {
|
||||
id: bigint;
|
||||
id: string;
|
||||
position?: number;
|
||||
}[];
|
||||
|
||||
+18
-9
@@ -1,13 +1,22 @@
|
||||
import { ChannelSchema, GuildChannel } from "fosscord-server-util";
|
||||
import { Length } from "../util/instanceOf";
|
||||
|
||||
export const GuildCreateSchema = {
|
||||
name: new Length(String, 2, 100), // ! 2-100 chars
|
||||
name: new Length(String, 2, 100),
|
||||
$region: String, // auto complete voice region of the user
|
||||
$icon: String,
|
||||
$channels: [Object],
|
||||
$guild_template_code: String,
|
||||
$system_channel_id: String,
|
||||
};
|
||||
|
||||
export interface GuildCreateSchema {
|
||||
name: string;
|
||||
region?: string;
|
||||
icon?: string;
|
||||
channels?: GuildChannel[];
|
||||
guild_template_code?: string;
|
||||
system_channel_id?: string;
|
||||
}
|
||||
|
||||
export const GuildUpdateSchema = {
|
||||
@@ -20,27 +29,27 @@ export const GuildUpdateSchema = {
|
||||
$verification_level: Number,
|
||||
$default_message_notifications: Number,
|
||||
$system_channel_flags: Number,
|
||||
$system_channel_id: BigInt,
|
||||
$system_channel_id: String,
|
||||
$explicit_content_filter: Number,
|
||||
$public_updates_channel_id: BigInt,
|
||||
$public_updates_channel_id: String,
|
||||
$afk_timeout: Number,
|
||||
$afk_channel_id: BigInt,
|
||||
$afk_channel_id: String,
|
||||
};
|
||||
// @ts-ignore
|
||||
delete GuildUpdateSchema.$channels;
|
||||
|
||||
export interface GuildUpdateSchema extends GuildCreateSchema {
|
||||
export interface GuildUpdateSchema extends Omit<GuildCreateSchema, "channels"> {
|
||||
banner?: string;
|
||||
splash?: string;
|
||||
description?: string;
|
||||
features?: [string];
|
||||
icon?: string;
|
||||
verification_level?: number;
|
||||
default_message_notifications?: number;
|
||||
system_channel_flags?: number;
|
||||
system_channel_id?: bigint;
|
||||
explicit_content_filter?: number;
|
||||
public_updates_channel_id?: bigint;
|
||||
public_updates_channel_id?: string;
|
||||
afk_timeout?: number;
|
||||
afk_channel_id?: bigint;
|
||||
afk_channel_id?: string;
|
||||
}
|
||||
|
||||
export const GuildGetSchema = {
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
export const InviteCreateSchema = {
|
||||
$target_user_id: String,
|
||||
$target_type: String,
|
||||
$validate: String, //? wtf is this
|
||||
$max_age: Number,
|
||||
$max_uses: Number,
|
||||
$temporary: Boolean,
|
||||
@@ -7,6 +10,9 @@ export const InviteCreateSchema = {
|
||||
$target_user_type: Number,
|
||||
};
|
||||
export interface InviteCreateSchema {
|
||||
target_user_id?: String;
|
||||
target_type?: String;
|
||||
validate?: String; //? wtf is this
|
||||
max_age?: Number;
|
||||
max_uses?: Number;
|
||||
temporary?: Boolean;
|
||||
|
||||
@@ -44,9 +44,9 @@ export const MessageCreateSchema = {
|
||||
},
|
||||
$allowed_mentions: [],
|
||||
$message_reference: {
|
||||
message_id: BigInt,
|
||||
channel_id: BigInt,
|
||||
$guild_id: BigInt,
|
||||
message_id: String,
|
||||
channel_id: String,
|
||||
$guild_id: String,
|
||||
$fail_if_not_exists: Boolean,
|
||||
},
|
||||
$payload_json: String,
|
||||
@@ -60,9 +60,9 @@ export interface MessageCreateSchema {
|
||||
embed?: Embed & { timestamp?: string };
|
||||
allowed_mentions?: [];
|
||||
message_reference?: {
|
||||
message_id: bigint;
|
||||
channel_id: bigint;
|
||||
guild_id?: bigint;
|
||||
message_id: string;
|
||||
channel_id: string;
|
||||
guild_id?: string;
|
||||
fail_if_not_exists: boolean;
|
||||
};
|
||||
payload_json?: string;
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import mongoose, { Schema, Types } from "mongoose";
|
||||
import { Long as MongoTypeLong } from "mongodb";
|
||||
require("mongoose-long")(mongoose);
|
||||
|
||||
const userSchema = new Schema({
|
||||
id: MongoTypeLong,
|
||||
id: String,
|
||||
});
|
||||
|
||||
const messageSchema = new Schema({
|
||||
id: MongoTypeLong,
|
||||
id: String,
|
||||
content: String,
|
||||
});
|
||||
const message = mongoose.model("message", messageSchema, "messages");
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { getPermission } from "fosscord-server-util";
|
||||
|
||||
async function main() {
|
||||
const t = await getPermission(811642917432066048n, 812327318532915201n);
|
||||
const t = await getPermission("811642917432066048", "812327318532915201");
|
||||
console.log(t);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ export interface RateLimit {
|
||||
|
||||
export interface DefaultOptions {
|
||||
general: {
|
||||
instance_id: bigint;
|
||||
instance_id: string;
|
||||
};
|
||||
permissions: {
|
||||
user: {
|
||||
|
||||
@@ -5,6 +5,7 @@ export async function emitEvent(payload: Omit<Event, "created_at">) {
|
||||
created_at: new Date(), // in seconds
|
||||
...payload,
|
||||
};
|
||||
// TODO: bigint isn't working
|
||||
|
||||
return await new EventModel(obj).save();
|
||||
}
|
||||
|
||||
+3
-4
@@ -25,12 +25,10 @@ export const PublicMemberProjection = {
|
||||
premium_since: true,
|
||||
};
|
||||
|
||||
export async function addMember(user_id: bigint, guild_id: bigint, cache?: { guild?: Guild }) {
|
||||
export async function addMember(user_id: string, guild_id: string, cache?: { guild?: Guild }) {
|
||||
const user = await getPublicUser(user_id, { guilds: true });
|
||||
|
||||
const guildSize = user.guilds.length;
|
||||
// @ts-ignore
|
||||
user.guilds = undefined;
|
||||
|
||||
const { maxGuilds } = Config.get().limits.user;
|
||||
if (guildSize >= maxGuilds) {
|
||||
@@ -77,6 +75,7 @@ export async function addMember(user_id: bigint, guild_id: bigint, cache?: { gui
|
||||
},
|
||||
guild_id: guild_id,
|
||||
} as GuildMemberAddEvent),
|
||||
|
||||
emitEvent({
|
||||
event: "GUILD_CREATE",
|
||||
data: guild,
|
||||
@@ -85,7 +84,7 @@ export async function addMember(user_id: bigint, guild_id: bigint, cache?: { gui
|
||||
]);
|
||||
}
|
||||
|
||||
export async function removeMember(user_id: bigint, guild_id: bigint) {
|
||||
export async function removeMember(user_id: string, guild_id: string) {
|
||||
const user = await getPublicUser(user_id);
|
||||
|
||||
const guild = await GuildModel.findOne({ id: guild_id }, { owner_id: true }).exec();
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
import { UserModel } from "fosscord-server-util";
|
||||
import { toObject, UserModel } from "fosscord-server-util";
|
||||
import { HTTPError } from "lambert-server";
|
||||
|
||||
export const PublicUserProjection = {
|
||||
@@ -9,7 +9,7 @@ export const PublicUserProjection = {
|
||||
avatar: true,
|
||||
};
|
||||
|
||||
export async function getPublicUser(user_id: bigint, additional_fields?: any) {
|
||||
export async function getPublicUser(user_id: string, additional_fields?: any) {
|
||||
const user = await UserModel.findOne(
|
||||
{ id: user_id },
|
||||
{
|
||||
@@ -18,5 +18,5 @@ export async function getPublicUser(user_id: bigint, additional_fields?: any) {
|
||||
}
|
||||
).exec();
|
||||
if (!user) throw new HTTPError("User not found", 404);
|
||||
return user;
|
||||
return toObject(user);
|
||||
}
|
||||
|
||||
+22
-8
@@ -114,8 +114,6 @@ export function instanceOf(
|
||||
}
|
||||
|
||||
if (typeof type === "object") {
|
||||
if (typeof value !== "object") throw new FieldError("BASE_TYPE_OBJECT", req.t("common:field.BASE_TYPE_OBJECT"));
|
||||
|
||||
if (Array.isArray(type)) {
|
||||
if (!Array.isArray(value)) throw new FieldError("BASE_TYPE_ARRAY", req.t("common:field.BASE_TYPE_ARRAY"));
|
||||
if (!type.length) return true; // type array didn't specify any type
|
||||
@@ -123,7 +121,8 @@ export function instanceOf(
|
||||
return (
|
||||
value.every((val, i) => {
|
||||
errors[i] = {};
|
||||
return (
|
||||
|
||||
if (
|
||||
instanceOf(type[0], val, {
|
||||
path: `${path}[${i}]`,
|
||||
optional,
|
||||
@@ -131,7 +130,12 @@ export function instanceOf(
|
||||
req,
|
||||
ref: { key: i, obj: value },
|
||||
}) === true
|
||||
);
|
||||
) {
|
||||
delete errors[i];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}) || errors
|
||||
);
|
||||
} else if (type?.constructor?.name != "Object") {
|
||||
@@ -150,10 +154,15 @@ export function instanceOf(
|
||||
})
|
||||
);
|
||||
}
|
||||
if (value instanceof type) return true;
|
||||
throw new FieldError("BASE_TYPE_CLASS", req.t("common:field.BASE_TYPE_CLASS", { type }));
|
||||
try {
|
||||
if (value instanceof type) return true;
|
||||
} catch (error) {
|
||||
throw new FieldError("BASE_TYPE_CLASS", req.t("common:field.BASE_TYPE_CLASS", { type }));
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof value !== "object") throw new FieldError("BASE_TYPE_OBJECT", req.t("common:field.BASE_TYPE_OBJECT"));
|
||||
|
||||
const diff = Object.keys(value).missing(
|
||||
Object.keys(type).map((x) => (x.startsWith(OPTIONAL_PREFIX) ? x.slice(OPTIONAL_PREFIX.length) : x))
|
||||
);
|
||||
@@ -167,7 +176,7 @@ export function instanceOf(
|
||||
if (OPTIONAL) newKey = newKey.slice(OPTIONAL_PREFIX.length);
|
||||
errors[newKey] = {};
|
||||
|
||||
return (
|
||||
if (
|
||||
instanceOf(type[key], value[newKey], {
|
||||
path: `${path}.${newKey}`,
|
||||
optional: OPTIONAL,
|
||||
@@ -175,7 +184,12 @@ export function instanceOf(
|
||||
req,
|
||||
ref: { key: newKey, obj: value },
|
||||
}) === true
|
||||
);
|
||||
) {
|
||||
delete errors[newKey];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}) || errors
|
||||
);
|
||||
} else if (typeof type === "number" || typeof type === "string" || typeof type === "boolean") {
|
||||
|
||||
Reference in New Issue
Block a user