1
0
mirror of https://github.com/spacebarchat/server.git synced 2024-11-06 10:52:31 +01:00

Merge branch 'master' into fix/claim_accounts

This commit is contained in:
Madeline 2022-04-11 00:25:53 +10:00
commit cf9a923838
No known key found for this signature in database
GPG Key ID: 80D25DA3BCB24281
55 changed files with 3397 additions and 4103 deletions

View File

@ -1,7 +1,40 @@
FROM node:14 FROM node:alpine
WORKDIR /usr/src/fosscord-server/
COPY . . # env vars
WORKDIR /usr/src/fosscord-server/bundle ENV WORK_DIR="/srv/fosscord-server"
ENV DEV_MODE=0
ENV HTTP_PORT=3001
ENV WS_PORT=3002
ENV CDN_PORT=3003
ENV RTC_PORT=3004
ENV ADMIN_PORT=3005
# exposed ports (only for reference, see https://docs.docker.com/engine/reference/builder/#expose)
EXPOSE ${HTTP_PORT}/tcp ${WS_PORT}/tcp ${CDN_PORT}/tcp ${RTC_PORT}/tcp ${ADMIN_PORT}/tcp
# install required apps
RUN apk add --no-cache --update git python2 py-pip make build-base
# optionl: packages for debugging/development
RUN apk add --no-cache sqlite
# download fosscord-server
WORKDIR $WORK_DIR/src
RUN git clone https://github.com/fosscord/fosscord-server.git .
# setup and run
WORKDIR $WORK_DIR/src/bundle
RUN npm run setup RUN npm run setup
EXPOSE 3001 RUN npm install @yukikaze-bot/erlpack
CMD [ "npm", "run", "start:bundle" ] # RUN npm install mysql --save
# create update script
RUN printf '#!/bin/sh\n\ngit -C $WORK_DIR/src/ checkout master\ngit -C $WORK_DIR/src/ reset --hard HEAD\ngit -C $WORK_DIR/src/ pull\ncd $WORK_DIR/src/bundle/\nnpm run setup\n' > $WORK_DIR/update.sh
RUN chmod +x $WORK_DIR/update.sh
# configure entrypoint file
RUN printf '#!/bin/sh\n\nDEV_MODE=${DEV_MODE:-0}\n\nif [ "$DEV_MODE" -eq 1 ]; then\n tail -f /dev/null\nelse\n cd $WORK_DIR/src/bundle/\n npm run start:bundle\nfi\n' > $WORK_DIR/entrypoint.sh
RUN chmod +x $WORK_DIR/entrypoint.sh
WORKDIR $WORK_DIR
ENTRYPOINT ["./entrypoint.sh"]

View File

@ -30,6 +30,6 @@ This repository contains:
- [Contributing](https://docs.fosscord.com/contributing/server/) - [Contributing](https://docs.fosscord.com/contributing/server/)
## [Setup](https://docs.fosscord.com/setup/server/) ## [Setup](https://docs.fosscord.com/server/setup/)
- [Download](https://github.com/fosscord/fosscord-server/releases) - [Download](https://github.com/fosscord/fosscord-server/releases)

View File

@ -2,7 +2,7 @@
"openapi": "3.0.0", "openapi": "3.0.0",
"servers": [ "servers": [
{ {
"url": "https://api.fosscord.com/v{version}", "url": "https://api.fosscord.com/api/v{version}",
"description": "Official fosscord instance", "description": "Official fosscord instance",
"variables": { "variables": {
"version": { "version": {
@ -2960,7 +2960,7 @@
"type": { "type": {
"type": "string" "type": "string"
}, },
"verifie": { "verified": {
"type": "boolean" "type": "boolean"
}, },
"visibility": { "visibility": {
@ -2980,7 +2980,7 @@
"type", "type",
"user", "user",
"user_id", "user_id",
"verifie", "verified",
"visibility" "visibility"
] ]
}, },
@ -3119,7 +3119,7 @@
"type": "boolean" "type": "boolean"
}, },
"status": { "status": {
"enum": ["dnd", "idle", "offline", "online"], "enum": ["dnd", "idle", "offline", "online", "invisible"],
"type": "string" "type": "string"
}, },
"stream_notifications_enabled": { "stream_notifications_enabled": {
@ -5677,7 +5677,7 @@
"type": "boolean" "type": "boolean"
}, },
"status": { "status": {
"enum": ["dnd", "idle", "offline", "online"], "enum": ["dnd", "idle", "offline", "online", "invisible"],
"type": "string" "type": "string"
}, },
"stream_notifications_enabled": { "stream_notifications_enabled": {

View File

@ -0,0 +1,12 @@
// Remove `<link id="logincss" rel="stylesheet" href="/assets/fosscord-login.css" />` from header when we're not accessing `/login` or `/register`
// fosscord-login.css replaces discord's TOS tooltip with something more fitting for fosscord, which when included in the main app, causes other tooltips
// to be affected, which is potentially unwanted.
//
// This script removes fosscord-login.css when a user reloads the page. From testing, it appears fosscord already properly removes
// fosscord-login.css after login is successful, but not if you reload the page after logging in. This script is to remove fosscord-login.css in
// that specific case.
var token = JSON.parse(localStorage.getItem("token"));
if (!token && location.pathname !== "/login" && location.pathname !== "/register") {
document.getElementById("logincss").remove();
}

View File

@ -355,11 +355,11 @@
"type": { "type": {
"type": "string" "type": "string"
}, },
"verifie": { "verified": {
"type": "boolean" "type": "boolean"
} }
}, },
"required": ["name", "type", "verifie"] "required": ["name", "type", "verified"]
} }
}, },
"$schema": "http://json-schema.org/draft-07/schema#" "$schema": "http://json-schema.org/draft-07/schema#"
@ -7900,7 +7900,7 @@
"type": "boolean" "type": "boolean"
}, },
"status": { "status": {
"enum": ["dnd", "idle", "offline", "online"], "enum": ["dnd", "idle", "offline", "online", "invisible"],
"type": "string" "type": "string"
}, },
"stream_notifications_enabled": { "stream_notifications_enabled": {

View File

@ -7,12 +7,12 @@
"BASE_TYPE_BOOLEAN": "This field must be a boolean", "BASE_TYPE_BOOLEAN": "This field must be a boolean",
"BASE_TYPE_CHOICES": "This field must be one of ({{types}})", "BASE_TYPE_CHOICES": "This field must be one of ({{types}})",
"BASE_TYPE_CLASS": "This field must be an instance of {{type}}", "BASE_TYPE_CLASS": "This field must be an instance of {{type}}",
"BASE_TYPE_OBJECT": "This field must be an object", "BASE_TYPE_OBJECT": "שדה זה חייב להיות אובייקט",
"BASE_TYPE_ARRAY": "This field must be an array", "BASE_TYPE_ARRAY": "שדה זה חייב להיות מערך",
"UNKOWN_FIELD": "Unknown key: {{key}}", "UNKOWN_FIELD": "מפתח לא ידוע: {{key}}",
"BASE_TYPE_CONSTANT": "This field must be {{value}}", "BASE_TYPE_CONSTANT": "שדה זה להיות {{value}}",
"EMAIL_TYPE_INVALID_EMAIL": "Not a well-formed email address", "EMAIL_TYPE_INVALID_EMAIL": "כתובת דואר אלקטרוני לא חוקית",
"DATE_TYPE_PARSE": "Could not parse {{date}}. Should be ISO8601", "DATE_TYPE_PARSE": "לא ניתן לנתח {{date}}. צריך להיות ISO8601",
"BASE_TYPE_BAD_LENGTH": "Must be between {{length}} in length" "BASE_TYPE_BAD_LENGTH": "האורך חייב להיות בין {{length}}"
} }
} }

6397
api/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -49,7 +49,7 @@
"@types/morgan": "^1.9.3", "@types/morgan": "^1.9.3",
"@types/multer": "^1.4.5", "@types/multer": "^1.4.5",
"@types/node": "^14.17.9", "@types/node": "^14.17.9",
"@types/node-fetch": "^2.5.7", "@types/node-fetch": "^2.5.5",
"@types/supertest": "^2.0.11", "@types/supertest": "^2.0.11",
"@zerollup/ts-transform-paths": "^1.7.18", "@zerollup/ts-transform-paths": "^1.7.18",
"jest": "^27.2.5", "jest": "^27.2.5",
@ -86,7 +86,7 @@
"missing-native-js-functions": "^1.2.18", "missing-native-js-functions": "^1.2.18",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"multer": "^1.4.2", "multer": "^1.4.2",
"node-fetch": "^3.1.1", "node-fetch": "^2.6.2",
"patch-package": "^6.4.7", "patch-package": "^6.4.7",
"picocolors": "^1.0.0", "picocolors": "^1.0.0",
"proxy-agent": "^5.0.0", "proxy-agent": "^5.0.0",

View File

@ -26,6 +26,6 @@ DROP TABLE webhooks;
DROP TABLE channels; DROP TABLE channels;
DROP TABLE members; DROP TABLE members;
DROP TABLE guilds; DROP TABLE guilds;
DROP TABLE client_relase; DROP TABLE client_release;
-- DROP TABLE users; -- DROP TABLE users;
-- DROP TABLE config; -- DROP TABLE config;

View File

