Merge branch 'master' into slowcord
This commit is contained in:
		
						commit
						ba6c4bd4f2
					
				| @ -7,12 +7,12 @@ | ||||
| 		"BASE_TYPE_BOOLEAN": "This field must be a boolean", | ||||
| 		"BASE_TYPE_CHOICES": "This field must be one of ({{types}})", | ||||
| 		"BASE_TYPE_CLASS": "This field must be an instance of {{type}}", | ||||
| 		"BASE_TYPE_OBJECT": "This field must be an object", | ||||
| 		"BASE_TYPE_ARRAY": "This field must be an array", | ||||
| 		"UNKOWN_FIELD": "Unknown key: {{key}}", | ||||
| 		"BASE_TYPE_CONSTANT": "This field must be {{value}}", | ||||
| 		"EMAIL_TYPE_INVALID_EMAIL": "Not a well-formed email address", | ||||
| 		"DATE_TYPE_PARSE": "Could not parse {{date}}. Should be ISO8601", | ||||
| 		"BASE_TYPE_BAD_LENGTH": "Must be between {{length}} in length" | ||||
| 		"BASE_TYPE_OBJECT": "שדה זה חייב להיות אובייקט", | ||||
| 		"BASE_TYPE_ARRAY": "שדה זה חייב להיות מערך", | ||||
| 		"UNKOWN_FIELD": "מפתח לא ידוע: {{key}}", | ||||
| 		"BASE_TYPE_CONSTANT": "שדה זה להיות {{value}}", | ||||
| 		"EMAIL_TYPE_INVALID_EMAIL": "כתובת דואר אלקטרוני לא חוקית", | ||||
| 		"DATE_TYPE_PARSE": "לא ניתן לנתח {{date}}. צריך להיות ISO8601", | ||||
| 		"BASE_TYPE_BAD_LENGTH": "האורך חייב להיות בין {{length}}" | ||||
| 	} | ||||
| } | ||||
|  | ||||
							
								
								
									
										18
									
								
								api/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										18
									
								
								api/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -4607,8 +4607,9 @@ | ||||
