mirror of
https://github.com/spacebarchat/server.git
synced 2026-04-17 21:55:39 +00:00
Bunch of fixes and improvements, everything appears to work now
This commit is contained in:
@@ -5,6 +5,7 @@ import fetch, { Response as FetchResponse, Headers } from "node-fetch";
|
||||
import ProxyAgent from 'proxy-agent';
|
||||
import { Config } from "@fosscord/util";
|
||||
import { AssetCacheItem } from "../util/entities/AssetCacheItem"
|
||||
import { green } from "picocolors";
|
||||
|
||||
export default function TestClient(app: Application) {
|
||||
const agent = new ProxyAgent();
|
||||
@@ -18,11 +19,16 @@ export default function TestClient(app: Application) {
|
||||
|
||||
//load asset cache
|
||||
let newAssetCache: Map<string, AssetCacheItem> = new Map<string, AssetCacheItem>();
|
||||
if(!fs.existsSync(path.join(__dirname, "..", "..", "assets", "cache"))) {
|
||||
fs.mkdirSync(path.join(__dirname, "..", "..", "assets", "cache"));
|
||||
let assetCacheDir = path.join(__dirname, "..", "..", "assets", "cache");
|
||||
if(process.env.ASSET_CACHE_DIR)
|
||||
assetCacheDir = process.env.ASSET_CACHE_DIR
|
||||
|
||||
console.log(`[TestClient] ${green(`Using asset cache path: ${assetCacheDir}`)}`)
|
||||
if(!fs.existsSync(assetCacheDir)) {
|
||||
fs.mkdirSync(assetCacheDir);
|
||||
}
|
||||
if(fs.existsSync(path.join(__dirname, "..", "..", "assets", "cache", "index.json"))) {
|
||||
let rawdata = fs.readFileSync(path.join(__dirname, "..", "..", "assets", "cache", "index.json"));
|
||||
if(fs.existsSync(path.join(assetCacheDir, "index.json"))) {
|
||||
let rawdata = fs.readFileSync(path.join(assetCacheDir, "index.json"));
|
||||
newAssetCache = new Map<string, AssetCacheItem>(Object.entries(JSON.parse(rawdata.toString())));
|
||||
}
|
||||
|
||||
@@ -39,6 +45,7 @@ export default function TestClient(app: Application) {
|
||||
});
|
||||
}
|
||||
else {
|
||||
console.log(`[TestClient] Downloading file not yet cached! Asset file: ${req.params.file}`);
|
||||
response = await fetch(`https://discord.com/assets/${req.params.file}`, {
|
||||
agent,
|
||||
// @ts-ignore
|
||||
@@ -49,11 +56,11 @@ export default function TestClient(app: Application) {
|
||||
|
||||
//set cache info
|
||||
assetCacheItem.Headers = Object.fromEntries(stripHeaders(response.headers));
|
||||
assetCacheItem.FilePath = path.join(__dirname, "..", "..", "assets", "cache", req.params.file);
|
||||
assetCacheItem.FilePath = path.join(assetCacheDir, req.params.file);
|
||||
assetCacheItem.Key = req.params.file;
|
||||
//add to cache and save
|
||||
newAssetCache.set(req.params.file, assetCacheItem);
|
||||
fs.writeFileSync(path.join(__dirname, "..", "..", "assets", "cache", "index.json"), JSON.stringify(Object.fromEntries(newAssetCache), null, 4));
|
||||
fs.writeFileSync(path.join(assetCacheDir, "index.json"), JSON.stringify(Object.fromEntries(newAssetCache), null, 4));
|
||||
//download file
|
||||
fs.writeFileSync(assetCacheItem.FilePath, await response.buffer());
|
||||
}
|
||||
|
||||
@@ -77,8 +77,8 @@ router.patch("/", route({ body: "ChannelModifySchema", permission: "MANAGE_CHANN
|
||||
const { channel_id } = req.params;
|
||||
if (payload.icon) payload.icon = await handleFile(`/channel-icons/${channel_id}`, payload.icon);
|
||||
|
||||
const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
|
||||
channel.assign(payload);
|
||||
let channel = await Channel.findOneOrFail({ where: { id: channel_id } });
|
||||
channel = Object.assign(channel, payload);
|
||||
|
||||
await Promise.all([
|
||||
channel.save(),
|
||||
|
||||
@@ -33,7 +33,7 @@ router.post("/", route({ body: "InviteCreateSchema", permission: "CREATE_INSTANT
|
||||
|
||||
const expires_at = new Date(req.body.max_age * 1000 + Date.now());
|
||||
|
||||
const invite = await new Invite({
|
||||
const invite = await Object.assign(new Invite(),{
|
||||
code: random(),
|
||||
temporary: req.body.temporary,
|
||||
uses: 0,
|
||||
|
||||
@@ -20,7 +20,7 @@ router.post("/", route({ body: "MessageAcknowledgeSchema" }), async (req: Reques
|
||||
permission.hasThrow("VIEW_CHANNEL");
|
||||
|
||||
let read_state = await ReadState.findOne({ where: { user_id: req.user_id, channel_id } });
|
||||
if (!read_state) read_state = new ReadState({ where: { user_id: req.user_id, channel_id } });
|
||||
if (!read_state) read_state = Object.assign(new ReadState(), { user_id: req.user_id, channel_id });
|
||||
read_state.last_message_id = message_id;
|
||||
|
||||
await read_state.save();
|
||||
|
||||
@@ -28,7 +28,7 @@ router.put("/:user_id", route({}), async (req: Request, res: Response) => {
|
||||
throw DiscordApiErrors.INVALID_RECIPIENT; //TODO is this the right error?
|
||||
}
|
||||
|
||||
channel.recipients!.push(new Recipient({ channel_id, user_id: user_id }));
|
||||
channel.recipients!.push(Object.assign(new Recipient(), { channel_id, user_id: user_id }));
|
||||
await channel.save();
|
||||
|
||||
await emitEvent({
|
||||
|
||||
@@ -90,7 +90,7 @@ router.put("/:user_id", route({ body: "BanCreateSchema", permission: "BAN_MEMBER
|
||||
|
||||
const banned_user = await User.getPublicUser(banned_user_id);
|
||||
|
||||
const ban = new Ban({
|
||||
const ban = Object.assign(new Ban(),{
|
||||
user_id: banned_user_id,
|
||||
guild_id: guild_id,
|
||||
ip: getIpAdress(req),
|
||||
@@ -122,7 +122,7 @@ router.put("/@me", route({ body: "BanCreateSchema"}), async (req: Request, res:
|
||||
if (req.permission!.cache.guild?.owner_id === req.params.user_id)
|
||||
throw new HTTPError("You are the guild owner, hence can't ban yourself", 403);
|
||||
|
||||
const ban = new Ban({
|
||||
const ban = Object.assign(new Ban(), {
|
||||
user_id: req.params.user_id,
|
||||
guild_id: guild_id,
|
||||
ip: getIpAdress(req),
|
||||
|
||||
@@ -50,7 +50,7 @@ router.post("/", route({ body: "EmojiCreateSchema", permission: "MANAGE_EMOJIS_A
|
||||
const user = await User.findOneOrFail({ where: { id: req.user_id } });
|
||||
body.image = (await handleFile(`/emojis/${id}`, body.image)) as string;
|
||||
|
||||
const emoji = await new Emoji({
|
||||
const emoji = await Object.assign(new Emoji(), {
|
||||
id: id,
|
||||
guild_id: guild_id,
|
||||
...body,
|
||||
@@ -80,7 +80,7 @@ router.patch(
|
||||
const { emoji_id, guild_id } = req.params;
|
||||
const body = req.body as EmojiModifySchema;
|
||||
|
||||
const emoji = await new Emoji({ ...body, id: emoji_id, guild_id: guild_id }).save();
|
||||
const emoji = await Object.assign(new Emoji(), { ...body, id: emoji_id, guild_id: guild_id }).save();
|
||||
|
||||
await emitEvent({
|
||||
event: "GUILD_EMOJIS_UPDATE",
|
||||
|
||||
@@ -59,7 +59,7 @@ router.patch("/", route({ body: "GuildUpdateSchema"}), async (req: Request, res:
|
||||
relations: ["emojis", "roles", "stickers"]
|
||||
});
|
||||
// TODO: check if body ids are valid
|
||||
guild.assign(body);
|
||||
guild = Object.assign(guild, body);
|
||||
|
||||
//TODO: check this, removed toJSON call
|
||||
const data = JSON.parse(JSON.stringify(guild));
|
||||
|
||||
@@ -31,7 +31,7 @@ router.patch("/", route({ body: "MemberChangeSchema" }), async (req: Request, re
|
||||
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) => Object.assign(new Role(), { id: x })); // foreign key constraint will fail if role doesn't exist
|
||||
}
|
||||
|
||||
await member.save();
|
||||
|
||||
@@ -43,7 +43,7 @@ router.patch("/", route({ body: "RoleModifySchema", permission: "MANAGE_ROLES" }
|
||||
|
||||
if (body.icon) body.icon = await handleFile(`/role-icons/${role_id}`, body.icon as string);
|
||||
|
||||
const role = new Role({
|
||||
const role = Object.assign(new Role(), {
|
||||
...body,
|
||||
id: role_id,
|
||||
guild_id,
|
||||
|
||||
@@ -51,7 +51,7 @@ router.post("/", route({ body: "RoleModifySchema", permission: "MANAGE_ROLES" })
|
||||
|
||||
if (role_count > maxRoles) throw DiscordApiErrors.MAXIMUM_ROLES.withParams(maxRoles);
|
||||
|
||||
const role = new Role({
|
||||
let role: Role = Object.assign(new Role(),{
|
||||
// values before ...body are default and can be overriden
|
||||
position: 0,
|
||||
hoist: false,
|
||||
|
||||
@@ -43,7 +43,7 @@ router.post(
|
||||
const id = Snowflake.generate();
|
||||
|
||||
const [sticker] = await Promise.all([
|
||||
new Sticker({
|
||||
Object.assign(new Sticker(), {
|
||||
...body,
|
||||
guild_id,
|
||||
id,
|
||||
@@ -105,7 +105,7 @@ router.patch(
|
||||
const { guild_id, sticker_id } = req.params;
|
||||
const body = req.body as ModifyGuildStickerSchema;
|
||||
|
||||
const sticker = await new Sticker({ ...body, guild_id, id: sticker_id }).save();
|
||||
const sticker = await Object.assign(new Sticker(), { ...body, guild_id, id: sticker_id }).save();
|
||||
await sendStickerUpdateEvent(guild_id);
|
||||
|
||||
return res.json(sticker);
|
||||
|
||||
@@ -47,7 +47,7 @@ router.post("/", route({ body: "TemplateCreateSchema", permission: "MANAGE_GUILD
|
||||
const exists = await Template.findOneOrFail({ where: { id: guild_id } }).catch((e) => {});
|
||||
if (exists) throw new HTTPError("Template already exists", 400);
|
||||
|
||||
const template = await new Template({
|
||||
const template = await Object.assign(new Template(), {
|
||||
...req.body,
|
||||
code: generateCode(),
|
||||
creator_id: req.user_id,
|
||||
@@ -75,7 +75,7 @@ router.put("/:code", route({ permission: "MANAGE_GUILD" }), async (req: Request,
|
||||
const { code, guild_id } = req.params;
|
||||
const guild = await Guild.findOneOrFail({ where: { id: guild_id }, select: TemplateGuildProjection });
|
||||
|
||||
const template = await new Template({ code, serialized_source_guild: guild }).save();
|
||||
const template = await Object.assign(new Template(), { code, serialized_source_guild: guild }).save();
|
||||
|
||||
res.json(template);
|
||||
});
|
||||
@@ -84,7 +84,7 @@ router.patch("/:code", route({ body: "TemplateModifySchema", permission: "MANAGE
|
||||
const { code, guild_id } = req.params;
|
||||
const { name, description } = req.body;
|
||||
|
||||
const template = await new Template({ code, name: name, description: description, source_guild_id: guild_id }).save();
|
||||
const template = await Object.assign(new Template(), { code, name: name, description: description, source_guild_id: guild_id }).save();
|
||||
|
||||
res.json(template);
|
||||
});
|
||||
|
||||
@@ -47,7 +47,7 @@ router.patch("/", route({ body: "VanityUrlSchema", permission: "MANAGE_GUILD" })
|
||||
|
||||
const { id } = await Channel.findOneOrFail({ where: { guild_id, type: ChannelType.GUILD_TEXT } });
|
||||
|
||||
await new Invite({
|
||||
await Object.assign(new Invite(), {
|
||||
vanity_url: true,
|
||||
code: code,
|
||||
temporary: false,
|
||||
|
||||
@@ -33,7 +33,7 @@ router.patch("/", route({ body: "VoiceStateUpdateSchema" }), async (req: Request
|
||||
if (!body.suppress) body.request_to_speak_timestamp = new Date();
|
||||
if (body.request_to_speak_timestamp) perms.hasThrow("REQUEST_TO_SPEAK");
|
||||
|
||||
const voice_state = await VoiceState.findOne({
|
||||
let voice_state = await VoiceState.findOne({
|
||||
where: {
|
||||
guild_id,
|
||||
channel_id: body.channel_id,
|
||||
@@ -42,7 +42,7 @@ router.patch("/", route({ body: "VoiceStateUpdateSchema" }), async (req: Request
|
||||
});
|
||||
if (!voice_state) throw DiscordApiErrors.UNKNOWN_VOICE_STATE;
|
||||
|
||||
voice_state.assign(body);
|
||||
voice_state = Object.assign(voice_state, body);
|
||||
const channel = await Channel.findOneOrFail({ where: { guild_id, id: body.channel_id } });
|
||||
if (channel.type !== ChannelType.GUILD_STAGE_VOICE) {
|
||||
throw DiscordApiErrors.CANNOT_EXECUTE_ON_THIS_CHANNEL_TYPE;
|
||||
|
||||
@@ -57,13 +57,13 @@ router.post("/:code", route({ body: "GuildTemplateCreateSchema" }), async (req:
|
||||
const guild_id = Snowflake.generate();
|
||||
|
||||
const [guild, role] = await Promise.all([
|
||||
new Guild({
|
||||
Object.assign(new Guild(), {
|
||||
...body,
|
||||
...template.serialized_source_guild,
|
||||
id: guild_id,
|
||||
owner_id: req.user_id
|
||||
}).save(),
|
||||
new Role({
|
||||
(Object.assign(new Role(), {
|
||||
id: guild_id,
|
||||
guild_id: guild_id,
|
||||
color: 0,
|
||||
@@ -74,7 +74,7 @@ router.post("/:code", route({ body: "GuildTemplateCreateSchema" }), async (req:
|
||||
permissions: BigInt("2251804225"),
|
||||
position: 0,
|
||||
tags: null
|
||||
}).save()
|
||||
}) as Role).save()
|
||||
]);
|
||||
|
||||
await Member.addToGuild(req.user_id, guild_id);
|
||||
|
||||
@@ -32,9 +32,7 @@ router.patch("/", route({ body: "UserModifySchema" }), async (req: Request, res:
|
||||
const body = req.body as UserModifySchema;
|
||||
|
||||
if (body.avatar) body.avatar = await handleFile(`/avatars/${req.user_id}`, body.avatar as string);
|
||||
if (body.banner) body.banner = await handleFile(`/banners/${req.user_id}`, body.banner as string);
|
||||
|
||||
const user = await User.findOneOrFail({ where: { id: req.user_id }, select: [...PrivateUserProjection, "data"] });
|
||||
if (body.banner) body.banner = await handleFile(`/banners/${req.user_id}`, body.banner as string);let user = await User.findOneOrFail({ where: { id: req.user_id }, select: [...PrivateUserProjection, "data"] });
|
||||
|
||||
if (body.password) {
|
||||
if (user.data?.hash) {
|
||||
@@ -65,7 +63,7 @@ router.patch("/", route({ body: "UserModifySchema" }), async (req: Request, res:
|
||||
}
|
||||
}
|
||||
|
||||
user.assign(body);
|
||||
user = Object.assign(user, body);
|
||||
await user.save();
|
||||
|
||||
// @ts-ignore
|
||||
|
||||
@@ -140,7 +140,7 @@ async function updateRelationship(req: Request, res: Response, friend: User, typ
|
||||
relationship.type = RelationshipType.blocked;
|
||||
await relationship.save();
|
||||
} else {
|
||||
relationship = await new Relationship({ to_id: id, type: RelationshipType.blocked, from_id: req.user_id }).save();
|
||||
relationship = await Object.assign(new Relationship(), { to_id: id, type: RelationshipType.blocked, from_id: req.user_id }).save();
|
||||
}
|
||||
|
||||
if (friendRequest && friendRequest.type !== RelationshipType.blocked) {
|
||||
@@ -166,8 +166,8 @@ async function updateRelationship(req: Request, res: Response, friend: User, typ
|
||||
const { maxFriends } = Config.get().limits.user;
|
||||
if (user.relationships.length >= maxFriends) throw DiscordApiErrors.MAXIMUM_FRIENDS.withParams(maxFriends);
|
||||
|
||||
let incoming_relationship = new Relationship({ nickname: undefined, type: RelationshipType.incoming, to: user, from: friend });
|
||||
let outgoing_relationship = new Relationship({
|
||||
let incoming_relationship = Object.assign(new Relationship(), { nickname: undefined, type: RelationshipType.incoming, to: user, from: friend });
|
||||
let outgoing_relationship = Object.assign(new Relationship(), {
|
||||
nickname: undefined,
|
||||
type: RelationshipType.outgoing,
|
||||
to: friend,
|
||||
@@ -178,7 +178,7 @@ async function updateRelationship(req: Request, res: Response, friend: User, typ
|
||||
if (friendRequest.type === RelationshipType.blocked) throw new HTTPError("The user blocked you");
|
||||
if (friendRequest.type === RelationshipType.friends) throw new HTTPError("You are already friends with the user");
|
||||
// accept friend request
|
||||
incoming_relationship = friendRequest;
|
||||
incoming_relationship = friendRequest as any; //TODO: checkme, any cast
|
||||
incoming_relationship.type = RelationshipType.friends;
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ async function updateRelationship(req: Request, res: Response, friend: User, typ
|
||||
if (relationship.type === RelationshipType.outgoing) throw new HTTPError("You already sent a friend request");
|
||||
if (relationship.type === RelationshipType.blocked) throw new HTTPError("Unblock the user before sending a friend request");
|
||||
if (relationship.type === RelationshipType.friends) throw new HTTPError("You are already friends with the user");
|
||||
outgoing_relationship = relationship;
|
||||
outgoing_relationship = relationship as any; //TODO: checkme, any cast
|
||||
outgoing_relationship.type = RelationshipType.friends;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
const channel = await Channel.findOneOrFail({ where: { id: opts.channel_id }, relations: ["recipients"] });
|
||||
if (!channel || !opts.channel_id) throw new HTTPError("Channel not found", 404);
|
||||
|
||||
const message = new Message({
|
||||
const message = Object.assign(new Message(), {
|
||||
...opts,
|
||||
sticker_items: opts.sticker_ids?.map((x) => ({ id: x })),
|
||||
guild_id: channel.guild_id,
|
||||
@@ -132,9 +132,9 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
}
|
||||
}
|
||||
|
||||
message.mention_channels = mention_channel_ids.map((x) => new Channel({ id: x }));
|
||||
message.mention_roles = mention_role_ids.map((x) => new Role({ id: x }));
|
||||
message.mentions = mention_user_ids.map((x) => new User({ id: x }));
|
||||
message.mention_channels = mention_channel_ids.map((x) => Object.assign(new Channel(), { id: x }));
|
||||
message.mention_roles = mention_role_ids.map((x) => Object.assign(new Role(), { id: x }));
|
||||
message.mentions = mention_user_ids.map((x) => Object.assign(new User(), { id: x }));
|
||||
message.mention_everyone = mention_everyone;
|
||||
|
||||
// TODO: check and put it all in the body
|
||||
|
||||
BIN
bundle/package-lock.json
generated
BIN
bundle/package-lock.json
generated
Binary file not shown.
@@ -9,8 +9,10 @@
|
||||
"depcheck": "node scripts/depcheck.js",
|
||||
"syncdeps": "node scripts/install.js",
|
||||
"build": "node scripts/build.js",
|
||||
"start": "node scripts/build.js && node dist/bundle/src/start.js",
|
||||
"start:bundle": "node dist/bundle/src/start.js",
|
||||
"start": "node scripts/build.js && node --enable-source-maps dist/bundle/src/start.js",
|
||||
"start:bundle": "node --enable-source-maps dist/bundle/src/start.js",
|
||||
"start:bundle:dbg": "node --enable-source-maps --inspect dist/bundle/src/start.js",
|
||||
"start:bundle:vscode-dbg": "npm run build clean logerrors pretty-errors && node --enable-source-maps --inspect dist/bundle/src/start.js",
|
||||
"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",
|
||||
"tsnode": "npx ts-node --transpile-only -P tsnode.tsconfig.json src/start.ts"
|
||||
@@ -27,28 +29,73 @@
|
||||
},
|
||||
"homepage": "https://fosscord.com",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.18.9",
|
||||
"@babel/preset-env": "^7.18.9",
|
||||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@types/amqplib": "^0.8.1",
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/body-parser": "^1.19.0",
|
||||
"@types/dotenv": "^8.2.0",
|
||||
"@types/express": "^4.17.12",
|
||||
"@types/i18next-node-fs-backend": "^2.1.0",
|
||||
"@types/jsonwebtoken": "^8.5.8",
|
||||
"@types/morgan": "^1.9.3",
|
||||
"@types/multer": "^1.4.7",
|
||||
"@types/node": "^18.0.6",
|
||||
"@types/node-os-utils": "^1.3.0",
|
||||
"@types/ws": "^8.5.3",
|
||||
"jest": "^28.1.3",
|
||||
"jest-expect-message": "^1.0.2",
|
||||
"ts-node": "^10.2.1",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
"ts-patch": "^2.0.1",
|
||||
"typescript": "^4.2.3"
|
||||
"typescript": "^4.2.3",
|
||||
"typescript-json-schema": "^0.54.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.137.0",
|
||||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@fosscord/api": "file:../api",
|
||||
"@fosscord/cdn": "file:../cdn",
|
||||
"@fosscord/gateway": "file:../gateway",
|
||||
"@ovos-media/ts-transform-paths": "^1.7.18-1",
|
||||
"@sentry/node": "^7.7.0",
|
||||
"@sentry/tracing": "^7.7.0",
|
||||
"@types/node-fetch": "^2.6.2",
|
||||
"ajv": "8.6.2",
|
||||
"ajv-formats": "^2.1.1",
|
||||
"amqplib": "^0.10.1",
|
||||
"bcrypt": "^5.0.1",
|
||||
"body-parser": "^1.19.0",
|
||||
"canvas": "^2.9.3",
|
||||
"cheerio": "^1.0.0-rc.10",
|
||||
"dotenv": "^16.0.1",
|
||||
"exif-be-gone": "^1.3.0",
|
||||
"express": "^4.17.1",
|
||||
"file-type": "16.5",
|
||||
"form-data": "^4.0.0",
|
||||
"i18next": "^21.8.14",
|
||||
"i18next-http-middleware": "^3.1.3",
|
||||
"i18next-node-fs-backend": "^2.1.3",
|
||||
"image-size": "^1.0.0",
|
||||
"jest": "^28.1.3",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"lambert-server": "^1.2.12",
|
||||
"missing-native-js-functions": "^1.2.18",
|
||||
"morgan": "^1.10.0",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"mysql2": "^2.3.3",
|
||||
"node-2fa": "^2.0.3",
|
||||
"node-fetch": "^2.6.7",
|
||||
"node-os-utils": "^1.3.7",
|
||||
"patch-package": "^6.4.7",
|
||||
"pg": "^8.7.3",
|
||||
"picocolors": "^1.0.0",
|
||||
"proxy-agent": "^5.0.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"sqlite3": "^5.0.11",
|
||||
"supertest": "^6.1.6",
|
||||
"typeorm": "^0.3.7",
|
||||
"typescript": "^4.1.2",
|
||||
"ws": "^8.8.1"
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"declaration": true /* Generates corresponding '.d.ts' file. */,
|
||||
"declarationMap": false /* Generates a sourcemap for each corresponding '.d.ts' file. */,
|
||||
"inlineSourceMap": true /* Emit a single file with source maps instead of having a separate file. */,
|
||||
// "sourceMap": true /* Generates corresponding '.map' file. */,
|
||||
"sourceMap": true /* Generates corresponding '.map' file. */,
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
"outDir": "./dist/" /* Redirect output structure to the directory. */,
|
||||
"rootDir": "./src/" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,
|
||||
|
||||
@@ -27,6 +27,21 @@ export async function Connection(
|
||||
socket.on("close", Close);
|
||||
// @ts-ignore
|
||||
socket.on("message", Message);
|
||||
|
||||
if(process.env.WS_LOGEVENTS)
|
||||
[
|
||||
"close",
|
||||
"error",
|
||||
"upgrade",
|
||||
//"message",
|
||||
"open",
|
||||
"ping",
|
||||
"pong",
|
||||
"unexpected-response"
|
||||
].forEach(x=>{
|
||||
socket.on(x, y => console.log(x, y));
|
||||
});
|
||||
|
||||
console.log(`[Gateway] Connections: ${this.clients.size}`);
|
||||
|
||||
const { searchParams } = new URL(`http://localhost${request.url}`);
|
||||
|
||||
@@ -15,17 +15,33 @@ const PayloadSchema = {
|
||||
$t: String,
|
||||
};
|
||||
|
||||
export async function Message(this: WebSocket, buffer: WS.Data) {
|
||||
export async function Message(this: WebSocket, buffer: WS.RawData) {
|
||||
// TODO: compression
|
||||
let data: Payload;
|
||||
|
||||
if (this.encoding === "etf" && buffer instanceof Buffer)
|
||||
data = erlpack.unpack(buffer);
|
||||
else if (this.encoding === "json" && typeof buffer === "string")
|
||||
data = JSON.parse(buffer);
|
||||
else return;
|
||||
else if (this.encoding === "json")
|
||||
data = JSON.parse(buffer as unknown as string); //TODO: is this even correct?? seems to work for web clients...
|
||||
else if(/--debug|--inspect/.test(process.execArgv.join(' '))) {
|
||||
debugger;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
console.log("Invalid gateway connection! Use a debugger to inspect!");
|
||||
return;
|
||||
}
|
||||
|
||||
check.call(this, PayloadSchema, data);
|
||||
if(process.env.WS_VERBOSE)
|
||||
console.log(`[Websocket] Incomming message: ${JSON.stringify(data)}`);
|
||||
if(data.op !== 1)
|
||||
check.call(this, PayloadSchema, data);
|
||||
else { //custom validation for numbers, because heartbeat
|
||||
if(data.s || data.t || typeof data.d !== "number") {
|
||||
console.log("Invalid heartbeat...");
|
||||
this.close(CLOSECODES.Decode_error);
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const OPCodeHandler = OPCodeHandlers[data.op];
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Payload, WebSocket } from "@fosscord/gateway";
|
||||
import { setHeartbeat } from "../util/Heartbeat";
|
||||
import { Send } from "../util/Send";
|
||||
|
||||
export async function onHeartbeat(this: WebSocket, data: Payload) {
|
||||
export async function onHeartbeat(this: WebSocket, _data: Payload) {
|
||||
// TODO: validate payload
|
||||
|
||||
setHeartbeat(this);
|
||||
|
||||
@@ -83,7 +83,7 @@ export async function onIdentify(this: WebSocket, data: Payload) {
|
||||
// TODO: public user selection
|
||||
}),
|
||||
// save the session and delete it when the websocket is closed
|
||||
new Session({
|
||||
Object.assign(new Session(),{
|
||||
user_id: this.user_id,
|
||||
session_id: session_id,
|
||||
// TODO: check if status is only one of: online, dnd, offline, idle
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getPermission, listenEvent, Member, Role } from "@fosscord/util";
|
||||
import { getPermission, listenEvent, Member, Role, initDatabase } from "@fosscord/util";
|
||||
import { LazyRequest } from "../schema/LazyRequest";
|
||||
import { Send } from "../util/Send";
|
||||
import { OPCODES } from "../util/Constants";
|
||||
@@ -15,8 +15,9 @@ async function getMembers(guild_id: string, range: [number, number]) {
|
||||
throw new Error("range is not a valid array");
|
||||
}
|
||||
// TODO: wait for typeorm to implement ordering for .find queries https://github.com/typeorm/typeorm/issues/2620
|
||||
// TODO: rewrite this, released in 0.3.0
|
||||
|
||||
let members = await getRepository(Member)
|
||||
let members = await (await initDatabase()).getRepository(Member)
|
||||
.createQueryBuilder("member")
|
||||
.where("member.guild_id = :guild_id", { guild_id })
|
||||
.leftJoinAndSelect("member.roles", "role")
|
||||
|
||||
@@ -47,9 +47,9 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) {
|
||||
|
||||
//The event send by Discord's client on channel leave has both guild_id and channel_id as null
|
||||
if (body.guild_id === null) body.guild_id = voiceState.guild_id;
|
||||
voiceState.assign(body);
|
||||
voiceState = Object.assign(voiceState, body);
|
||||
} catch (error) {
|
||||
voiceState = new VoiceState({
|
||||
voiceState = Object.assign(new VoiceState(), {
|
||||
...body,
|
||||
user_id: this.user_id,
|
||||
deaf: false,
|
||||
|
||||
@@ -7,6 +7,8 @@ try {
|
||||
import { Payload, WebSocket } from "@fosscord/gateway";
|
||||
|
||||
export async function Send(socket: WebSocket, data: Payload) {
|
||||
if(process.env.WS_VERBOSE)
|
||||
console.log(`[Websocket] Outgoing message: ${JSON.stringify(data)}`);
|
||||
let buffer: Buffer | string;
|
||||
if (socket.encoding === "etf") buffer = erlpack.pack(data);
|
||||
// TODO: encode circular object
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||
"declaration": true /* Generates corresponding '.d.ts' file. */,
|
||||
"declarationMap": false /* Generates a sourcemap for each corresponding '.d.ts' file. */,
|
||||
// "sourceMap": true /* Generates corresponding '.map' file. */,
|
||||
"sourceMap": true /* Generates corresponding '.map' file. */,
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
"outDir": "./dist/" /* Redirect output structure to the directory. */,
|
||||
"rootDir": "./src/" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import "reflect-metadata";
|
||||
import { BaseEntity, EntityMetadata, ObjectIdColumn, PrimaryColumn, FindOptionsWhere } from "typeorm";
|
||||
import { BaseEntity, EntityMetadata, ObjectIdColumn, PrimaryColumn, FindOptionsWhere, Generated, SaveOptions } from "typeorm";
|
||||
import { Snowflake } from "../util/Snowflake";
|
||||
|
||||
export class BaseClassWithoutId extends BaseEntity {
|
||||
constructor(props?: any) {
|
||||
super();
|
||||
this.assign(props);
|
||||
if(props != undefined && props != null && Object.keys(props).length > 0)
|
||||
this.assign(props);
|
||||
}
|
||||
|
||||
assign(props: any = {}) {
|
||||
@@ -13,8 +14,11 @@ export class BaseClassWithoutId extends BaseEntity {
|
||||
delete props.opts;
|
||||
delete props.props;
|
||||
// will not include relational properties
|
||||
|
||||
for (const key in props) {
|
||||
console.warn("WARNING: BaseClass.assign called! This will probably fail!");
|
||||
console.warn(this)
|
||||
Object.assign(this,props);
|
||||
if(/--debug|--inspect/.test(process.execArgv.join(' '))) debugger;
|
||||
/*for (const key in props) {
|
||||
// @ts-ignore
|
||||
const setter = this[`set${key.capitalize()}`]; // use setter function if it exists
|
||||
|
||||
@@ -24,7 +28,7 @@ export class BaseClassWithoutId extends BaseEntity {
|
||||
// @ts-ignore
|
||||
this[key] = props[key];
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +39,13 @@ export class BaseClass extends BaseClassWithoutId {
|
||||
id: string;
|
||||
|
||||
assign(props: any = {}) {
|
||||
super.assign(props);
|
||||
super.assign(props);
|
||||
if (!this.id) this.id = Snowflake.generate();
|
||||
return this;
|
||||
}
|
||||
|
||||
save(options?: SaveOptions | undefined): Promise<this> {
|
||||
if (!this.id) this.id = Snowflake.generate();
|
||||
return super.save(options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ export class Channel extends BaseClass {
|
||||
};
|
||||
|
||||
await Promise.all([
|
||||
new Channel(channel).save(),
|
||||
Object.assign(new Channel(),channel).save(),
|
||||
!opts?.skipEventEmit
|
||||
? emitEvent({
|
||||
event: "CHANNEL_CREATE",
|
||||
@@ -263,7 +263,8 @@ export class Channel extends BaseClass {
|
||||
if (containsAll(re, channelRecipients)) {
|
||||
if (channel == null) {
|
||||
channel = ur.channel;
|
||||
await ur.assign({ closed: false }).save();
|
||||
ur = Object.assign(ur, { closed: false });
|
||||
await ur.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -272,7 +273,7 @@ export class Channel extends BaseClass {
|
||||
if (channel == null) {
|
||||
name = trimSpecial(name);
|
||||
|
||||
channel = await new Channel({
|
||||
channel = await (Object.assign(new Channel(), {
|
||||
name,
|
||||
type,
|
||||
owner_id: type === ChannelType.DM ? undefined : null, // 1:1 DMs are ownerless in fosscord-server
|
||||
@@ -280,9 +281,9 @@ export class Channel extends BaseClass {
|
||||
last_message_id: null,
|
||||
recipients: channelRecipients.map(
|
||||
(x) =>
|
||||
new Recipient({ user_id: x, closed: !(type === ChannelType.GROUP_DM || x === creator_user_id) })
|
||||
Object.assign(new Recipient(), { user_id: x, closed: !(type === ChannelType.GROUP_DM || x === creator_user_id) })
|
||||
),
|
||||
}).save();
|
||||
}) as Channel).save();
|
||||
}
|
||||
|
||||
const channel_dto = await DmChannelDTO.from(channel);
|
||||
|
||||
@@ -129,6 +129,7 @@ export interface ConfigValue {
|
||||
requireCaptcha: boolean;
|
||||
};
|
||||
register: {
|
||||
defaultRights: string;
|
||||
email: {
|
||||
required: boolean;
|
||||
allowlist: boolean;
|
||||
@@ -349,6 +350,7 @@ export const DefaultConfigOptions: ConfigValue = {
|
||||
minSymbols: 0,
|
||||
},
|
||||
incrementingDiscriminators: false,
|
||||
defaultRights: "0"
|
||||
},
|
||||
regions: {
|
||||
default: "fosscord",
|
||||
|
||||
@@ -285,7 +285,7 @@ export class Guild extends BaseClass {
|
||||
}) {
|
||||
const guild_id = Snowflake.generate();
|
||||
|
||||
const guild = await new Guild({
|
||||
const guild: Guild = Object.assign(new Guild(),{
|
||||
name: body.name || "Fosscord",
|
||||
icon: await handleFile(`/icons/${guild_id}`, body.icon as string),
|
||||
region: Config.get().regions.default,
|
||||
@@ -316,11 +316,12 @@ export class Guild extends BaseClass {
|
||||
welcome_channels: [],
|
||||
},
|
||||
widget_enabled: true, // NB: don't set it as false to prevent artificial restrictions
|
||||
}).save();
|
||||
});
|
||||
await guild.save();
|
||||
|
||||
// we have to create the role _after_ the guild because else we would get a "SQLITE_CONSTRAINT: FOREIGN KEY constraint failed" error
|
||||
// TODO: make the @everyone a pseudorole that is dynamically generated at runtime so we can save storage
|
||||
await new Role({
|
||||
let role: Role = Object.assign(new Role(), {
|
||||
id: guild_id,
|
||||
guild_id: guild_id,
|
||||
color: 0,
|
||||
@@ -333,7 +334,8 @@ export class Guild extends BaseClass {
|
||||
position: 0,
|
||||
icon: null,
|
||||
unicode_emoji: null
|
||||
}).save();
|
||||
});
|
||||
await role.save();
|
||||
|
||||
if (!body.channels || !body.channels.length) body.channels = [{ id: "01", type: 0, name: "general" }];
|
||||
|
||||
|
||||
@@ -85,8 +85,8 @@ export class Member extends BaseClassWithoutId {
|
||||
@Column()
|
||||
joined_at: Date;
|
||||
|
||||
@Column({ type: "bigint", nullable: true })
|
||||
premium_since?: number;
|
||||
@Column({ nullable: true })
|
||||
premium_since?: Date;
|
||||
|
||||
@Column()
|
||||
deaf: boolean;
|
||||
@@ -161,7 +161,7 @@ export class Member extends BaseClassWithoutId {
|
||||
}),
|
||||
Role.findOneOrFail({ where: { id: role_id, guild_id }, select: ["id"] }),
|
||||
]);
|
||||
member.roles.push(new Role({ id: role_id }));
|
||||
member.roles.push(Object.assign(new Role(), { id: role_id }));
|
||||
|
||||
await Promise.all([
|
||||
member.save(),
|
||||
@@ -264,9 +264,9 @@ export class Member extends BaseClassWithoutId {
|
||||
//TODO: check for bugs
|
||||
if(guild.member_count) guild.member_count++;
|
||||
await Promise.all([
|
||||
new Member({
|
||||
Object.assign(new Member(), {
|
||||
...member,
|
||||
roles: [new Role({ id: guild_id })],
|
||||
roles: [Object.assign(new Role(), { id: guild_id })],
|
||||
// read_state: {},
|
||||
settings: {
|
||||
channel_overrides: [],
|
||||
|
||||
@@ -255,7 +255,7 @@ export class User extends BaseClass {
|
||||
// if nsfw_allowed is null/undefined it'll require date_of_birth to set it to true/false
|
||||
const language = req.language === "en" ? "en-US" : req.language || "en-US";
|
||||
|
||||
const user = new User({
|
||||
const user = Object.assign(new User(), {
|
||||
created_at: new Date(),
|
||||
username: username,
|
||||
discriminator,
|
||||
@@ -275,7 +275,7 @@ export class User extends BaseClass {
|
||||
disabled: false,
|
||||
deleted: false,
|
||||
email: email,
|
||||
rights: "0", // TODO: grant rights correctly, as 0 actually stands for no rights at all
|
||||
rights: Config.get().register.defaultRights, // TODO: grant rights correctly, as 0 actually stands for no rights at all
|
||||
nsfw_allowed: true, // TODO: depending on age
|
||||
public_flags: "0",
|
||||
flags: "0", // TODO: generate
|
||||
@@ -289,6 +289,8 @@ export class User extends BaseClass {
|
||||
notes: {},
|
||||
});
|
||||
|
||||
console.log("new user")
|
||||
console.log(user);
|
||||
await user.save();
|
||||
|
||||
setImmediate(async () => {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class premiumSinceAsDate1659921859145 implements MigrationInterface {
|
||||
name = 'premiumSinceAsDate1659921859145'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`members\` DROP COLUMN \`premium_since\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`members\`
|
||||
ADD \`premium_since\` datetime NULL
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`members\` DROP COLUMN \`premium_since\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`members\`
|
||||
ADD \`premium_since\` bigint NULL
|
||||
`);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class premiumSinceAsDate1659921826567 implements MigrationInterface {
|
||||
name = 'premiumSinceAsDate1659921826567'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE "members" DROP COLUMN "premium_since"
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE "members"
|
||||
ADD "premium_since" TIMESTAMP
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE "members" DROP COLUMN "premium_since"
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE "members"
|
||||
ADD "premium_since" bigint
|
||||
`);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class premiumSinceAsDate1659921722863 implements MigrationInterface {
|
||||
name = 'premiumSinceAsDate1659921722863'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
DROP INDEX "IDX_bb2bf9386ac443afbbbf9f12d3"
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
CREATE TABLE "temporary_members" (
|
||||
"index" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
"id" varchar NOT NULL,
|
||||
"guild_id" varchar NOT NULL,
|
||||
"nick" varchar,
|
||||
"joined_at" datetime NOT NULL,
|
||||
"premium_since" bigint,
|
||||
"deaf" boolean NOT NULL,
|
||||
"mute" boolean NOT NULL,
|
||||
"pending" boolean NOT NULL,
|
||||
"settings" text NOT NULL,
|
||||
"last_message_id" varchar,
|
||||
"joined_by" varchar,
|
||||
CONSTRAINT "FK_16aceddd5b89825b8ed6029ad1c" FOREIGN KEY ("guild_id") REFERENCES "guilds" ("id") ON DELETE CASCADE ON UPDATE NO ACTION,
|
||||
CONSTRAINT "FK_28b53062261b996d9c99fa12404" FOREIGN KEY ("id") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE NO ACTION
|
||||
)
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
INSERT INTO "temporary_members"(
|
||||
"index",
|
||||
"id",
|
||||
"guild_id",
|
||||
"nick",
|
||||
"joined_at",
|
||||
"premium_since",
|
||||
"deaf",
|
||||
"mute",
|
||||
"pending",
|
||||
"settings",
|
||||
"last_message_id",
|
||||
"joined_by"
|
||||
)
|
||||
SELECT "index",
|
||||
"id",
|
||||
"guild_id",
|
||||
"nick",
|
||||
"joined_at",
|
||||
"premium_since",
|
||||
"deaf",
|
||||
"mute",
|
||||
"pending",
|
||||
"settings",
|
||||
"last_message_id",
|
||||
"joined_by"
|
||||
FROM "members"
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
DROP TABLE "members"
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE "temporary_members"
|
||||
RENAME TO "members"
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
CREATE UNIQUE INDEX "IDX_bb2bf9386ac443afbbbf9f12d3" ON "members" ("id", "guild_id")
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
DROP INDEX "IDX_bb2bf9386ac443afbbbf9f12d3"
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
CREATE TABLE "temporary_members" (
|
||||
"index" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
"id" varchar NOT NULL,
|
||||
"guild_id" varchar NOT NULL,
|
||||
"nick" varchar,
|
||||
"joined_at" datetime NOT NULL,
|
||||
"premium_since" datetime,
|
||||
"deaf" boolean NOT NULL,
|
||||
"mute" boolean NOT NULL,
|
||||
"pending" boolean NOT NULL,
|
||||
"settings" text NOT NULL,
|
||||
"last_message_id" varchar,
|
||||
"joined_by" varchar,
|
||||
CONSTRAINT "FK_16aceddd5b89825b8ed6029ad1c" FOREIGN KEY ("guild_id") REFERENCES "guilds" ("id") ON DELETE CASCADE ON UPDATE NO ACTION,
|
||||
CONSTRAINT "FK_28b53062261b996d9c99fa12404" FOREIGN KEY ("id") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE NO ACTION
|
||||
)
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
INSERT INTO "temporary_members"(
|
||||
"index",
|
||||
"id",
|
||||
"guild_id",
|
||||
"nick",
|
||||
"joined_at",
|
||||
"premium_since",
|
||||
"deaf",
|
||||
"mute",
|
||||
"pending",
|
||||
"settings",
|
||||
"last_message_id",
|
||||
"joined_by"
|
||||
)
|
||||
SELECT "index",
|
||||
"id",
|
||||
"guild_id",
|
||||
"nick",
|
||||
"joined_at",
|
||||
"premium_since",
|
||||
"deaf",
|
||||
"mute",
|
||||
"pending",
|
||||
"settings",
|
||||
"last_message_id",
|
||||
"joined_by"
|
||||
FROM "members"
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
DROP TABLE "members"
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE "temporary_members"
|
||||
RENAME TO "members"
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
CREATE UNIQUE INDEX "IDX_bb2bf9386ac443afbbbf9f12d3" ON "members" ("id", "guild_id")
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
DROP INDEX "IDX_bb2bf9386ac443afbbbf9f12d3"
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE "members"
|
||||
RENAME TO "temporary_members"
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
CREATE TABLE "members" (
|
||||
"index" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
"id" varchar NOT NULL,
|
||||
"guild_id" varchar NOT NULL,
|
||||
"nick" varchar,
|
||||
"joined_at" datetime NOT NULL,
|
||||
"premium_since" bigint,
|
||||
"deaf" boolean NOT NULL,
|
||||
"mute" boolean NOT NULL,
|
||||
"pending" boolean NOT NULL,
|
||||
"settings" text NOT NULL,
|
||||
"last_message_id" varchar,
|
||||
"joined_by" varchar,
|
||||
CONSTRAINT "FK_16aceddd5b89825b8ed6029ad1c" FOREIGN KEY ("guild_id") REFERENCES "guilds" ("id") ON DELETE CASCADE ON UPDATE NO ACTION,
|
||||
CONSTRAINT "FK_28b53062261b996d9c99fa12404" FOREIGN KEY ("id") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE NO ACTION
|
||||
)
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
INSERT INTO "members"(
|
||||
"index",
|
||||
"id",
|
||||
"guild_id",
|
||||
"nick",
|
||||
"joined_at",
|
||||
"premium_since",
|
||||
"deaf",
|
||||
"mute",
|
||||
"pending",
|
||||
"settings",
|
||||
"last_message_id",
|
||||
"joined_by"
|
||||
)
|
||||
SELECT "index",
|
||||
"id",
|
||||
"guild_id",
|
||||
"nick",
|
||||
"joined_at",
|
||||
"premium_since",
|
||||
"deaf",
|
||||
"mute",
|
||||
"pending",
|
||||
"settings",
|
||||
"last_message_id",
|
||||
"joined_by"
|
||||
FROM "temporary_members"
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
DROP TABLE "temporary_members"
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
CREATE UNIQUE INDEX "IDX_bb2bf9386ac443afbbbf9f12d3" ON "members" ("id", "guild_id")
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
DROP INDEX "IDX_bb2bf9386ac443afbbbf9f12d3"
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE "members"
|
||||
RENAME TO "temporary_members"
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
CREATE TABLE "members" (
|
||||
"index" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
"id" varchar NOT NULL,
|
||||
"guild_id" varchar NOT NULL,
|
||||
"nick" varchar,
|
||||
"joined_at" datetime NOT NULL,
|
||||
"premium_since" bigint,
|
||||
"deaf" boolean NOT NULL,
|
||||
"mute" boolean NOT NULL,
|
||||
"pending" boolean NOT NULL,
|
||||
"settings" text NOT NULL,
|
||||
"last_message_id" varchar,
|
||||
"joined_by" varchar,
|
||||
CONSTRAINT "FK_16aceddd5b89825b8ed6029ad1c" FOREIGN KEY ("guild_id") REFERENCES "guilds" ("id") ON DELETE CASCADE ON UPDATE NO ACTION,
|
||||
CONSTRAINT "FK_28b53062261b996d9c99fa12404" FOREIGN KEY ("id") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE NO ACTION
|
||||
)
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
INSERT INTO "members"(
|
||||
"index",
|
||||
"id",
|
||||
"guild_id",
|
||||
"nick",
|
||||
"joined_at",
|
||||
"premium_since",
|
||||
"deaf",
|
||||
"mute",
|
||||
"pending",
|
||||
"settings",
|
||||
"last_message_id",
|
||||
"joined_by"
|
||||
)
|
||||
SELECT "index",
|
||||
"id",
|
||||
"guild_id",
|
||||
"nick",
|
||||
"joined_at",
|
||||
"premium_since",
|
||||
"deaf",
|
||||
"mute",
|
||||
"pending",
|
||||
"settings",
|
||||
"last_message_id",
|
||||
"joined_by"
|
||||
FROM "temporary_members"
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
DROP TABLE "temporary_members"
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
CREATE UNIQUE INDEX "IDX_bb2bf9386ac443afbbbf9f12d3" ON "members" ("id", "guild_id")
|
||||
`);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -138,6 +138,9 @@ export class BitField {
|
||||
return bit.map((p) => resolve.call(this, p)).reduce((prev, p) => BigInt(prev) | BigInt(p), BigInt(0));
|
||||
}
|
||||
if (typeof bit === "string" && typeof FLAGS[bit] !== "undefined") return FLAGS[bit];
|
||||
if (bit === "0") return BigInt(0); //special case: 0
|
||||
if (typeof bit === "string") return BigInt(bit); //last ditch effort...
|
||||
if(/--debug|--inspect/.test(process.execArgv.join(' '))) debugger; //if you're here, we have an invalid bitfield... if bit is 0, thats fine, I guess...
|
||||
throw new RangeError("BITFIELD_INVALID: " + bit);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user