@ -15,6 +15,7 @@ export const NO_AUTHORIZATION_ROUTES = [
"/experiments", "/experiments",
"/updates", "/updates",
"/downloads/", "/downloads/",
"/scheduled-maintenances/upcoming.json",
// Public kubernetes integration // Public kubernetes integration
"/-/readyz", "/-/readyz",
"/-/healthz", "/-/healthz",

View File

@ -19,7 +19,8 @@ export interface InviteCreateSchema {
target_user_type?: number; target_user_type?: number;
} }
router.post("/", route({ body: "InviteCreateSchema", permission: "CREATE_INSTANT_INVITE" }), async (req: Request, res: Response) => { router.post("/", route({ body: "InviteCreateSchema", permission: "CREATE_INSTANT_INVITE", right: "CREATE_INVITES" }),
async (req: Request, res: Response) => {
const { user_id } = req; const { user_id } = req;
const { channel_id } = req.params; const { channel_id } = req.params;
const channel = await Channel.findOneOrFail({ where: { id: channel_id }, select: ["id", "name", "type", "guild_id"] }); const channel = await Channel.findOneOrFail({ where: { id: channel_id }, select: ["id", "name", "type", "guild_id"] });

View File

@ -1,4 +1,4 @@
import { Channel, emitEvent, getPermission, MessageDeleteEvent, Message, MessageUpdateEvent } from "@fosscord/util"; import { Channel, emitEvent, getPermission, getRights, MessageDeleteEvent, Message, MessageUpdateEvent } from "@fosscord/util";
import { Router, Response, Request } from "express"; import { Router, Response, Request } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { handleMessage, postHandleMessage } from "@fosscord/api"; import { handleMessage, postHandleMessage } from "@fosscord/api";
@ -7,18 +7,23 @@ import { MessageCreateSchema } from "../index";
const router = Router(); const router = Router();
// TODO: message content/embed string length limit // TODO: message content/embed string length limit
router.patch("/", route({ body: "MessageCreateSchema", permission: "SEND_MESSAGES" }), async (req: Request, res: Response) => { router.patch("/", route({ body: "MessageCreateSchema", permission: "SEND_MESSAGES", right: "SEND_MESSAGES" }), async (req: Request, res: Response) => {
const { message_id, channel_id } = req.params; const { message_id, channel_id } = req.params;
var body = req.body as MessageCreateSchema; var body = req.body as MessageCreateSchema;
const message = await Message.findOneOrFail({ where: { id: message_id, channel_id }, relations: ["attachments"] }); const message = await Message.findOneOrFail({ where: { id: message_id, channel_id }, relations: ["attachments"] });
const permissions = await getPermission(req.user_id, undefined, channel_id); const permissions = await getPermission(req.user_id, undefined, channel_id);
const rights = await getRights(req.user_id);
if (req.user_id !== message.author_id) { if ((req.user_id !== message.author_id)) {
permissions.hasThrow("MANAGE_MESSAGES"); if (!rights.has("MANAGE_MESSAGES")) {
body = { flags: body.flags }; // admins can only suppress embeds of other messages permissions.hasThrow("MANAGE_MESSAGES");
} body = { flags: body.flags };
// guild admins can only suppress embeds of other messages, no such restriction imposed to instance-wide admins
}
} else rights.hasThrow("SELF_EDIT_MESSAGES");
const new_message = await handleMessage({ const new_message = await handleMessage({
...message, ...message,
@ -46,17 +51,20 @@ router.patch("/", route({ body: "MessageCreateSchema", permission: "SEND_MESSAGE
return res.json(message); return res.json(message);
}); });
// permission check only if deletes messagr from other user
router.delete("/", route({}), async (req: Request, res: Response) => { router.delete("/", route({}), async (req: Request, res: Response) => {
const { message_id, channel_id } = req.params; const { message_id, channel_id } = req.params;
const channel = await Channel.findOneOrFail({ id: channel_id }); const channel = await Channel.findOneOrFail({ id: channel_id });
const message = await Message.findOneOrFail({ id: message_id }); const message = await Message.findOneOrFail({ id: message_id });
const rights = await getRights(req.user_id);
if (message.author_id !== req.user_id) { if ((message.author_id !== req.user_id)) {
const permission = await getPermission(req.user_id, channel.guild_id, channel_id); if (!rights.has("MANAGE_MESSAGES")) {
permission.hasThrow("MANAGE_MESSAGES"); const permission = await getPermission(req.user_id, channel.guild_id, channel_id);
} permission.hasThrow("MANAGE_MESSAGES");
}
} else rights.hasThrow("SELF_DELETE_MESSAGES");
await Message.delete({ id: message_id }); await Message.delete({ id: message_id });

View File

@ -1,6 +1,6 @@
import { Router, Response, Request } from "express"; import { Router, Response, Request } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Relase, Config } from "@fosscord/util"; import { Release, Config } from "@fosscord/util";
const router = Router(); const router = Router();
@ -12,9 +12,9 @@ router.get("/:branch", route({}), async (req: Request, res: Response) => {
if(!platform || !["linux", "osx", "win"].includes(platform.toString())) return res.status(404) if(!platform || !["linux", "osx", "win"].includes(platform.toString())) return res.status(404)
const relase = await Relase.findOneOrFail({ name: client.relases.upstreamVersion }); const release = await Release.findOneOrFail({ name: client.releases.upstreamVersion });
res.redirect(relase[`win_url`]); res.redirect(release[`win_url`]);
}); });
export default router; export default router;

View File

@ -33,17 +33,32 @@ router.get("/", route({ permission: "BAN_MEMBERS" }), async (req: Request, res:
const { guild_id } = req.params; const { guild_id } = req.params;
let bans = await Ban.find({ guild_id: guild_id }); let bans = await Ban.find({ guild_id: guild_id });
let promisesToAwait: object[] = [];
const bansObj: object[] = [];
/* Filter secret from database registry.*/ bans.filter((ban) => ban.user_id !== ban.executor_id); // pretend self-bans don't exist to prevent victim chasing
bans.filter(ban => ban.user_id !== ban.executor_id); bans.forEach((ban) => {
// pretend self-bans don't exist to prevent victim chasing promisesToAwait.push(User.getPublicUser(ban.user_id));
bans.forEach((registry: BanRegistrySchema) => {
delete registry.ip;
}); });
return res.json(bans); const bannedUsers: object[] = await Promise.all(promisesToAwait);
bans.forEach((ban, index) => {
const user = bannedUsers[index] as User;
bansObj.push({
reason: ban.reason,
user: {
username: user.username,
discriminator: user.discriminator,
id: user.id,
avatar: user.avatar,
public_flags: user.public_flags
}
});
});
return res.json(bansObj);
}); });
router.get("/:user", route({ permission: "BAN_MEMBERS" }), async (req: Request, res: Response) => { router.get("/:user", route({ permission: "BAN_MEMBERS" }), async (req: Request, res: Response) => {

View File

@ -1,5 +1,5 @@
import { Request, Response, Router } from "express"; import { Request, Response, Router } from "express";
import { emitEvent, getPermission, Guild, GuildUpdateEvent, handleFile, Member } from "@fosscord/util"; import { DiscordApiErrors, emitEvent, getPermission, getRights, Guild, GuildUpdateEvent, handleFile, Member } from "@fosscord/util";
import { HTTPError } from "lambert-server"; import { HTTPError } from "lambert-server";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import "missing-native-js-functions"; import "missing-native-js-functions";
@ -37,9 +37,17 @@ router.get("/", route({}), async (req: Request, res: Response) => {
return res.send(guild); return res.send(guild);
}); });
router.patch("/", route({ body: "GuildUpdateSchema", permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => { router.patch("/", route({ body: "GuildUpdateSchema"}), async (req: Request, res: Response) => {
const body = req.body as GuildUpdateSchema; const body = req.body as GuildUpdateSchema;
const { guild_id } = req.params; const { guild_id } = req.params;
const rights = await getRights(req.user_id);
const permission = await getPermission(req.user_id, guild_id);
if (!rights.has("MANAGE_GUILDS")||!permission.has("MANAGE_GUILD"))
throw DiscordApiErrors.MISSING_PERMISSIONS.withParams("MANAGE_GUILD");
// TODO: guild update check image // TODO: guild update check image
if (body.icon) body.icon = await handleFile(`/icons/${guild_id}`, body.icon); if (body.icon) body.icon = await handleFile(`/icons/${guild_id}`, body.icon);

View File

@ -25,13 +25,19 @@ router.patch("/", route({ body: "MemberChangeSchema" }), async (req: Request, re
const member = await Member.findOneOrFail({ where: { id: member_id, guild_id }, relations: ["roles", "user"] }); const member = await Member.findOneOrFail({ where: { id: member_id, guild_id }, relations: ["roles", "user"] });
const permission = await getPermission(req.user_id, guild_id); const permission = await getPermission(req.user_id, guild_id);
const everyone = await Role.findOneOrFail({ guild_id: guild_id, name: "@everyone", position: 0 });
if (body.roles) { if (body.roles) {
permission.hasThrow("MANAGE_ROLES"); permission.hasThrow("MANAGE_ROLES");
if (body.roles.indexOf(everyone.id) === -1) body.roles.push(everyone.id);
member.roles = body.roles.map((x) => new Role({ id: x })); // foreign key constraint will fail if role doesn't exist member.roles = body.roles.map((x) => new Role({ id: x })); // foreign key constraint will fail if role doesn't exist
} }
await member.save(); await member.save();
member.roles = member.roles.filter((x) => x.id !== everyone.id);
// do not use promise.all as we have to first write to db before emitting the event to catch errors // do not use promise.all as we have to first write to db before emitting the event to catch errors
await emitEvent({ await emitEvent({
event: "GUILD_MEMBER_UPDATE", event: "GUILD_MEMBER_UPDATE",

View File

@ -9,11 +9,19 @@ const InviteRegex = /\W/g;
router.get("/", route({ permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => { router.get("/", route({ permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => {
const { guild_id } = req.params; const { guild_id } = req.params;
const guild = await Guild.findOneOrFail({ id: guild_id });
const invite = await Invite.findOne({ where: { guild_id: guild_id, vanity_url: true } }); if (!guild.features.includes("ALIASABLE_NAMES")) {
if (!invite) 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: invite.code, uses: invite.uses }); return res.json({ code: invite.code, uses: invite.uses });
} else {
const invite = await Invite.find({ where: { guild_id: guild_id, vanity_url: true } });
if (!invite || invite.length == 0) return res.json({ code: null });
return res.json(invite.map((x) => ({ code: x.code, uses: x.uses })));
}
}); });
export interface VanityUrlSchema { export interface VanityUrlSchema {
@ -24,18 +32,33 @@ export interface VanityUrlSchema {
code?: string; code?: string;
} }
// TODO: check if guild is elgible for vanity url
router.patch("/", route({ body: "VanityUrlSchema", permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => { router.patch("/", route({ body: "VanityUrlSchema", permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => {
const { guild_id } = req.params; const { guild_id } = req.params;
const body = req.body as VanityUrlSchema; const body = req.body as VanityUrlSchema;
const code = body.code?.replace(InviteRegex, ""); const code = body.code?.replace(InviteRegex, "");
const guild = await Guild.findOneOrFail({ id: guild_id });
if (!guild.features.includes("VANITY_URL")) throw new HTTPError("Your guild doesn't support vanity urls");
if (!code || code.length === 0) throw new HTTPError("Code cannot be null or empty");
const invite = await Invite.findOne({ code }); const invite = await Invite.findOne({ code });
if (invite) throw new HTTPError("Invite already exists"); if (invite) throw new HTTPError("Invite already exists");
const { id } = await Channel.findOneOrFail({ guild_id, type: ChannelType.GUILD_TEXT }); const { id } = await Channel.findOneOrFail({ guild_id, type: ChannelType.GUILD_TEXT });
await Invite.update({ vanity_url: true, guild_id }, { code: code, channel_id: id }); await new Invite({
vanity_url: true,
code: code,
temporary: false,
uses: 0,
max_uses: 0,
max_age: 0,
created_at: new Date(),
expires_at: new Date(),
guild_id: guild_id,
channel_id: id
}).save();
return res.json({ code: code }); return res.json({ code: code });
}); });

View File

@ -1,5 +1,5 @@
import { Router, Request, Response } from "express"; import { Router, Request, Response } from "express";
import { Role, Guild, Snowflake, Config, Member, Channel, DiscordApiErrors, handleFile } from "@fosscord/util"; import { Role, Guild, Snowflake, Config, getRights, Member, Channel, DiscordApiErrors, handleFile } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { ChannelModifySchema } from "../channels/#channel_id"; import { ChannelModifySchema } from "../channels/#channel_id";
@ -20,12 +20,13 @@ export interface GuildCreateSchema {
//TODO: create default channel //TODO: create default channel
router.post("/", route({ body: "GuildCreateSchema" }), async (req: Request, res: Response) => { router.post("/", route({ body: "GuildCreateSchema", right: "CREATE_GUILDS" }), async (req: Request, res: Response) => {
const body = req.body as GuildCreateSchema; const body = req.body as GuildCreateSchema;
const { maxGuilds } = Config.get().limits.user; const { maxGuilds } = Config.get().limits.user;
const guild_count = await Member.count({ id: req.user_id }); const guild_count = await Member.count({ id: req.user_id });
if (guild_count >= maxGuilds) { const rights = await getRights(req.user_id);
if ((guild_count >= maxGuilds)&&!rights.has("MANAGE_GUILDS")) {
throw DiscordApiErrors.MAXIMUM_GUILDS.withParams(maxGuilds); throw DiscordApiErrors.MAXIMUM_GUILDS.withParams(maxGuilds);
} }

View File

@ -13,7 +13,7 @@ router.get("/:code", route({}), async (req: Request, res: Response) => {
res.status(200).send(invite); res.status(200).send(invite);
}); });
router.post("/:code", route({}), async (req: Request, res: Response) => { router.post("/:code", route({right: "JOIN_GUILDS"}), async (req: Request, res: Response) => {
const { code } = req.params; const { code } = req.params;
const { guild_id } = await Invite.findOneOrFail({ code }) const { guild_id } = await Invite.findOneOrFail({ code })
const { features } = await Guild.findOneOrFail({ id: guild_id}); const { features } = await Guild.findOneOrFail({ id: guild_id});

View File

@ -0,0 +1,12 @@
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api";
const router = Router();
router.get("/scheduled-maintenances/upcoming.json",route({}), async (req: Request, res: Response) => {
res.json({
"page": {},
"scheduled_maintenances": {}
});
});
export default router;

View File

@ -18,7 +18,7 @@ router.get("/:id", route({}), async (req: Request, res: Response) => {
access_type: 2, access_type: 2,
name: "", name: "",
features: [], features: [],
relase_date: "", release_date: "",
premium: false, premium: false,
slug: "", slug: "",
flags: 4, flags: 4,

View File

@ -18,7 +18,7 @@ router.get("/:id", route({}), async (req: Request, res: Response) => {
access_type: 2, access_type: 2,
name: "", name: "",
features: [], features: [],
relase_date: "", release_date: "",
premium: false, premium: false,
slug: "", slug: "",
flags: 4, flags: 4,

View File

@ -1,19 +1,19 @@
import { Router, Response, Request } from "express"; import { Router, Response, Request } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Config, Relase } from "@fosscord/util"; import { Config, Release } from "@fosscord/util";
const router = Router(); const router = Router();
router.get("/", route({}), async (req: Request, res: Response) => { router.get("/", route({}), async (req: Request, res: Response) => {
const { client } = Config.get(); const { client } = Config.get();
const relase = await Relase.findOneOrFail({ name: client.relases.upstreamVersion}) const release = await Release.findOneOrFail({ name: client.releases.upstreamVersion})
res.json({ res.json({
name: relase.name, name: release.name,
pub_date: relase.pub_date, pub_date: release.pub_date,
url: relase.url, url: release.url,
notes: relase.notes notes: release.notes
}); });
}); });

View File

@ -53,8 +53,6 @@ router.patch("/", route({ body: "UserModifySchema" }), async (req: Request, res:
throw FieldErrors({ email: { message: req.t("auth:register.EMAIL_INVALID"), code: "EMAIL_INVALID" } }); throw FieldErrors({ email: { message: req.t("auth:register.EMAIL_INVALID"), code: "EMAIL_INVALID" } });
} }
user.assign(body);
if (body.new_password) { if (body.new_password) {
if (!body.password && !user.email) { if (!body.password && !user.email) {
throw FieldErrors({ throw FieldErrors({
@ -64,14 +62,16 @@ router.patch("/", route({ body: "UserModifySchema" }), async (req: Request, res:
user.data.hash = await bcrypt.hash(body.new_password, 12); user.data.hash = await bcrypt.hash(body.new_password, 12);
} }
var check_username = body?.username?.replace(/\s/g, ''); if (body.username) {
var check_username = body?.username?.replace(/\s/g, '');
if(!check_username && !body?.avatar && !body?.banner) { if (!check_username) {
throw FieldErrors({ throw FieldErrors({
username: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED") } username: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED") }
}); });
}
} }
user.assign(body);
await user.save(); await user.save();
// @ts-ignore // @ts-ignore

View File

@ -1,14 +1,39 @@
import { Request, Response, Router } from "express"; import { Request, Response, Router } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { User, emitEvent } from "@fosscord/util";
const router: Router = Router(); const router: Router = Router();
router.get("/:id", route({}), async (req: Request, res: Response) => {
const { id } = req.params;
const user = await User.findOneOrFail({ where: { id: req.user_id }, select: ["notes"] });
const note = user.notes[id];
return res.json({
note: note,
note_user_id: id,
user_id: user.id,
});
});
router.put("/:id", route({}), async (req: Request, res: Response) => { router.put("/:id", route({}), async (req: Request, res: Response) => {
//TODO const { id } = req.params;
res.json({ const user = await User.findOneOrFail({ where: { id: req.user_id } });
message: "400: Bad Request", const noteUser = await User.findOneOrFail({ where: { id: id }}); //if noted user does not exist throw
code: 0 const { note } = req.body;
}).status(400);
await User.update({ id: req.user_id }, { notes: { ...user.notes, [noteUser.id]: note } });
await emitEvent({
event: "USER_NOTE_UPDATE",
data: {
note: note,
id: noteUser.id
},
user_id: user.id,
})
return res.status(204);
}); });
export default router; export default router;

View File

@ -7,6 +7,7 @@ import {
MessageCreateEvent, MessageCreateEvent,
MessageUpdateEvent, MessageUpdateEvent,
getPermission, getPermission,
getRights,
CHANNEL_MENTION, CHANNEL_MENTION,
Snowflake, Snowflake,
USER_MENTION, USER_MENTION,
@ -61,19 +62,20 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
throw new HTTPError("Content length over max character limit") throw new HTTPError("Content length over max character limit")
} }
// TODO: are tts messages allowed in dm channels? should permission be checked?
if (opts.author_id) { if (opts.author_id) {
message.author = await User.getPublicUser(opts.author_id); message.author = await User.getPublicUser(opts.author_id);
} const rights = await getRights(opts.author_id);
rights.hasThrow("SEND_MESSAGES");
}
if (opts.application_id) { if (opts.application_id) {
message.application = await Application.findOneOrFail({ id: opts.application_id }); message.application = await Application.findOneOrFail({ id: opts.application_id });
} }
if (opts.webhook_id) { if (opts.webhook_id) {
message.webhook = await Webhook.findOneOrFail({ id: opts.webhook_id }); message.webhook = await Webhook.findOneOrFail({ id: opts.webhook_id });
} }
const permission = await getPermission(opts.author_id, channel.guild_id, opts.channel_id); const permission = await getPermission(opts.author_id, channel.guild_id, opts.channel_id);
permission.hasThrow("SEND_MESSAGES"); // TODO: add the rights check permission.hasThrow("SEND_MESSAGES");
if (permission.cache.member) { if (permission.cache.member) {
message.member = permission.cache.member; message.member = permission.cache.member;
} }
@ -81,13 +83,15 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
if (opts.tts) permission.hasThrow("SEND_TTS_MESSAGES"); if (opts.tts) permission.hasThrow("SEND_TTS_MESSAGES");
if (opts.message_reference) { if (opts.message_reference) {
permission.hasThrow("READ_MESSAGE_HISTORY"); permission.hasThrow("READ_MESSAGE_HISTORY");
// code below has to be redone when we add custom message routing and cross-channel replies // code below has to be redone when we add custom message routing
const guild = await Guild.findOneOrFail({ id: channel.guild_id }); if (message.guild_id !== null) {
if (!guild.features.includes("CROSS_CHANNEL_REPLIES")) { const guild = await Guild.findOneOrFail({ id: channel.guild_id });
if (opts.message_reference.guild_id !== channel.guild_id) throw new HTTPError("You can only reference messages from this guild"); if (!guild.features.includes("CROSS_CHANNEL_REPLIES")) {
if (opts.message_reference.channel_id !== opts.channel_id) throw new HTTPError("You can only reference messages from this channel"); if (opts.message_reference.guild_id !== channel.guild_id) throw new HTTPError("You can only reference messages from this guild");
if (opts.message_reference.channel_id !== opts.channel_id) throw new HTTPError("You can only reference messages from this channel");
}
} }
// TODO: should be checked if the referenced message exists? // Q: should be checked if the referenced message exists? ANSWER: NO
// @ts-ignore // @ts-ignore
message.type = MessageType.REPLY; message.type = MessageType.REPLY;
} }

View File

@ -13,6 +13,7 @@ const blocklist: string[] = []; // TODO: update ones passwordblocklist is stored
* - min <n> numbers * - min <n> numbers
* - min <n> symbols * - min <n> symbols
* - min <n> uppercase chars * - min <n> uppercase chars
* - shannon entropy divided by password entropy
* *
* Returns: 0 > pw > 1 * Returns: 0 > pw > 1
*/ */
@ -22,28 +23,38 @@ export function checkPassword(password: string): number {
// checks for total password len // checks for total password len
if (password.length >= minLength - 1) { if (password.length >= minLength - 1) {
strength += 0.25; strength += 0.05;
} }
// checks for amount of Numbers // checks for amount of Numbers
if (password.count(reNUMBER) >= minNumbers - 1) { if (password.count(reNUMBER) >= minNumbers - 1) {
strength += 0.25; strength += 0.05;
} }
// checks for amount of Uppercase Letters // checks for amount of Uppercase Letters
if (password.count(reUPPERCASELETTER) >= minUpperCase - 1) { if (password.count(reUPPERCASELETTER) >= minUpperCase - 1) {
strength += 0.25; strength += 0.05;
} }
// checks for amount of symbols // checks for amount of symbols
if (password.replace(reSYMBOLS, "").length >= minSymbols - 1) { if (password.replace(reSYMBOLS, "").length >= minSymbols - 1) {
strength += 0.25; strength += 0.05;
} }
// checks if password only consists of numbers or only consists of chars // checks if password only consists of numbers or only consists of chars
if (password.length == password.count(reNUMBER) || password.length === password.count(reUPPERCASELETTER)) { if (password.length == password.count(reNUMBER) || password.length === password.count(reUPPERCASELETTER)) {
strength = 0; strength = 0;
} }
let entropyMap;
for (let i = 0; i < password.length; i++) {
if (entropyMap[password[i]]) entropyMap[password[i]]++;
else entropyMap[password[i]] = 1;
}
let entropies = Array(entropyMap);
entropies.map(x => (x / entropyMap.length));
strength += entropies.reduceRight((a, x), a - (x * Math.log2(x))) / Math.log2(password.length);
return strength; return strength;
} }

View File

@ -1,18 +1,35 @@
{ {
// Use IntelliSense to learn about possible attributes. "version": "0.2.0",
// Hover to view descriptions of existing attributes. "configurations": [
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 {
"version": "0.2.0", "sourceMaps": true,
"configurations": [ "name": "ts-node",
{ "type": "node",
"sourceMaps": true, "request": "launch",
"type": "node", "args": [
"request": "launch", "${workspaceFolder}/src/start.ts"
"name": "Launch Server", ],
"program": "${workspaceFolder}/dist/bundle/src/start.js", "runtimeArgs": [
"preLaunchTask": "tsc: build - tsconfig.json", "-r",
"outFiles": ["${workspaceFolder}/dist/**/*.js"], "ts-node/register"
"envFile": "${workspaceFolder}/.env" ],
} "protocol": "inspector",
] "internalConsoleOptions": "openOnSessionStart",
} "env": {
"TS_NODE_PROJECT": "${workspaceFolder}/tsnode.tsconfig.json",
"TS_NODE_COMPILER": "typescript-cached-transpile"
},
"resolveSourceMapLocations": null, /* allow breakpoints in modules other than bundle */
},
{
"sourceMaps": true,
"type": "node",
"request": "launch",
"name": "Launch Server",
"program": "${workspaceFolder}/dist/bundle/src/start.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"envFile": "${workspaceFolder}/.env",
}
]
}

210
bundle/package-lock.json generated
View File

@ -45,6 +45,7 @@
"missing-native-js-functions": "^1.2.18", "missing-native-js-functions": "^1.2.18",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"multer": "^1.4.2", "multer": "^1.4.2",
"nan": "^2.15.0",
"nanocolors": "^0.2.12", "nanocolors": "^0.2.12",
"node-fetch": "^2.6.2", "node-fetch": "^2.6.2",
"node-os-utils": "^1.3.5", "node-os-utils": "^1.3.5",
@ -58,6 +59,7 @@
"tslib": "^2.3.1", "tslib": "^2.3.1",
"typeorm": "^0.2.37", "typeorm": "^0.2.37",
"typescript": "^4.1.2", "typescript": "^4.1.2",
"typescript-cached-transpile": "^0.0.6",
"typescript-json-schema": "^0.50.1", "typescript-json-schema": "^0.50.1",
"ws": "^7.4.2" "ws": "^7.4.2"
}, },
@ -90,6 +92,7 @@
"ts-node": "^10.2.1", "ts-node": "^10.2.1",
"ts-node-dev": "^1.1.6", "ts-node-dev": "^1.1.6",
"ts-patch": "^1.4.4", "ts-patch": "^1.4.4",
"tsconfig-paths": "^3.12.0",
"typescript": "^4.2.3", "typescript": "^4.2.3",
"typescript-json-schema": "0.50.1" "typescript-json-schema": "0.50.1"
} }
@ -124,7 +127,7 @@
"missing-native-js-functions": "^1.2.18", "missing-native-js-functions": "^1.2.18",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"multer": "^1.4.2", "multer": "^1.4.2",
"node-fetch": "^3.1.1", "node-fetch": "^2.6.2",
"patch-package": "^6.4.7", "patch-package": "^6.4.7",
"picocolors": "^1.0.0", "picocolors": "^1.0.0",
"proxy-agent": "^5.0.0", "proxy-agent": "^5.0.0",
@ -145,7 +148,7 @@
"@types/morgan": "^1.9.3", "@types/morgan": "^1.9.3",
"@types/multer": "^1.4.5", "@types/multer": "^1.4.5",
"@types/node": "^14.17.9", "@types/node": "^14.17.9",
"@types/node-fetch": "^2.5.7", "@types/node-fetch": "^2.5.5",
"@types/supertest": "^2.0.11", "@types/supertest": "^2.0.11",
"@zerollup/ts-transform-paths": "^1.7.18", "@zerollup/ts-transform-paths": "^1.7.18",
"jest": "^27.2.5", "jest": "^27.2.5",
@ -182,7 +185,7 @@
"missing-native-js-functions": "^1.2.17", "missing-native-js-functions": "^1.2.17",
"multer": "^1.4.2", "multer": "^1.4.2",
"nanocolors": "^0.2.12", "nanocolors": "^0.2.12",
"node-fetch": "^2.6.7", "node-fetch": "^2.6.2",
"supertest": "^6.1.6", "supertest": "^6.1.6",
"typescript": "^4.1.2" "typescript": "^4.1.2"
}, },
@ -3601,6 +3604,12 @@
"integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==",
"dev": true "dev": true
}, },
"node_modules/@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true
},
"node_modules/@types/jsonwebtoken": { "node_modules/@types/jsonwebtoken": {
"version": "8.5.5", "version": "8.5.5",
"integrity": "sha512-OGqtHQ7N5/Ap/TUwO6IgHDuLiAoTmHhGpNvgkCm/F4N6pKzx/RBSfr2OXZSwC6vkfnsEdb6+7DNZVtiXiwdwFw==", "integrity": "sha512-OGqtHQ7N5/Ap/TUwO6IgHDuLiAoTmHhGpNvgkCm/F4N6pKzx/RBSfr2OXZSwC6vkfnsEdb6+7DNZVtiXiwdwFw==",
@ -7325,8 +7334,9 @@
} }
}, },
"node_modules/minimist": { "node_modules/minimist": {
"version": "1.2.5", "version": "1.2.6",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
}, },
"node_modules/minipass": { "node_modules/minipass": {
"version": "3.1.5", "version": "3.1.5",
@ -7610,6 +7620,11 @@
"thenify-all": "^1.0.0" "thenify-all": "^1.0.0"
} }
}, },
"node_modules/nan": {
"version": "2.15.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ=="
},
"node_modules/nanocolors": { "node_modules/nanocolors": {
"version": "0.2.12", "version": "0.2.12",
"integrity": "sha512-SFNdALvzW+rVlzqexid6epYdt8H9Zol7xDoQarioEFcFN0JHo4CYNztAxmtfgGTVRCmFlEOqqhBpoFGKqSAMug==" "integrity": "sha512-SFNdALvzW+rVlzqexid6epYdt8H9Zol7xDoQarioEFcFN0JHo4CYNztAxmtfgGTVRCmFlEOqqhBpoFGKqSAMug=="
@ -10157,6 +10172,39 @@
"strip-json-comments": "^2.0.0" "strip-json-comments": "^2.0.0"
} }
}, },
"node_modules/tsconfig-paths": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz",
"integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==",
"dev": true,
"dependencies": {
"@types/json5": "^0.0.29",
"json5": "^1.0.1",
"minimist": "^1.2.0",
"strip-bom": "^3.0.0"
}
},
"node_modules/tsconfig-paths/node_modules/json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"dev": true,
"dependencies": {
"minimist": "^1.2.0"
},
"bin": {
"json5": "lib/cli.js"
}
},
"node_modules/tsconfig-paths/node_modules/strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/tsconfig/node_modules/strip-bom": { "node_modules/tsconfig/node_modules/strip-bom": {
"version": "3.0.0", "version": "3.0.0",
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
@ -10418,7 +10466,6 @@
"node_modules/typescript": { "node_modules/typescript": {
"version": "4.4.3", "version": "4.4.3",
"integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==",
"dev": true,
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"
@ -10427,6 +10474,58 @@
"node": ">=4.2.0" "node": ">=4.2.0"
} }
}, },
"node_modules/typescript-cached-transpile": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typescript-cached-transpile/-/typescript-cached-transpile-0.0.6.tgz",
"integrity": "sha512-bfPc7YUW0PrVkQHU0xN0ANRuxdPgoYYXtZEW6PNkH5a97/AOM+kPPxSTMZbpWA3BG1do22JUkfC60KoCKJ9VZQ==",
"dependencies": {
"@types/node": "^12.12.7",
"fs-extra": "^8.1.0",
"tslib": "^1.10.0"
},
"peerDependencies": {
"typescript": "*"
}
},
"node_modules/typescript-cached-transpile/node_modules/@types/node": {
"version": "12.20.41",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.41.tgz",
"integrity": "sha512-f6xOqucbDirG7LOzedpvzjP3UTmHttRou3Mosx3vL9wr9AIQGhcPgVnqa8ihpZYnxyM1rxeNCvTyukPKZtq10Q=="
},
"node_modules/typescript-cached-transpile/node_modules/fs-extra": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
},
"engines": {
"node": ">=6 <7 || >=8"
}
},
"node_modules/typescript-cached-transpile/node_modules/jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/typescript-cached-transpile/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/typescript-cached-transpile/node_modules/universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/typescript-json-schema": { "node_modules/typescript-json-schema": {
"version": "0.50.1", "version": "0.50.1",
"integrity": "sha512-GCof/SDoiTDl0qzPonNEV4CHyCsZEIIf+mZtlrjoD8vURCcEzEfa2deRuxYid8Znp/e27eDR7Cjg8jgGrimBCA==", "integrity": "sha512-GCof/SDoiTDl0qzPonNEV4CHyCsZEIIf+mZtlrjoD8vURCcEzEfa2deRuxYid8Znp/e27eDR7Cjg8jgGrimBCA==",
@ -12885,7 +12984,7 @@
"@types/morgan": "^1.9.3", "@types/morgan": "^1.9.3",
"@types/multer": "^1.4.5", "@types/multer": "^1.4.5",
"@types/node": "^14.17.9", "@types/node": "^14.17.9",
"@types/node-fetch": "^2.5.7", "@types/node-fetch": "^2.5.5",
"@types/supertest": "^2.0.11", "@types/supertest": "^2.0.11",
"@zerollup/ts-transform-paths": "^1.7.18", "@zerollup/ts-transform-paths": "^1.7.18",
"ajv": "8.6.2", "ajv": "8.6.2",
@ -12910,7 +13009,7 @@
"missing-native-js-functions": "^1.2.18", "missing-native-js-functions": "^1.2.18",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"multer": "^1.4.2", "multer": "^1.4.2",
"node-fetch": "^3.1.1", "node-fetch": "^2.6.2",
"patch-package": "^6.4.7", "patch-package": "^6.4.7",
"picocolors": "^1.0.0", "picocolors": "^1.0.0",
"proxy-agent": "^5.0.0", "proxy-agent": "^5.0.0",
@ -12956,7 +13055,7 @@
"missing-native-js-functions": "^1.2.17", "missing-native-js-functions": "^1.2.17",
"multer": "^1.4.2", "multer": "^1.4.2",
"nanocolors": "^0.2.12", "nanocolors": "^0.2.12",
"node-fetch": "^2.6.7", "node-fetch": "^2.6.2",
"supertest": "^6.1.6", "supertest": "^6.1.6",
"ts-patch": "^1.4.4", "ts-patch": "^1.4.4",
"typescript": "^4.1.2" "typescript": "^4.1.2"
@ -13633,6 +13732,12 @@
"integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==",
"dev": true "dev": true
}, },
"@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true
},
"@types/jsonwebtoken": { "@types/jsonwebtoken": {
"version": "8.5.5", "version": "8.5.5",
"integrity": "sha512-OGqtHQ7N5/Ap/TUwO6IgHDuLiAoTmHhGpNvgkCm/F4N6pKzx/RBSfr2OXZSwC6vkfnsEdb6+7DNZVtiXiwdwFw==", "integrity": "sha512-OGqtHQ7N5/Ap/TUwO6IgHDuLiAoTmHhGpNvgkCm/F4N6pKzx/RBSfr2OXZSwC6vkfnsEdb6+7DNZVtiXiwdwFw==",
@ -16446,8 +16551,9 @@
} }
}, },
"minimist": { "minimist": {
"version": "1.2.5", "version": "1.2.6",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
}, },
"minipass": { "minipass": {
"version": "3.1.5", "version": "3.1.5",
@ -16633,6 +16739,11 @@
"thenify-all": "^1.0.0" "thenify-all": "^1.0.0"
} }
}, },
"nan": {
"version": "2.15.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ=="
},
"nanocolors": { "nanocolors": {
"version": "0.2.12", "version": "0.2.12",
"integrity": "sha512-SFNdALvzW+rVlzqexid6epYdt8H9Zol7xDoQarioEFcFN0JHo4CYNztAxmtfgGTVRCmFlEOqqhBpoFGKqSAMug==" "integrity": "sha512-SFNdALvzW+rVlzqexid6epYdt8H9Zol7xDoQarioEFcFN0JHo4CYNztAxmtfgGTVRCmFlEOqqhBpoFGKqSAMug=="
@ -18517,6 +18628,35 @@
} }
} }
}, },
"tsconfig-paths": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz",
"integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==",
"dev": true,
"requires": {
"@types/json5": "^0.0.29",
"json5": "^1.0.1",
"minimist": "^1.2.0",
"strip-bom": "^3.0.0"
},
"dependencies": {
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"dev": true,
"requires": {
"minimist": "^1.2.0"
}
},
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
"dev": true
}
}
},
"tslib": { "tslib": {
"version": "2.3.1", "version": "2.3.1",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
@ -18649,8 +18789,52 @@
}, },
"typescript": { "typescript": {
"version": "4.4.3", "version": "4.4.3",
"integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA=="
"dev": true },
"typescript-cached-transpile": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typescript-cached-transpile/-/typescript-cached-transpile-0.0.6.tgz",
"integrity": "sha512-bfPc7YUW0PrVkQHU0xN0ANRuxdPgoYYXtZEW6PNkH5a97/AOM+kPPxSTMZbpWA3BG1do22JUkfC60KoCKJ9VZQ==",
"requires": {
"@types/node": "^12.12.7",
"fs-extra": "^8.1.0",
"tslib": "^1.10.0"
},
"dependencies": {
"@types/node": {
"version": "12.20.41",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.41.tgz",
"integrity": "sha512-f6xOqucbDirG7LOzedpvzjP3UTmHttRou3Mosx3vL9wr9AIQGhcPgVnqa8ihpZYnxyM1rxeNCvTyukPKZtq10Q=="
},
"fs-extra": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"requires": {
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
}
},
"jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
"requires": {
"graceful-fs": "^4.1.6"
}
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
}
}
}, },
"typescript-json-schema": { "typescript-json-schema": {
"version": "0.50.1", "version": "0.50.1",

View File

@ -9,7 +9,8 @@
"start": "node scripts/build.js && node dist/bundle/src/start.js", "start": "node scripts/build.js && node dist/bundle/src/start.js",
"start:bundle": "node dist/bundle/src/start.js", "start:bundle": "node dist/bundle/src/start.js",
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"migrate": "cd ../util/ && npm i && node --require ts-node/register node_modules/typeorm/cli.js -f ../util/ormconfig.json migration:run" "migrate": "cd ../util/ && npm i && node --require ts-node/register node_modules/typeorm/cli.js -f ../util/ormconfig.json migration:run",
"tsnode": "npx ts-node --transpile-only -P tsnode.tsconfig.json src/start.ts"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -51,6 +52,7 @@
"ts-node": "^10.2.1", "ts-node": "^10.2.1",
"ts-node-dev": "^1.1.6", "ts-node-dev": "^1.1.6",
"ts-patch": "^1.4.4", "ts-patch": "^1.4.4",
"tsconfig-paths": "^3.12.0",
"typescript": "^4.2.3", "typescript": "^4.2.3",
"typescript-json-schema": "0.50.1" "typescript-json-schema": "0.50.1"
}, },
@ -91,6 +93,7 @@
"missing-native-js-functions": "^1.2.18", "missing-native-js-functions": "^1.2.18",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"multer": "^1.4.2", "multer": "^1.4.2",
"nan": "^2.15.0",
"nanocolors": "^0.2.12", "nanocolors": "^0.2.12",
"node-fetch": "^2.6.2", "node-fetch": "^2.6.2",
"node-os-utils": "^1.3.5", "node-os-utils": "^1.3.5",
@ -104,8 +107,8 @@
"tslib": "^2.3.1", "tslib": "^2.3.1",
"typeorm": "^0.2.37", "typeorm": "^0.2.37",
"typescript": "^4.1.2", "typescript": "^4.1.2",
"typescript-cached-transpile": "^0.0.6",
"typescript-json-schema": "^0.50.1", "typescript-json-schema": "^0.50.1",
"ws": "^7.4.2", "ws": "^7.4.2"
"nan": "^2.15.0"
} }
} }

View File

@ -1,86 +1,85 @@
{ {
"include": ["dist/**/*.ts"], "include": ["dist/**/*.ts"],
"exclude": [], "exclude": [],
"compilerOptions": { "compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Basic Options */
/* Basic Options */ "incremental": false /* Enable incremental compilation */,
"incremental": false /* Enable incremental compilation */, "target": "ES6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
"target": "ES6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, "lib": [
"lib": [ "ES2021"
"ES2021" ] /* Specify library files to be included in the compilation. */,
] /* Specify library files to be included in the compilation. */, "allowJs": true /* Allow javascript files to be compiled. */,
"allowJs": true /* Allow javascript files to be compiled. */, "checkJs": true /* Report errors in .js files. */,
"checkJs": true /* Report errors in .js files. */, // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ "declaration": false /* Generates corresponding '.d.ts' file. */,
"declaration": false /* Generates corresponding '.d.ts' file. */, "declarationMap": false /* Generates a sourcemap for each corresponding '.d.ts' file. */,
"declarationMap": false /* Generates a sourcemap for each corresponding '.d.ts' file. */, "sourceMap": true /* Generates corresponding '.map' file. */,
"sourceMap": false /* Generates corresponding '.map' file. */, // "outFile": "./", /* Concatenate and emit output to single file. */
// "outFile": "./", /* Concatenate and emit output to single file. */ "outDir": "./dist/" /* Redirect output structure to the directory. */,
"outDir": "./dist/" /* Redirect output structure to the directory. */, "rootDir": "./dist/" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,
"rootDir": "./dist/" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, // "composite": true, /* Enable project compilation */
// "composite": true, /* Enable project compilation */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ // "removeComments": true, /* Do not emit comments to output. */
// "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */
// "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */ // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
/* Strict Type-Checking Options */ "strict": true /* Enable all strict type-checking options. */,
"strict": true /* Enable all strict type-checking options. */, "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, "strictNullChecks": true /* Enable strict null checks. */,
"strictNullChecks": true /* Enable strict null checks. */, // "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ "strictPropertyInitialization": false /* Enable strict checking of property initialization in classes. */,
"strictPropertyInitialization": false /* Enable strict checking of property initialization in classes. */, // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */,
"alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */,
/* Additional Checks */
/* Additional Checks */ // "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedLocals": true, /* Report errors on unused locals. */ // "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
/* Module Resolution Options */ "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */
// "typeRoots": [], /* List of folders to include type definitions from. */ "types": [
"types": [ "node"
"node" ] /* Type declaration files to be included in compilation. */,
] /* Type declaration files to be included in compilation. */, // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
/* Source Map Options */ // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
/* Experimental Options */ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
/* Advanced Options */ "skipLibCheck": true /* Skip type checking of declaration files. */,
"skipLibCheck": true /* Skip type checking of declaration files. */, "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, "emitDecoratorMetadata": true,
"emitDecoratorMetadata": true, "experimentalDecorators": true,
"experimentalDecorators": true, "resolveJsonModule": true,
"resolveJsonModule": true, "baseUrl": "./dist/",
"baseUrl": "./dist/", "paths": {
"paths": { "@fosscord/api": ["api/src/index"],
"@fosscord/api": ["api/src/index"], "@fosscord/gateway": ["gateway/src/index"],
"@fosscord/gateway": ["gateway/src/index"], "@fosscord/cdn": ["cdn/src/index"],
"@fosscord/cdn": ["cdn/src/index"], "@fosscord/util": ["util/src/index"]
"@fosscord/util": ["util/src/index"] },
}, "plugins": [{ "transform": "@zerollup/ts-transform-paths" }],
"plugins": [{ "transform": "@zerollup/ts-transform-paths" }], "noEmitHelpers": true,
"noEmitHelpers": true, "importHelpers": true
"importHelpers": true }
} }
}

View File

@ -0,0 +1,15 @@
{
"extends": "./tsconfig.json",
"ts-node": {
"transpileOnly": true,
"preferTsExts": true,
"require": ["tsconfig-paths/register"],
"compiler": "typescript-cached-transpile",
},
"compilerOptions": {
"rootDir": "../",
"baseUrl": "../",
"sourceRoot": "../",
"sourceMap": true,
}
}

14
cdn/package-lock.json generated
View File

@ -28,7 +28,7 @@
"missing-native-js-functions": "^1.2.17", "missing-native-js-functions": "^1.2.17",
"multer": "^1.4.2", "multer": "^1.4.2",
"nanocolors": "^0.2.12", "nanocolors": "^0.2.12",
"node-fetch": "^2.6.7", "node-fetch": "^2.6.2",
"supertest": "^6.1.6", "supertest": "^6.1.6",
"typescript": "^4.1.2" "typescript": "^4.1.2"
}, },
@ -5739,9 +5739,9 @@
} }
}, },
"node_modules/minimist": { "node_modules/minimist": {
"version": "1.2.5", "version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
}, },
"node_modules/minipass": { "node_modules/minipass": {
"version": "3.1.6", "version": "3.1.6",
@ -12301,9 +12301,9 @@
} }
}, },
"minimist": { "minimist": {
"version": "1.2.5", "version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
}, },
"minipass": { "minipass": {
"version": "3.1.6", "version": "3.1.6",

View File

@ -54,7 +54,7 @@
"missing-native-js-functions": "^1.2.17", "missing-native-js-functions": "^1.2.17",
"multer": "^1.4.2", "multer": "^1.4.2",
"nanocolors": "^0.2.12", "nanocolors": "^0.2.12",
"node-fetch": "^2.6.7", "node-fetch": "^2.6.2",
"supertest": "^6.1.6", "supertest": "^6.1.6",
"typescript": "^4.1.2" "typescript": "^4.1.2"
}, },