| 			} | ||||
| 		}, | ||||
| 		"../util/node_modules/minimist": { | ||||
| 			"version": "1.2.5", | ||||
| 			"license": "MIT" | ||||
| 			"version": "1.2.6", | ||||
| 			"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", | ||||
| 			"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" | ||||
| 		}, | ||||
| 		"../util/node_modules/minipass": { | ||||
| 			"version": "2.9.0", | ||||
| @ -13196,8 +13197,9 @@ | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/minimist": { | ||||
| 			"version": "1.2.5", | ||||
| 			"license": "MIT" | ||||
| 			"version": "1.2.6", | ||||
| 			"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", | ||||
| 			"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" | ||||
| 		}, | ||||
| 		"node_modules/minipass": { | ||||
| 			"version": "3.1.5", | ||||
| @ -19764,7 +19766,9 @@ | ||||
| 					} | ||||
| 				}, | ||||
| 				"minimist": { | ||||
| 					"version": "1.2.5" | ||||
| 					"version": "1.2.6", | ||||
| 					"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", | ||||
| 					"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" | ||||
| 				}, | ||||
| 				"minipass": { | ||||
| 					"version": "2.9.0", | ||||
| @ -24388,7 +24392,9 @@ | ||||
| 			} | ||||
| 		}, | ||||
| 		"minimist": { | ||||
| 			"version": "1.2.5" | ||||
| 			"version": "1.2.6", | ||||
| 			"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", | ||||
| 			"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" | ||||
| 		}, | ||||
| 		"minipass": { | ||||
| 			"version": "3.1.5", | ||||
|  | ||||
| @ -15,6 +15,7 @@ export const NO_AUTHORIZATION_ROUTES = [ | ||||
| 	"/experiments", | ||||
| 	"/updates", | ||||
| 	"/downloads/", | ||||
| 	"/scheduled-maintenances/upcoming.json", | ||||
| 	// Public kubernetes integration
 | ||||
| 	"/-/readyz", | ||||
| 	"/-/healthz", | ||||
|  | ||||
| @ -19,7 +19,8 @@ export interface InviteCreateSchema { | ||||
| 	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 { channel_id } = req.params; | ||||
| 	const channel = await Channel.findOneOrFail({ where: { id: channel_id }, select: ["id", "name", "type", "guild_id"] }); | ||||
|  | ||||
| @ -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 { route } from "@fosscord/api"; | ||||
| import { handleMessage, postHandleMessage } from "@fosscord/api"; | ||||
| @ -7,18 +7,23 @@ import { MessageCreateSchema } from "../index"; | ||||
| const router = Router(); | ||||
| // 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; | ||||
| 	var body = req.body as MessageCreateSchema; | ||||
| 
 | ||||
| 	const message = await Message.findOneOrFail({ where: { id: message_id, channel_id }, relations: ["attachments"] }); | ||||
| 
 | ||||
| 	const permissions = await getPermission(req.user_id, undefined, channel_id); | ||||
| 	 | ||||
| 	const rights = await getRights(req.user_id); | ||||
| 
 | ||||
| 	if (req.user_id !== message.author_id) { | ||||
| 		permissions.hasThrow("MANAGE_MESSAGES"); | ||||
| 		body = { flags: body.flags }; // admins can only suppress embeds of other messages
 | ||||
| 	} | ||||
| 	if ((req.user_id !== message.author_id)) { | ||||
| 		if (!rights.has("MANAGE_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({ | ||||
| 		...message, | ||||
| @ -46,17 +51,20 @@ router.patch("/", route({ body: "MessageCreateSchema", permission: "SEND_MESSAGE | ||||
| 	return res.json(message); | ||||
| }); | ||||
| 
 | ||||
| // permission check only if deletes messagr from other user
 | ||||
| router.delete("/", route({}), async (req: Request, res: Response) => { | ||||
| 	const { message_id, channel_id } = req.params; | ||||
| 
 | ||||
| 	const channel = await Channel.findOneOrFail({ id: channel_id }); | ||||
| 	const message = await Message.findOneOrFail({ id: message_id }); | ||||
| 	 | ||||
| 	const rights = await getRights(req.user_id); | ||||
| 
 | ||||
| 	if (message.author_id !== req.user_id) { | ||||
| 		const permission = await getPermission(req.user_id, channel.guild_id, channel_id); | ||||
| 		permission.hasThrow("MANAGE_MESSAGES"); | ||||
| 	} | ||||
| 	if ((message.author_id !== req.user_id)) { | ||||
| 		if (!rights.has("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 }); | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| 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 { route } from "@fosscord/api"; | ||||
| import "missing-native-js-functions"; | ||||
| @ -37,9 +37,17 @@ router.get("/", route({}), async (req: Request, res: Response) => { | ||||
| 	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 { 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
 | ||||
| 
 | ||||
| 	if (body.icon) body.icon = await handleFile(`/icons/${guild_id}`, body.icon); | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| 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 { ChannelModifySchema } from "../channels/#channel_id"; | ||||
| 
 | ||||
| @ -20,12 +20,13 @@ export interface GuildCreateSchema { | ||||
| 
 | ||||
| //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 { maxGuilds } = Config.get().limits.user; | ||||
| 	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); | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -13,7 +13,7 @@ router.get("/:code", route({}), async (req: Request, res: Response) => { | ||||
| 	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 { guild_id } = await Invite.findOneOrFail({ code }) | ||||
| 	const { features } = await Guild.findOneOrFail({ id: guild_id}); | ||||
|  | ||||
							
								
								
									
										12
									
								
								api/src/routes/scheduled-maintenances/upcoming_json.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								api/src/routes/scheduled-maintenances/upcoming_json.ts
									
									
									
									
									
										Normal 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; | ||||
| @ -1,14 +1,39 @@ | ||||
| import { Request, Response, Router } from "express"; | ||||
| import { route } from "@fosscord/api"; | ||||
| import { User, emitEvent } from "@fosscord/util"; | ||||
| 
 | ||||
| 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) => { | ||||
| 	//TODO
 | ||||
| 	res.json({ | ||||
| 		message: "Unknown User", | ||||
| 		code: 10013 | ||||
| 	}).status(404); | ||||
| 	const { id } = req.params; | ||||
| 	const user = await User.findOneOrFail({ where: { id: req.user_id } }); | ||||
| 	const noteUser = await User.findOneOrFail({ where: { id: id }});		//if noted user does not exist throw
 | ||||
| 	const { note } = req.body; | ||||
| 
 | ||||
| 	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; | ||||
|  | ||||
| @ -7,6 +7,7 @@ import { | ||||
| 	MessageCreateEvent, | ||||
| 	MessageUpdateEvent, | ||||
| 	getPermission, | ||||
| 	getRights, | ||||
| 	CHANNEL_MENTION, | ||||
| 	Snowflake, | ||||
| 	USER_MENTION, | ||||
| @ -61,19 +62,20 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> { | ||||
| 		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) { | ||||
| 		message.author = await User.getPublicUser(opts.author_id); | ||||
| 	} | ||||
| 		const rights = await getRights(opts.author_id); | ||||
| 		rights.hasThrow("SEND_MESSAGES"); | ||||
| 	}	 | ||||
| 	if (opts.application_id) { | ||||
| 		message.application = await Application.findOneOrFail({ id: opts.application_id }); | ||||
| 	} | ||||
| 	if (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); | ||||
| 	permission.hasThrow("SEND_MESSAGES"); // TODO: add the rights check
 | ||||
| 	permission.hasThrow("SEND_MESSAGES"); | ||||
| 	if (permission.cache.member) { | ||||
| 		message.member = permission.cache.member; | ||||
| 	} | ||||
| @ -81,7 +83,7 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> { | ||||
| 	if (opts.tts) permission.hasThrow("SEND_TTS_MESSAGES"); | ||||
| 	if (opts.message_reference) { | ||||
| 		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
 | ||||
| 		if (message.guild_id !== null) { | ||||
| 			const guild = await Guild.findOneOrFail({ id: channel.guild_id }); | ||||
| 			if (!guild.features.includes("CROSS_CHANNEL_REPLIES")) { | ||||
| @ -89,7 +91,7 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> { | ||||
| 				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
 | ||||
| 		message.type = MessageType.REPLY; | ||||
| 	} | ||||
|  | ||||
| @ -13,6 +13,7 @@ const blocklist: string[] = []; // TODO: update ones passwordblocklist is stored | ||||
|  *  - min <n> numbers | ||||
|  *  - min <n> symbols | ||||
|  *  - min <n> uppercase chars | ||||
|  *  - shannon entropy divided by password entropy | ||||
|  * | ||||
|  * Returns: 0 > pw > 1 | ||||
|  */ | ||||
| @ -22,28 +23,38 @@ export function checkPassword(password: string): number { | ||||
| 
 | ||||
| 	// checks for total password len
 | ||||
| 	if (password.length >= minLength - 1) { | ||||
| 		strength += 0.25; | ||||
| 		strength += 0.05; | ||||
| 	} | ||||
| 
 | ||||
| 	// checks for amount of Numbers
 | ||||
| 	if (password.count(reNUMBER) >= minNumbers - 1) { | ||||
| 		strength += 0.25; | ||||
| 		strength += 0.05; | ||||
| 	} | ||||
| 
 | ||||
| 	// checks for amount of Uppercase Letters
 | ||||
| 	if (password.count(reUPPERCASELETTER) >= minUpperCase - 1) { | ||||
| 		strength += 0.25; | ||||
| 		strength += 0.05; | ||||
| 	} | ||||
| 
 | ||||
| 	// checks for amount of symbols
 | ||||
| 	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
 | ||||
| 	if (password.length == password.count(reNUMBER) || password.length === password.count(reUPPERCASELETTER)) { | ||||
| 		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; | ||||
| } | ||||
|  | ||||
							
								
								
									
										10
									
								
								bundle/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								bundle/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -7350,8 +7350,9 @@ | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/minimist": { | ||||
| 			"version": "1.2.5", | ||||
| 			"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" | ||||
| 			"version": "1.2.6", | ||||
| 			"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", | ||||
| 			"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" | ||||
| 		}, | ||||
| 		"node_modules/minipass": { | ||||
| 			"version": "3.1.5", | ||||
| @ -16582,8 +16583,9 @@ | ||||
| 			} | ||||
| 		}, | ||||
| 		"minimist": { | ||||
| 			"version": "1.2.5", | ||||
| 			"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" | ||||
| 			"version": "1.2.6", | ||||
| 			"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", | ||||
| 			"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" | ||||
| 		}, | ||||
| 		"minipass": { | ||||
| 			"version": "3.1.5", | ||||
|  | ||||
							
								
								
									
										12
									
								
								cdn/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										12
									
								
								cdn/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -5739,9 +5739,9 @@ | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/minimist": { | ||||
| 			"version": "1.2.5", | ||||
| 			"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", | ||||
| 			"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" | ||||
| 			"version": "1.2.6", | ||||
| 			"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", | ||||
| 			"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" | ||||
| 		}, | ||||
| 		"node_modules/minipass": { | ||||
| 			"version": "3.1.6", | ||||
| @ -12301,9 +12301,9 @@ | ||||
| 			} | ||||
| 		}, | ||||
| 		"minimist": { | ||||
| 			"version": "1.2.5", | ||||
| 			"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", | ||||
| 			"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" | ||||
| 			"version": "1.2.6", | ||||
| 			"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", | ||||
| 			"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" | ||||
| 		}, | ||||
| 		"minipass": { | ||||
| 			"version": "3.1.6", | ||||
|  | ||||
							
								
								
									
										20
									
								
								gateway/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										20
									
								
								gateway/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -4479,8 +4479,9 @@ | ||||
| 			} | ||||
| 		}, | ||||
| 		"../util/node_modules/minimist": { | ||||
| 			"version": "1.2.5", | ||||
| 			"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" | ||||
| 			"version": "1.2.6", | ||||
| 			"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", | ||||
| 			"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" | ||||
| 		}, | ||||
| 		"../util/node_modules/minipass": { | ||||
| 			"version": "2.9.0", | ||||
| @ -8768,8 +8769,9 @@ | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/minimist": { | ||||
| 			"version": "1.2.5", | ||||
| 			"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", | ||||
| 			"version": "1.2.6", | ||||
| 			"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", | ||||
| 			"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", | ||||
| 			"dev": true | ||||
| 		}, | ||||
| 		"node_modules/minipass": { | ||||
| @ -13666,8 +13668,9 @@ | ||||
| 					} | ||||
| 				}, | ||||
| 				"minimist": { | ||||
| 					"version": "1.2.5", | ||||
| 					"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" | ||||
| 					"version": "1.2.6", | ||||
| 					"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", | ||||
| 					"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" | ||||
| 				}, | ||||
| 				"minipass": { | ||||
| 					"version": "2.9.0", | ||||
| @ -16870,8 +16873,9 @@ | ||||
| 			} | ||||
| 		}, | ||||
| 		"minimist": { | ||||
| 			"version": "1.2.5", | ||||
| 			"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", | ||||
| 			"version": "1.2.6", | ||||
| 			"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", | ||||
| 			"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", | ||||
| 			"dev": true | ||||
| 		}, | ||||
| 		"minipass": { | ||||
|  | ||||
							
								
								
									
										12
									
								
								util/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										12
									
								
								util/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -5003,9 +5003,9 @@ | ||||
| 			} | ||||
| 		}, | ||||
| 		"node_modules/minimist": { | ||||
| 			"version": "1.2.5", | ||||
| 			"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", | ||||
| 			"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" | ||||
| 			"version": "1.2.6", | ||||
| 			"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", | ||||
| 			"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" | ||||
| 		}, | ||||
| 		"node_modules/minipass": { | ||||
| 			"version": "2.9.0", | ||||
| @ -12060,9 +12060,9 @@ | ||||
| 			} | ||||
| 		}, | ||||
| 		"minimist": { | ||||
| 			"version": "1.2.5", | ||||
| 			"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", | ||||
| 			"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" | ||||
| 			"version": "1.2.6", | ||||
| 			"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", | ||||
| 			"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" | ||||
| 		}, | ||||
| 		"minipass": { | ||||
| 			"version": "2.9.0", | ||||
|  | ||||
| @ -20,13 +20,17 @@ export enum ChannelType { | ||||
| 	GROUP_DM = 3, // a direct message between multiple users
 | ||||
| 	GUILD_CATEGORY = 4, // an organizational category that contains zero or more channels
 | ||||
| 	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_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_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_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
 | ||||
| 	UNHANDLED = 255 // unhandled unowned pass-through channel type
 | ||||
| } | ||||
|  | ||||
							
								
								
									
										35
									
								
								util/src/entities/Encryption.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								util/src/entities/Encryption.ts
									
									
									
									
									
										Normal 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; | ||||
| 
 | ||||
| } | ||||
| @ -187,11 +187,11 @@ export class Guild extends BaseClass { | ||||
| 
 | ||||
| 	@Column({ nullable: true }) | ||||
| 	@RelationId((guild: Guild) => guild.owner) | ||||
| 	owner_id: string; | ||||
| 	owner_id?: string; // optional to allow for ownerless guilds
 | ||||
| 
 | ||||
| 	@JoinColumn({ name: "owner_id", referencedColumnName: "id" }) | ||||
| 	@ManyToOne(() => User) | ||||
| 	owner: User; | ||||
| 	owner?: User; // optional to allow for ownerless guilds
 | ||||
| 
 | ||||
| 	@Column({ nullable: true }) | ||||
| 	preferred_locale?: string; | ||||
| @ -200,7 +200,7 @@ export class Guild extends BaseClass { | ||||
| 	premium_subscription_count?: number; | ||||
| 
 | ||||
| 	@Column({ nullable: true }) | ||||
| 	premium_tier?: number; // nitro boost level
 | ||||
| 	premium_tier?: number; // crowd premium level
 | ||||
| 
 | ||||
| 	@Column({ nullable: true }) | ||||
| 	@RelationId((guild: Guild) => guild.public_updates_channel) | ||||
| @ -269,6 +269,10 @@ export class Guild extends BaseClass { | ||||
| 
 | ||||
| 	@Column({ nullable: true }) | ||||
| 	nsfw?: boolean; | ||||
| 	 | ||||
| 	// TODO: nested guilds
 | ||||
| 	@Column({ nullable: true }) | ||||
| 	parent?: string; | ||||
| 
 | ||||
| 	// only for developer portal
 | ||||
| 	permissions?: number; | ||||
| @ -308,7 +312,7 @@ export class Guild extends BaseClass { | ||||
| 			verification_level: 0, | ||||
| 			welcome_screen: { | ||||
| 				enabled: false, | ||||
| 				description: "No description", | ||||
| 				description: "Fill in your description", | ||||
| 				welcome_channels: [], | ||||
| 			}, | ||||
| 			widget_enabled: true, // NB: don't set it as false to prevent artificial restrictions
 | ||||
|  | ||||
| @ -70,7 +70,7 @@ export class Member extends BaseClassWithoutId { | ||||
| 
 | ||||
| 	@Column({ nullable: true }) | ||||
| 	nick?: string; | ||||
| 
 | ||||
| 	 | ||||
| 	@JoinTable({ | ||||
| 		name: "member_roles", | ||||
| 		joinColumn: { name: "index", referencedColumnName: "index" }, | ||||
| @ -85,8 +85,8 @@ export class Member extends BaseClassWithoutId { | ||||
| 	@Column() | ||||
| 	joined_at: Date; | ||||
| 
 | ||||
| 	@Column() | ||||
| 	premium_since?: Date; | ||||
| 	@Column({ type: "bigint", nullable: true }) | ||||
| 	premium_since?: number; | ||||
| 
 | ||||
| 	@Column() | ||||
| 	deaf: boolean; | ||||
| @ -102,8 +102,17 @@ export class Member extends BaseClassWithoutId { | ||||
| 
 | ||||
| 	@Column({ nullable: true }) | ||||
| 	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" })
 | ||||
| 	// read_state: ReadState;
 | ||||
| 
 | ||||
| @ -245,7 +254,7 @@ export class Member extends BaseClassWithoutId { | ||||
| 			nick: undefined, | ||||
| 			roles: [guild_id], // @everyone role
 | ||||
| 			joined_at: new Date(), | ||||
| 			premium_since: new Date(), | ||||
| 			premium_since: (new Date()).getTime(), | ||||
| 			deaf: false, | ||||
| 			mute: false, | ||||
| 			pending: false, | ||||
|  | ||||
| @ -41,8 +41,14 @@ export enum MessageType { | ||||
| 	CHANNEL_FOLLOW_ADD = 12, | ||||
| 	GUILD_DISCOVERY_DISQUALIFIED = 14, | ||||
| 	GUILD_DISCOVERY_REQUALIFIED = 15, | ||||
| 	ENCRYPTED = 16, | ||||
| 	REPLY = 19, | ||||
| 	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") | ||||
| @ -84,7 +90,7 @@ export class Message extends BaseClass { | ||||
| 	@RelationId((message: Message) => message.member) | ||||
| 	member_id: string; | ||||
| 
 | ||||
| 	@JoinColumn({ name: "author_id", referencedColumnName: "id" }) | ||||
| 	@JoinColumn({ name: "member_id", referencedColumnName: "id" }) | ||||
| 	@ManyToOne(() => User, { | ||||
| 		onDelete: "CASCADE", | ||||
| 	}) | ||||
| @ -203,6 +209,7 @@ export interface MessageComponent { | ||||
| } | ||||
| 
 | ||||
| export enum MessageComponentType { | ||||
| 	Script = 0, // self command script
 | ||||
| 	ActionRow = 1, | ||||
| 	Button = 2, | ||||
| } | ||||
|  | ||||
| @ -49,6 +49,7 @@ export class ReadState extends BaseClass { | ||||
| 	@Column({ nullable: true }) | ||||
| 	mention_count: number; | ||||
| 
 | ||||
| 	@Column({ nullable: true }) | ||||
| 	// @Column({ nullable: true })
 | ||||
| 	// TODO: derive this from (last_message_id=notifications_cursor=public_ack)=true
 | ||||
| 	manual: boolean; | ||||
| } | ||||
|  | ||||
| @ -60,7 +60,7 @@ export class User extends BaseClass { | ||||
| 	username: string; // username max length 32, min 2 (should be configurable)
 | ||||
| 
 | ||||
| 	@Column() | ||||
| 	discriminator: string; // #0001 4 digit long string from #0001 - #9999
 | ||||
| 	discriminator: string; // opaque string: 4 digits on discord.com
 | ||||
| 
 | ||||
| 	setDiscriminator(val: string) { | ||||
| 		const number = Number(val); | ||||
| @ -88,10 +88,10 @@ export class User extends BaseClass { | ||||
| 	mobile: boolean; // if the user has mobile app installed
 | ||||
| 
 | ||||
| 	@Column() | ||||
| 	premium: boolean; // if user bought nitro
 | ||||
| 
 | ||||
| 	premium: boolean; // if user bought individual premium
 | ||||
| 	 | ||||
| 	@Column() | ||||
| 	premium_type: number; // nitro level
 | ||||
| 	premium_type: number; // individual premium level
 | ||||
| 
 | ||||
| 	@Column() | ||||
| 	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)
 | ||||
| 
 | ||||
| 	@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 }) | ||||
| 	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 }) | ||||
| 	mfa_enabled: boolean; // if multi factor authentication is enabled
 | ||||
| 
 | ||||
| @ -132,7 +132,7 @@ export class User extends BaseClass { | ||||
| 	@Column() | ||||
| 	public_flags: number; | ||||
| 
 | ||||
| 	@Column() | ||||
| 	@Column({ type: "bigint" }) | ||||
| 	rights: string; // Rights
 | ||||
| 
 | ||||
| 	@OneToMany(() => Session, (session: Session) => session.user) | ||||
| @ -164,6 +164,9 @@ export class User extends BaseClass { | ||||
| 	@Column({ type: "simple-json", select: false }) | ||||
| 	settings: UserSettings; | ||||
| 
 | ||||
| 	@Column({ type: "simple-json" }) | ||||
| 	notes: { [key: string]: string };	//key is ID of user
 | ||||
| 
 | ||||
| 	toPublicUser() { | ||||
| 		const user: any = {}; | ||||
| 		PublicUserProjection.forEach((x) => { | ||||
| @ -271,6 +274,7 @@ export class User extends BaseClass { | ||||
| 			}, | ||||
| 			settings: { ...defaultSettings, locale: language }, | ||||
| 			fingerprints: [], | ||||
| 			notes: {}, | ||||
| 		}); | ||||
| 
 | ||||
| 		await user.save(); | ||||
|  | ||||
| @ -623,6 +623,7 @@ export type EVENT = | ||||
| 	| "PRESENCE_UPDATE" | ||||
| 	| "TYPING_START" | ||||
| 	| "USER_UPDATE" | ||||
| 	| "USER_NOTE_UPDATE" | ||||
| 	| "WEBHOOKS_UPDATE" | ||||
| 	| "INTERACTION_CREATE" | ||||
| 	| "VOICE_STATE_UPDATE" | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| import { BitField } from "./BitField"; | ||||
| import "missing-native-js-functions"; | ||||
| import { BitFieldResolvable, BitFlag } from "./BitField"; | ||||
| import { User } from "../entities"; | ||||
| 
 | ||||
| var HTTPError: any; | ||||
| 
 | ||||
| @ -85,6 +86,15 @@ export class Rights extends BitField { | ||||
| 		// @ts-ignore
 | ||||
| 		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)); | ||||
| 
 | ||||
| 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); | ||||
| }  | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Madeline
						Madeline