View File

@ -1,7 +1,47 @@
version: "3" version: '3.8'
services: services:
server: fosscord:
image: fosscord/server container_name: fosscord
image: fosscord
restart: on-failure:5
# depends_on: mariadb
build: . build: .
ports: ports:
- 3001:3001 - '3001-3005:3001-3005'
volumes:
# - ./data/:${WORK_DIR:-/srv/fosscord-server}/data/
- data:${WORK_DIR:-/srv/fosscord-server}/
environment:
WORK_DIR: ${WORK_DIR:-/srv/fosscord-server}
DEV_MODE: ${DEV_MODE:-0}
THREADS: ${THREADS:-1}
DATABASE: ${DATABASE:-../../data/database.db}
STORAGE_LOCATION: ${STORAGE_LOCATION:-../../data/files/}
HTTP_PORT: 3001
WS_PORT: 3002
CDN_PORT: 3003
RTC_PORT: 3004
ADMIN_PORT: 3005
# mariadb:
# image: mariadb:latest
# restart: on-failure:5
# environment:
# MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-secr3tpassw0rd}
# MYSQL_DATABASE: ${MYSQL_DATABASE:-fosscord}
# MYSQL_USER: ${MYSQL_USER:-fosscord}
# MYSQL_PASSWORD: ${MYSQL_PASSWORD:-password1}
# networks:
# - default
# volumes:
# - mariadb:/var/lib/mysql
volumes:
data:
# mariadb:
networks:
default:
name: fosscord
driver: bridge

View File

@ -4479,8 +4479,9 @@
} }
}, },
"../util/node_modules/minimist": { "../util/node_modules/minimist": {
"version": "1.2.5", "version": "1.2.6",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
}, },
"../util/node_modules/minipass": { "../util/node_modules/minipass": {
"version": "2.9.0", "version": "2.9.0",
@ -8768,8 +8769,9 @@
} }
}, },
"node_modules/minimist": { "node_modules/minimist": {
"version": "1.2.5", "version": "1.2.6",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
"dev": true "dev": true
}, },
"node_modules/minipass": { "node_modules/minipass": {
@ -13666,8 +13668,9 @@
} }
}, },
"minimist": { "minimist": {
"version": "1.2.5", "version": "1.2.6",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
}, },
"minipass": { "minipass": {
"version": "2.9.0", "version": "2.9.0",
@ -16870,8 +16873,9 @@
} }
}, },
"minimist": { "minimist": {
"version": "1.2.5", "version": "1.2.6",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
"dev": true "dev": true
}, },
"minipass": { "minipass": {

View File

@ -240,8 +240,6 @@ export async function onIdentify(this: WebSocket, data: Payload) {
x.guild_hashes = {}; // @ts-ignore x.guild_hashes = {}; // @ts-ignore
x.guild_scheduled_events = []; // @ts-ignore x.guild_scheduled_events = []; // @ts-ignore
x.threads = []; x.threads = [];
x.premium_subscription_count = 30;
x.premium_tier = 3;
return x; return x;
}), }),
guild_experiments: [], // TODO guild_experiments: [], // TODO

12
util/package-lock.json generated
View File

@ -5003,9 +5003,9 @@
} }
}, },
"node_modules/minimist": { "node_modules/minimist": {
"version": "1.2.5", "version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
}, },
"node_modules/minipass": { "node_modules/minipass": {
"version": "2.9.0", "version": "2.9.0",
@ -12060,9 +12060,9 @@
} }
}, },
"minimist": { "minimist": {
"version": "1.2.5", "version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
}, },
"minipass": { "minipass": {
"version": "2.9.0", "version": "2.9.0",

View File

@ -14,19 +14,23 @@ import { Webhook } from "./Webhook";
import { DmChannelDTO } from "../dtos"; import { DmChannelDTO } from "../dtos";
export enum ChannelType { export enum ChannelType {
GUILD_TEXT = 0, // a text channel within a server GUILD_TEXT = 0, // a text channel within a guild
DM = 1, // a direct message between users DM = 1, // a direct message between users
GUILD_VOICE = 2, // a voice channel within a server GUILD_VOICE = 2, // a voice channel within a guild
GROUP_DM = 3, // a direct message between multiple users GROUP_DM = 3, // a direct message between multiple users
GUILD_CATEGORY = 4, // an organizational category that contains up to 50 channels GUILD_CATEGORY = 4, // an organizational category that contains zero or more channels
GUILD_NEWS = 5, // a channel that users can follow and crosspost into their own server GUILD_NEWS = 5, // a channel that users can follow and crosspost into a guild or route
GUILD_STORE = 6, // a channel in which game developers can sell their game on Discord GUILD_STORE = 6, // a channel in which game developers can sell their things
ENCRYPTED = 7, // end-to-end encrypted channel ENCRYPTED = 7, // end-to-end encrypted channel
ENCRYPTED_THREAD = 8, // end-to-end encrypted thread channel ENCRYPTED_THREAD = 8, // end-to-end encrypted thread channel
TRANSACTIONAL = 9, // event chain style transactional channel
GUILD_NEWS_THREAD = 10, // a temporary sub-channel within a GUILD_NEWS channel GUILD_NEWS_THREAD = 10, // a temporary sub-channel within a GUILD_NEWS channel
GUILD_PUBLIC_THREAD = 11, // a temporary sub-channel within a GUILD_TEXT channel GUILD_PUBLIC_THREAD = 11, // a temporary sub-channel within a GUILD_TEXT channel
GUILD_PRIVATE_THREAD = 12, // a temporary sub-channel within a GUILD_TEXT channel that is only viewable by those invited and those with the MANAGE_THREADS permission GUILD_PRIVATE_THREAD = 12, // a temporary sub-channel within a GUILD_TEXT channel that is only viewable by those invited and those with the MANAGE_THREADS permission
GUILD_STAGE_VOICE = 13, // a voice channel for hosting events with an audience GUILD_STAGE_VOICE = 13, // a voice channel for hosting events with an audience
TICKET_TRACKER = 33, // ticket tracker, individual ticket items shall have type 12
KANBAN = 34, // confluence like kanban board
VOICELESS_WHITEBOARD = 35, // whiteboard but without voice (whiteboard + voice is the same as stage)
CUSTOM_START = 64, // start custom channel types from here CUSTOM_START = 64, // start custom channel types from here
UNHANDLED = 255 // unhandled unowned pass-through channel type UNHANDLED = 255 // unhandled unowned pass-through channel type
} }
@ -72,7 +76,7 @@ export class Channel extends BaseClass {
@ManyToOne(() => Channel) @ManyToOne(() => Channel)
parent?: Channel; parent?: Channel;
// only for group dms // for group DMs and owned custom channel types
@Column({ nullable: true }) @Column({ nullable: true })
@RelationId((channel: Channel) => channel.owner) @RelationId((channel: Channel) => channel.owner)
owner_id: string; owner_id: string;
@ -117,6 +121,9 @@ export class Channel extends BaseClass {
}) })
invites?: Invite[]; invites?: Invite[];
@Column({ nullable: true })
retention_policy_id?: string;
@OneToMany(() => Message, (message: Message) => message.channel, { @OneToMany(() => Message, (message: Message) => message.channel, {
cascade: true, cascade: true,
orphanedRowAction: "delete", orphanedRowAction: "delete",
@ -140,7 +147,7 @@ export class Channel extends BaseClass {
orphanedRowAction: "delete", orphanedRowAction: "delete",
}) })
webhooks?: Webhook[]; webhooks?: Webhook[];
// TODO: DM channel // TODO: DM channel
static async createChannel( static async createChannel(
channel: Partial<Channel>, channel: Partial<Channel>,
@ -182,6 +189,7 @@ export class Channel extends BaseClass {
switch (channel.type) { switch (channel.type) {
case ChannelType.GUILD_TEXT: case ChannelType.GUILD_TEXT:
case ChannelType.GUILD_NEWS:
case ChannelType.GUILD_VOICE: case ChannelType.GUILD_VOICE:
if (channel.parent_id && !opts?.skipExistsCheck) { if (channel.parent_id && !opts?.skipExistsCheck) {
const exists = await Channel.findOneOrFail({ id: channel.parent_id }); const exists = await Channel.findOneOrFail({ id: channel.parent_id });
@ -191,25 +199,24 @@ export class Channel extends BaseClass {
} }
break; break;
case ChannelType.GUILD_CATEGORY: case ChannelType.GUILD_CATEGORY:
case ChannelType.UNHANDLED:
break; break;
case ChannelType.DM: case ChannelType.DM:
case ChannelType.GROUP_DM: case ChannelType.GROUP_DM:
throw new HTTPError("You can't create a dm channel in a guild"); throw new HTTPError("You can't create a dm channel in a guild");
// TODO: check if guild is community server
case ChannelType.GUILD_STORE: case ChannelType.GUILD_STORE:
case ChannelType.GUILD_NEWS:
default: default:
throw new HTTPError("Not yet supported"); throw new HTTPError("Not yet supported");
} }
if (!channel.permission_overwrites) channel.permission_overwrites = []; if (!channel.permission_overwrites) channel.permission_overwrites = [];
// TODO: auto generate position // TODO: eagerly auto generate position of all guild channels
channel = { channel = {
...channel, ...channel,
...(!opts?.keepId && { id: Snowflake.generate() }), ...(!opts?.keepId && { id: Snowflake.generate() }),
created_at: new Date(), created_at: new Date(),
position: channel.position || 0, position: (channel.type === ChannelType.UNHANDLED ? 0 : channel.position) || 0,
}; };
await Promise.all([ await Promise.all([
@ -231,11 +238,13 @@ export class Channel extends BaseClass {
const otherRecipientsUsers = await User.find({ where: recipients.map((x) => ({ id: x })) }); const otherRecipientsUsers = await User.find({ where: recipients.map((x) => ({ id: x })) });
// TODO: check config for max number of recipients // TODO: check config for max number of recipients
/** if you want to disallow note to self channels, uncomment the conditional below
if (otherRecipientsUsers.length !== recipients.length) { if (otherRecipientsUsers.length !== recipients.length) {
throw new HTTPError("Recipient/s not found"); throw new HTTPError("Recipient/s not found");
} }
**/
const type = recipients.length === 1 ? ChannelType.DM : ChannelType.GROUP_DM; const type = recipients.length > 1 ? ChannelType.DM : ChannelType.GROUP_DM;
let channel = null; let channel = null;
@ -288,7 +297,8 @@ export class Channel extends BaseClass {
await emitEvent({ event: "CHANNEL_CREATE", data: channel_dto, user_id: creator_user_id }); await emitEvent({ event: "CHANNEL_CREATE", data: channel_dto, user_id: creator_user_id });
} }
return channel_dto.excludedRecipients([creator_user_id]); if (recipients.length === 1) return channel_dto;
else return channel_dto.excludedRecipients([creator_user_id]);
} }
static async removeRecipientFromChannel(channel: Channel, user_id: string) { static async removeRecipientFromChannel(channel: Channel, user_id: string) {
@ -354,4 +364,5 @@ export interface ChannelPermissionOverwrite {
export enum ChannelPermissionOverwriteType { export enum ChannelPermissionOverwriteType {
role = 0, role = 0,
member = 1, member = 1,
group = 2,
} }

View File

@ -1,8 +1,8 @@
import { Column, Entity} from "typeorm"; import { Column, Entity} from "typeorm";
import { BaseClass } from "./BaseClass"; import { BaseClass } from "./BaseClass";
@Entity("client_relase") @Entity("client_release")
export class Relase extends BaseClass { export class Release extends BaseClass {
@Column() @Column()
name: string; name: string;

View File

@ -188,8 +188,8 @@ export interface ConfigValue {
}, },
client: { client: {
useTestClient: Boolean; useTestClient: Boolean;
relases: { releases: {
useLocalRelases: Boolean; //TODO useLocalRelease: Boolean; //TODO
upstreamVersion: string; upstreamVersion: string;
} }
}, },
@ -222,7 +222,7 @@ export const DefaultConfigOptions: ConfigValue = {
}, },
general: { general: {
instanceName: "Fosscord Instance", instanceName: "Fosscord Instance",
instanceDescription: "This is a Fosscord instance made in pre-relase days", instanceDescription: "This is a Fosscord instance made in pre-release days",
frontPage: null, frontPage: null,
tosPage: null, tosPage: null,
correspondenceEmail: "noreply@localhost.local", correspondenceEmail: "noreply@localhost.local",
@ -389,8 +389,8 @@ export const DefaultConfigOptions: ConfigValue = {
}, },
client: { client: {
useTestClient: true, useTestClient: true,
relases: { releases: {
useLocalRelases: true, useLocalRelease: true,
upstreamVersion: "0.0.264" upstreamVersion: "0.0.264"
} }
}, },

View File

@ -2,7 +2,7 @@ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
import { BaseClass } from "./BaseClass"; import { BaseClass } from "./BaseClass";
import { User } from "./User"; import { User } from "./User";
export interface PublicConnectedAccount extends Pick<ConnectedAccount, "name" | "type" | "verifie"> {} export interface PublicConnectedAccount extends Pick<ConnectedAccount, "name" | "type" | "verified"> {}
@Entity("connected_accounts") @Entity("connected_accounts")
export class ConnectedAccount extends BaseClass { export class ConnectedAccount extends BaseClass {
@ -35,7 +35,7 @@ export class ConnectedAccount extends BaseClass {
type: string; type: string;
@Column() @Column()
verifie: boolean; verified: boolean;
@Column({ select: false }) @Column({ select: false })
visibility: number; visibility: number;

View File

@ -0,0 +1,35 @@
import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId } from "typeorm";
import { BaseClass } from "./BaseClass";
import { Guild } from "./Guild";
import { PublicUserProjection, User } from "./User";
import { HTTPError } from "lambert-server";
import { containsAll, emitEvent, getPermission, Snowflake, trimSpecial, InvisibleCharacters } from "../util";
import { BitField, BitFieldResolvable, BitFlag } from "../util/BitField";
import { Recipient } from "./Recipient";
import { Message } from "./Message";
import { ReadState } from "./ReadState";
import { Invite } from "./Invite";
import { DmChannelDTO } from "../dtos";
@Entity("security_settings")
export class SecuritySettings extends BaseClass {
@Column({nullable: true})
guild_id: Snowflake;
@Column({nullable: true})
channel_id: Snowflake;
@Column()
encryption_permission_mask: BitField;
@Column()
allowed_algorithms: string[];
@Column()
current_algorithm: string;
@Column({nullable: true})
used_since_message: Snowflake;
}

View File

@ -187,11 +187,11 @@ export class Guild extends BaseClass {
@Column({ nullable: true }) @Column({ nullable: true })
@RelationId((guild: Guild) => guild.owner) @RelationId((guild: Guild) => guild.owner)
owner_id: string; owner_id?: string; // optional to allow for ownerless guilds
@JoinColumn({ name: "owner_id", referencedColumnName: "id" }) @JoinColumn({ name: "owner_id", referencedColumnName: "id" })
@ManyToOne(() => User) @ManyToOne(() => User)
owner: User; owner?: User; // optional to allow for ownerless guilds
@Column({ nullable: true }) @Column({ nullable: true })
preferred_locale?: string; preferred_locale?: string;
@ -200,7 +200,7 @@ export class Guild extends BaseClass {
premium_subscription_count?: number; premium_subscription_count?: number;
@Column({ nullable: true }) @Column({ nullable: true })
premium_tier?: number; // nitro boost level premium_tier?: number; // crowd premium level
@Column({ nullable: true }) @Column({ nullable: true })
@RelationId((guild: Guild) => guild.public_updates_channel) @RelationId((guild: Guild) => guild.public_updates_channel)
@ -269,6 +269,10 @@ export class Guild extends BaseClass {
@Column({ nullable: true }) @Column({ nullable: true })
nsfw?: boolean; nsfw?: boolean;
// TODO: nested guilds
@Column({ nullable: true })
parent?: string;
// only for developer portal // only for developer portal
permissions?: number; permissions?: number;
@ -308,7 +312,7 @@ export class Guild extends BaseClass {
verification_level: 0, verification_level: 0,
welcome_screen: { welcome_screen: {
enabled: false, enabled: false,
description: "No description", description: "Fill in your description",
welcome_channels: [], welcome_channels: [],
}, },
widget_enabled: true, // NB: don't set it as false to prevent artificial restrictions widget_enabled: true, // NB: don't set it as false to prevent artificial restrictions

View File

@ -70,7 +70,7 @@ export class Member extends BaseClassWithoutId {
@Column({ nullable: true }) @Column({ nullable: true })
nick?: string; nick?: string;
@JoinTable({ @JoinTable({
name: "member_roles", name: "member_roles",
joinColumn: { name: "index", referencedColumnName: "index" }, joinColumn: { name: "index", referencedColumnName: "index" },
@ -85,8 +85,8 @@ export class Member extends BaseClassWithoutId {
@Column() @Column()
joined_at: Date; joined_at: Date;
@Column({ nullable: true }) @Column({ type: "bigint", nullable: true })
premium_since?: Date; premium_since?: number;
@Column() @Column()
deaf: boolean; deaf: boolean;
@ -102,8 +102,17 @@ export class Member extends BaseClassWithoutId {
@Column({ nullable: true }) @Column({ nullable: true })
last_message_id?: string; last_message_id?: string;
/**
@JoinColumn({ name: "id" })
@ManyToOne(() => User, {
onDelete: "DO NOTHING",
// do not auto-kick force-joined members just because their joiners left the server
}) **/
@Column({ nullable: true})
joined_by?: string;
// TODO: update // TODO: add this when we have proper read receipts
// @Column({ type: "simple-json" }) // @Column({ type: "simple-json" })
// read_state: ReadState; // read_state: ReadState;
@ -245,7 +254,7 @@ export class Member extends BaseClassWithoutId {
nick: undefined, nick: undefined,
roles: [guild_id], // @everyone role roles: [guild_id], // @everyone role
joined_at: new Date(), joined_at: new Date(),
premium_since: new Date(), premium_since: (new Date()).getTime(),
deaf: false, deaf: false,
mute: false, mute: false,
pending: false, pending: false,

View File

@ -41,8 +41,14 @@ export enum MessageType {
CHANNEL_FOLLOW_ADD = 12, CHANNEL_FOLLOW_ADD = 12,
GUILD_DISCOVERY_DISQUALIFIED = 14, GUILD_DISCOVERY_DISQUALIFIED = 14,
GUILD_DISCOVERY_REQUALIFIED = 15, GUILD_DISCOVERY_REQUALIFIED = 15,
ENCRYPTED = 16,
REPLY = 19, REPLY = 19,
APPLICATION_COMMAND = 20, APPLICATION_COMMAND = 20,
ROUTE_ADDED = 41, // custom message routing: new route affecting that channel
ROUTE_DISABLED = 42, // custom message routing: given route no longer affecting that channel
ENCRYPTION = 50,
CUSTOM_START = 63,
UNHANDLED = 255
} }
@Entity("messages") @Entity("messages")
@ -84,7 +90,7 @@ export class Message extends BaseClass {
@RelationId((message: Message) => message.member) @RelationId((message: Message) => message.member)
member_id: string; member_id: string;
@JoinColumn({ name: "author_id", referencedColumnName: "id" }) @JoinColumn({ name: "member_id", referencedColumnName: "id" })
@ManyToOne(() => User, { @ManyToOne(() => User, {
onDelete: "CASCADE", onDelete: "CASCADE",
}) })
@ -203,6 +209,7 @@ export interface MessageComponent {
} }
export enum MessageComponentType { export enum MessageComponentType {
Script = 0, // self command script
ActionRow = 1, ActionRow = 1,
Button = 2, Button = 2,
} }

View File

@ -49,6 +49,7 @@ export class ReadState extends BaseClass {
@Column({ nullable: true }) @Column({ nullable: true })
mention_count: number; mention_count: number;
@Column({ nullable: true }) // @Column({ nullable: true })
// TODO: derive this from (last_message_id=notifications_cursor=public_ack)=true
manual: boolean; manual: boolean;
} }

View File

@ -60,7 +60,7 @@ export class User extends BaseClass {
username: string; // username max length 32, min 2 (should be configurable) username: string; // username max length 32, min 2 (should be configurable)
@Column() @Column()
discriminator: string; // #0001 4 digit long string from #0001 - #9999 discriminator: string; // opaque string: 4 digits on discord.com
setDiscriminator(val: string) { setDiscriminator(val: string) {
const number = Number(val); const number = Number(val);
@ -88,10 +88,10 @@ export class User extends BaseClass {
mobile: boolean; // if the user has mobile app installed mobile: boolean; // if the user has mobile app installed
@Column() @Column()
premium: boolean; // if user bought nitro premium: boolean; // if user bought individual premium
@Column() @Column()
premium_type: number; // nitro level premium_type: number; // individual premium level
@Column() @Column()
bot: boolean; // if user is bot bot: boolean; // if user is bot
@ -100,11 +100,11 @@ export class User extends BaseClass {
bio: string; // short description of the user (max 190 chars -> should be configurable) bio: string; // short description of the user (max 190 chars -> should be configurable)
@Column() @Column()
system: boolean; // shouldn't be used, the api sents this field type true, if the generated message comes from a system generated author system: boolean; // shouldn't be used, the api sends this field type true, if the generated message comes from a system generated author
@Column({ select: false }) @Column({ select: false })
nsfw_allowed: boolean; // if the user is older than 18 (resp. Config) nsfw_allowed: boolean; // if the user can do age-restricted actions (NSFW channels/guilds/commands)
@Column({ select: false }) @Column({ select: false })
mfa_enabled: boolean; // if multi factor authentication is enabled mfa_enabled: boolean; // if multi factor authentication is enabled
@ -132,7 +132,7 @@ export class User extends BaseClass {
@Column() @Column()
public_flags: number; public_flags: number;
@Column() @Column({ type: "bigint" })
rights: string; // Rights rights: string; // Rights
@OneToMany(() => Session, (session: Session) => session.user) @OneToMany(() => Session, (session: Session) => session.user)
@ -164,6 +164,9 @@ export class User extends BaseClass {
@Column({ type: "simple-json", select: false }) @Column({ type: "simple-json", select: false })
settings: UserSettings; settings: UserSettings;
@Column({ type: "simple-json" })
notes: { [key: string]: string }; //key is ID of user
toPublicUser() { toPublicUser() {
const user: any = {}; const user: any = {};
PublicUserProjection.forEach((x) => { PublicUserProjection.forEach((x) => {
@ -271,6 +274,7 @@ export class User extends BaseClass {
}, },
settings: { ...defaultSettings, locale: language }, settings: { ...defaultSettings, locale: language },
fingerprints: [], fingerprints: [],
notes: {},
}); });
await user.save(); await user.save();
@ -360,7 +364,7 @@ export interface UserSettings {
render_reactions: boolean; render_reactions: boolean;
restricted_guilds: string[]; restricted_guilds: string[];
show_current_game: boolean; show_current_game: boolean;
status: "online" | "offline" | "dnd" | "idle"; status: "online" | "offline" | "dnd" | "idle" | "invisible";
stream_notifications_enabled: boolean; stream_notifications_enabled: boolean;
theme: "dark" | "white"; // dark theme: "dark" | "white"; // dark
timezone_offset: number; // e.g -60 timezone_offset: number; // e.g -60

View File

@ -27,4 +27,4 @@ export * from "./Template";
export * from "./User"; export * from "./User";
export * from "./VoiceState"; export * from "./VoiceState";
export * from "./Webhook"; export * from "./Webhook";
export * from "./ClientRelase"; export * from "./ClientRelease";

View File

@ -623,6 +623,7 @@ export type EVENT =
| "PRESENCE_UPDATE" | "PRESENCE_UPDATE"
| "TYPING_START" | "TYPING_START"
| "USER_UPDATE" | "USER_UPDATE"
| "USER_NOTE_UPDATE"
| "WEBHOOKS_UPDATE" | "WEBHOOKS_UPDATE"
| "INTERACTION_CREATE" | "INTERACTION_CREATE"
| "VOICE_STATE_UPDATE" | "VOICE_STATE_UPDATE"

View File

@ -12,11 +12,13 @@ export interface Interaction {
} }
export enum InteractionType { export enum InteractionType {
SelfCommand = 0,
Ping = 1, Ping = 1,
ApplicationCommand = 2, ApplicationCommand = 2,
} }
export enum InteractionResponseType { export enum InteractionResponseType {
SelfCommandResponse = 0,
Pong = 1, Pong = 1,
Acknowledge = 2, Acknowledge = 2,
ChannelMessage = 3, ChannelMessage = 3,

View File

@ -1,4 +1,4 @@
export type Status = "idle" | "dnd" | "online" | "offline"; export type Status = "idle" | "dnd" | "online" | "offline" | "invisible";
export interface ClientStatus { export interface ClientStatus {
desktop?: string; // e.g. Windows/Linux/Mac desktop?: string; // e.g. Windows/Linux/Mac

View File

@ -0,0 +1,16 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class ReleaseTypo1648643945733 implements MigrationInterface {
name = "ReleaseTypo1648643945733";
public async up(queryRunner: QueryRunner): Promise<void> {
//drop table first because typeorm creates it before migrations run
await queryRunner.dropTable("client_release", true);
await queryRunner.renameTable("client_relase", "client_release");
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropTable("client_relase", true);
await queryRunner.renameTable("client_release", "client_relase");
}
}

View File

@ -1,6 +1,7 @@
import { BitField } from "./BitField"; import { BitField } from "./BitField";
import "missing-native-js-functions"; import "missing-native-js-functions";
import { BitFieldResolvable, BitFlag } from "./BitField"; import { BitFieldResolvable, BitFlag } from "./BitField";
import { User } from "../entities";
var HTTPError: any; var HTTPError: any;
@ -65,6 +66,8 @@ export class Rights extends BitField {
// inverts the presence confidentiality default (OPERATOR's presence is not routed by default, others' are) for a given user // inverts the presence confidentiality default (OPERATOR's presence is not routed by default, others' are) for a given user
SELF_ADD_DISCOVERABLE: BitFlag(36), // can mark discoverable guilds that they have permissions to mark as discoverable SELF_ADD_DISCOVERABLE: BitFlag(36), // can mark discoverable guilds that they have permissions to mark as discoverable
MANAGE_GUILD_DIRECTORY: BitFlag(37), // can change anything in the primary guild directory MANAGE_GUILD_DIRECTORY: BitFlag(37), // can change anything in the primary guild directory
POGGERS: BitFlag(38), // can send confetti, screenshake, random user mention (@someone)
USE_ACHIEVEMENTS: BitFlag(39), // can use achievements and cheers
INITIATE_INTERACTIONS: BitFlag(40), // can initiate interactions INITIATE_INTERACTIONS: BitFlag(40), // can initiate interactions
RESPOND_TO_INTERACTIONS: BitFlag(41), // can respond to interactions RESPOND_TO_INTERACTIONS: BitFlag(41), // can respond to interactions
SEND_BACKDATED_EVENTS: BitFlag(42), // can send backdated events SEND_BACKDATED_EVENTS: BitFlag(42), // can send backdated events
@ -83,6 +86,15 @@ export class Rights extends BitField {
// @ts-ignore // @ts-ignore
throw new HTTPError(`You are missing the following rights ${permission}`, 403); throw new HTTPError(`You are missing the following rights ${permission}`, 403);
} }
} }
const ALL_RIGHTS = Object.values(Rights.FLAGS).reduce((total, val) => total | val, BigInt(0)); const ALL_RIGHTS = Object.values(Rights.FLAGS).reduce((total, val) => total | val, BigInt(0));
export async function getRights( user_id: string
/**, opts: {
in_behalf?: (keyof User)[];
} = {} **/) {
let user = await User.findOneOrFail({ where: { id: user_id } });
return new Rights(user.rights);
}

View File

@ -1,6 +1,9 @@
import { Server, traverseDirectory } from "lambert-server"; import { Server, traverseDirectory } from "lambert-server";
const DEFAULT_FILTER = /^([^\.].*)(?<!\.d)\.(js)$/; //if we're using ts-node, use ts files instead of js
const extension = Symbol.for("ts-node.register.instance") in process ? "ts" : "js"
const DEFAULT_FILTER = new RegExp("^([^\.].*)(?<!\.d)\.(" + extension + ")$");
export function registerRoutes(server: Server, root: string) { export function registerRoutes(server: Server, root: string) {
return traverseDirectory( return traverseDirectory(