Merge branch 'master' of https://github.com/fosscord/fosscord-api
This commit is contained in:
		
						commit
						fe8a1c1000
					
				
							
								
								
									
										1
									
								
								.npmignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.npmignore
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| !dist/ | ||||
							
								
								
									
										32
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								README.md
									
									
									
									
									
								
							| @ -1,39 +1,51 @@ | ||||
| # Fosscord API Server | ||||
| This repository contains the HTTP API Server | ||||
| 
 | ||||
| This repository contains the Fosscord HTTP API Server | ||||
| 
 | ||||
| ## Bug Tracker | ||||
| 
 | ||||
| [Project Board](https://github.com/fosscord/fosscord-api/projects/2) | ||||
| 
 | ||||
| ## API | ||||
| 
 | ||||
| We use [express](https://expressjs.com/) for the HTTP Server and  | ||||
| We use [express](https://expressjs.com/) for the HTTP Server and | ||||
| [lambert-server](https://www.npmjs.com/package/lambert-server) for route handling and body validation (customized). | ||||
| 
 | ||||
| ## Contribution | ||||
| You should be familiar with: | ||||
| - [Git](https://git-scm.com/) | ||||
| - [NodeJS](https://nodejs.org/) | ||||
| - [TypeScript](https://www.typescriptlang.org/) | ||||
| - [MongoDB/mongoose](http://mongoosejs.com/) | ||||
| 
 | ||||
| and the other technologies we use  | ||||
| You should be familiar with: | ||||
| 
 | ||||
| -   [Git](https://git-scm.com/) | ||||
| -   [NodeJS](https://nodejs.org/) | ||||
| -   [TypeScript](https://www.typescriptlang.org/) | ||||
| -   [MongoDB/mongoose](http://mongoosejs.com/) | ||||
| 
 | ||||
| and the other technologies we use | ||||
| 
 | ||||
| ### Getting Started | ||||
| 
 | ||||
| Clone the Repository: | ||||
| 
 | ||||
| ```bash | ||||
| git clone https://github.com/fosscord/fosscord-api | ||||
| cd discord-server | ||||
| ``` | ||||
| 
 | ||||
| #### Install (dev)dependencies: | ||||
| 
 | ||||
| ```bash | ||||
| npm install | ||||
| npm install --only=dev | ||||
| ``` | ||||
| 
 | ||||
| #### Starting: | ||||
| 
 | ||||
| ``` | ||||
| npm start | ||||
| ``` | ||||
| 
 | ||||
| #### Debugging: | ||||
| 
 | ||||
| **Vscode:** | ||||
| The Launch file configuration is in ``./vscode/launch.json``, | ||||
| so you can just debug the server by pressing ``F5`` or the ``> Launch Server`` button | ||||
| The Launch file configuration is in `./vscode/launch.json`, | ||||
| so you can just debug the server by pressing `F5` or the `> Launch Server` button | ||||
|  | ||||
							
								
								
									
										830
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										830
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										23
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								package.json
									
									
									
									
									
								
							| @ -1,35 +1,42 @@ | ||||
| { | ||||
| 	"name": "fosscord-api", | ||||
| 	"name": "@fosscord/api", | ||||
| 	"version": "1.0.0", | ||||
| 	"description": "This repository contains the HTTP API Server", | ||||
| 	"main": "index.js", | ||||
| 	"main": "dist/Server.js", | ||||
| 	"types": "dist/Server.d.ts", | ||||
| 	"scripts": { | ||||
| 		"test": "jest", | ||||
| 		"test:watch": "jest --watch", | ||||
| 		"start": "npm run build:util && npm run build && node dist/", | ||||
| 		"start": "npm run build:util && npm run build && node dist/start", | ||||
| 		"build": "tsc -b .", | ||||
| 		"build:util": "tsc -b ./node_modules/fosscord-server-util/", | ||||
| 		"postinstall": "npm i github:fosscord/fosscord-server-util && patch-package" | ||||
| 		"build:util": "tsc -b ./node_modules/@fosscord/server-util/", | ||||
| 		"postinstall": "patch-package" | ||||
| 	}, | ||||
| 	"repository": { | ||||
| 		"type": "git", | ||||
| 		"url": "git+https://github.com/fosscord/fosscord-api.git" | ||||
| 	}, | ||||
| 	"keywords": [], | ||||
| 	"author": "", | ||||
| 	"keywords": [ | ||||
| 		"discord", | ||||
| 		"fosscord", | ||||
| 		"fosscord-api", | ||||
| 		"discord open source", | ||||
| 		"discord-open-source" | ||||
| 	], | ||||
| 	"author": "Fosscord", | ||||
| 	"license": "ISC", | ||||
| 	"bugs": { | ||||
| 		"url": "https://github.com/fosscord/fosscord-api/issues" | ||||
| 	}, | ||||
| 	"homepage": "https://github.com/fosscord/fosscord-api#readme", | ||||
| 	"dependencies": { | ||||
| 		"@fosscord/server-util": "^1.0.4", | ||||
| 		"@types/jest": "^26.0.22", | ||||
| 		"bcrypt": "^5.0.0", | ||||
| 		"body-parser": "^1.19.0", | ||||
| 		"dotenv": "^8.2.0", | ||||
| 		"express": "^4.17.1", | ||||
| 		"express-validator": "^6.9.2", | ||||
| 		"fosscord-server-util": "github:fosscord/fosscord-server-util", | ||||
| 		"i18next": "^19.8.5", | ||||
| 		"i18next-http-middleware": "^3.1.0", | ||||
| 		"i18next-node-fs-backend": "^2.1.3", | ||||
|  | ||||
| @ -4,7 +4,7 @@ import { Connection } from "mongoose"; | ||||
| import { Server, ServerOptions } from "lambert-server"; | ||||
| import { Authentication, GlobalRateLimit } from "./middlewares/"; | ||||
| import Config from "./util/Config"; | ||||
| import { db } from "fosscord-server-util"; | ||||
| import { db } from "@fosscord/server-util"; | ||||
| import i18next from "i18next"; | ||||
| import i18nextMiddleware, { I18next } from "i18next-http-middleware"; | ||||
| import i18nextBackend from "i18next-node-fs-backend"; | ||||
| @ -13,21 +13,21 @@ import { BodyParser } from "./middlewares/BodyParser"; | ||||
| import { Router } from "express"; | ||||
| import fetch from "node-fetch"; | ||||
| 
 | ||||
| export interface DiscordServerOptions extends ServerOptions {} | ||||
| export interface FosscordServerOptions extends ServerOptions {} | ||||
| 
 | ||||
| declare global { | ||||
| 	namespace Express { | ||||
| 		interface Request { | ||||
| 			// @ts-ignore
 | ||||
| 			server: DiscordServer; | ||||
| 			server: FosscordServer; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| export class DiscordServer extends Server { | ||||
| 	public options: DiscordServerOptions; | ||||
| export class FosscordServer extends Server { | ||||
| 	public options: FosscordServerOptions; | ||||
| 
 | ||||
| 	constructor(opts?: Partial<DiscordServerOptions>) { | ||||
| 	constructor(opts?: Partial<FosscordServerOptions>) { | ||||
| 		// @ts-ignore
 | ||||
| 		super({ ...opts, errorHandler: false, jsonBody: false }); | ||||
| 	} | ||||
|  | ||||
							
								
								
									
										36
									
								
								src/index.ts
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								src/index.ts
									
									
									
									
									
								
							| @ -1,17 +1,19 @@ | ||||
| process.on("uncaughtException", console.error); | ||||
| process.on("unhandledRejection", console.error); | ||||
| 
 | ||||
| import "missing-native-js-functions"; | ||||
| import { config } from "dotenv"; | ||||
| config(); | ||||
| import { DiscordServer } from "./Server"; | ||||
| 
 | ||||
| var port = Number(process.env.PORT); | ||||
| if (isNaN(port)) port = 1000; | ||||
| 
 | ||||
| const server = new DiscordServer({ port }); | ||||
| server.start().catch(console.error); | ||||
| 
 | ||||
| // @ts-ignore
 | ||||
| global.server = server; | ||||
| export default server; | ||||
| export * from "./Server"; | ||||
| export * from "./middlewares/"; | ||||
| export * from "./schema/Ban"; | ||||
| export * from "./schema/Channel"; | ||||
| export * from "./schema/Guild"; | ||||
| export * from "./schema/Invite"; | ||||
| export * from "./schema/Message"; | ||||
| export * from "./util/Captcha"; | ||||
| export * from "./util/Config"; | ||||
| export * from "./util/Constants"; | ||||
| export * from "./util/Event"; | ||||
| export * from "./util/instanceOf"; | ||||
| export * from "./util/Event"; | ||||
| export * from "./util/instanceOf"; | ||||
| export * from "./util/Member"; | ||||
| export * from "./util/RandomInviteID"; | ||||
| export * from "./util/String"; | ||||
| export * from "./util/User"; | ||||
| export { check as checkPassword } from "./util/passwordStrength"; | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| import { NextFunction, Request, Response } from "express"; | ||||
| import { HTTPError } from "lambert-server"; | ||||
| import { checkToken } from "fosscord-server-util"; | ||||
| import { checkToken } from "@fosscord/server-util"; | ||||
| 
 | ||||
| export const NO_AUTHORIZATION_ROUTES = [ | ||||
| 	"/api/v8/auth/login", | ||||
|  | ||||
| @ -1,4 +1,6 @@ | ||||
| import { Authentication } from "./Authentication"; | ||||
| import { GlobalRateLimit } from "./GlobalRateLimit"; | ||||
| 
 | ||||
| export { Authentication, GlobalRateLimit }; | ||||
| export * from "./GlobalRateLimit"; | ||||
| export * from "./Authentication"; | ||||
| export * from "./BodyParser"; | ||||
| export * from "./CORS"; | ||||
| export * from "./ErrorHandler"; | ||||
| export * from "./RateLimit"; | ||||
|  | ||||
| @ -2,7 +2,7 @@ import { Request, Response, Router } from "express"; | ||||
| import { check, FieldErrors, Length } from "../../util/instanceOf"; | ||||
| import bcrypt from "bcrypt"; | ||||
| import jwt from "jsonwebtoken"; | ||||
| import { User, UserModel } from "fosscord-server-util"; | ||||
| import { UserModel } from "@fosscord/server-util"; | ||||
| import Config from "../../util/Config"; | ||||
| import { adjustEmail } from "./register"; | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| import { Request, Response, Router } from "express"; | ||||
| import Config from "../../util/Config"; | ||||
| import { trimSpecial, User, Snowflake, UserModel } from "fosscord-server-util"; | ||||
| import { trimSpecial, User, Snowflake, UserModel } from "@fosscord/server-util"; | ||||
| import bcrypt from "bcrypt"; | ||||
| import { check, Email, EMAIL_REGEX, FieldErrors, Length } from "../../util/instanceOf"; | ||||
| import "missing-native-js-functions"; | ||||
|  | ||||
| @ -1,4 +1,14 @@ | ||||
| import { Router } from "express"; | ||||
| const router: Router = Router(); | ||||
| // TODO:
 | ||||
| 
 | ||||
| export default router; | ||||
| 
 | ||||
| /** | ||||
|  * | ||||
|  * @param {"webhook_channel_id":"754001514330062952"} | ||||
|  * | ||||
|  * Creates a WebHook in the channel and returns the id of it | ||||
|  * | ||||
|  * @returns {"channel_id": "816382962056560690", "webhook_id": "834910735095037962"} | ||||
|  */ | ||||
|  | ||||
| @ -1,4 +1,30 @@ | ||||
| import { ChannelModel, getPermission, toObject } from "@fosscord/server-util"; | ||||
| import { Router } from "express"; | ||||
| import { HTTPError } from "lambert-server"; | ||||
| const router: Router = Router(); | ||||
| // TODO: delete channel
 | ||||
| // TODO: Get channel
 | ||||
| 
 | ||||
| router.delete("/", async(req,res)=>{ | ||||
| 	const {channel_id} = req.params | ||||
| 
 | ||||
| 	const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true, type: true, permission_overwrites: true }).exec(); | ||||
| 	if (!channel) throw new HTTPError("Channel not found", 404); | ||||
| 	if (channel.guild_id) { | ||||
| 		const permission = await getPermission(req.user_id, channel.guild_id) | ||||
| 		permission.hasThrow("MANAGE_CHANNELS") | ||||
| 		 | ||||
| 		// TODO Channel Update Gateway event will fire for each of them
 | ||||
| 		await ChannelModel.updateMany({parent_id: channel_id}, {$set: {channel_id: null}}).exec() | ||||
| 		 | ||||
| 		await ChannelModel.deleteOne({id: channel_id}) | ||||
| 	} | ||||
| 	 | ||||
| 	// TODO: Dm channel "close" not delete
 | ||||
| 	 | ||||
| 	const data = toObject(channel); | ||||
| 	//TODO: Reload channel list if request successful
 | ||||
| 	res.send(data) | ||||
| }) | ||||
| 
 | ||||
| export default router; | ||||
|  | ||||
| @ -7,7 +7,7 @@ import { emitEvent } from "../../../util/Event"; | ||||
| 
 | ||||
| import { InviteCreateSchema } from "../../../schema/Invite"; | ||||
| 
 | ||||
| import { getPermission, ChannelModel, InviteModel, InviteCreateEvent, toObject } from "fosscord-server-util"; | ||||
| import { getPermission, ChannelModel, InviteModel, InviteCreateEvent, toObject } from "@fosscord/server-util"; | ||||
| 
 | ||||
| const router: Router = Router(); | ||||
| 
 | ||||
| @ -22,10 +22,7 @@ router.post("/", check(InviteCreateSchema), async (req: Request, res: Response) | ||||
| 	const { guild_id } = channel; | ||||
| 
 | ||||
| 	const permission = await getPermission(user_id, guild_id); | ||||
| 
 | ||||
| 	if (!permission.has("CREATE_INSTANT_INVITE")) { | ||||
| 		throw new HTTPError("You aren't authorised to access this endpoint", 401); | ||||
| 	} | ||||
| 	permission.hasThrow("CREATE_INSTANT_INVITE"); | ||||
| 
 | ||||
| 	const invite = { | ||||
| 		code: random(), | ||||
| @ -55,10 +52,7 @@ router.get("/", async (req: Request, res: Response) => { | ||||
| 	} | ||||
| 	const { guild_id } = channel; | ||||
| 	const permission = await getPermission(user_id, guild_id); | ||||
| 
 | ||||
| 	if (!permission.has("MANAGE_CHANNELS")) { | ||||
| 		throw new HTTPError("You aren't authorised to access this endpoint", 401); | ||||
| 	} | ||||
| 	permission.hasThrow("MANAGE_CHANNELS"); | ||||
| 
 | ||||
| 	const invites = await InviteModel.find({ guild_id }).exec(); | ||||
| 
 | ||||
|  | ||||
| @ -0,0 +1,8 @@ | ||||
| import { Router } from "express"; | ||||
| 
 | ||||
| const router = Router(); | ||||
| 
 | ||||
| // TODO:
 | ||||
| // router.post("/", (req, res) => {});
 | ||||
| 
 | ||||
| export default router; | ||||
| @ -0,0 +1,6 @@ | ||||
| import { Router } from "express"; | ||||
| 
 | ||||
| const router = Router(); | ||||
| // TODO:
 | ||||
| 
 | ||||
| export default router; | ||||
| @ -0,0 +1,6 @@ | ||||
| import { Router } from "express"; | ||||
| 
 | ||||
| const router = Router(); | ||||
| // TODO:
 | ||||
| 
 | ||||
| export default router; | ||||
| @ -1,5 +1,5 @@ | ||||
| import { Router } from "express"; | ||||
| import { ChannelModel, getPermission, MessageDeleteBulkEvent, MessageModel } from "fosscord-server-util"; | ||||
| import { ChannelModel, getPermission, MessageDeleteBulkEvent, MessageModel } from "@fosscord/server-util"; | ||||
| import { HTTPError } from "lambert-server"; | ||||
| import Config from "../../../../util/Config"; | ||||
| import { emitEvent } from "../../../../util/Event"; | ||||
| @ -13,12 +13,12 @@ export default router; | ||||
| // TODO: should this request fail, if you provide messages older than 14 days/invalid ids?
 | ||||
| // https://discord.com/developers/docs/resources/channel#bulk-delete-messages
 | ||||
| router.post("/", check({ messages: [String] }), async (req, res) => { | ||||
| 	const channel_id = req.params.channel_id | ||||
| 	const channel_id = req.params.channel_id; | ||||
| 	const channel = await ChannelModel.findOne({ id: channel_id }, { permission_overwrites: true, guild_id: true }).exec(); | ||||
| 	if (!channel?.guild_id) throw new HTTPError("Can't bulk delete dm channel messages", 400); | ||||
| 
 | ||||
| 	const permission = await getPermission(req.user_id, channel?.guild_id, channel_id, { channel }); | ||||
| 	if (!permission.has("MANAGE_MESSAGES")) throw new HTTPError("You are missing the MANAGE_MESSAGES permissions"); | ||||
| 	permission.hasThrow("MANAGE_MESSAGES"); | ||||
| 
 | ||||
| 	const { maxBulkDelete } = Config.get().limits.message; | ||||
| 
 | ||||
|  | ||||
| @ -9,7 +9,7 @@ import { | ||||
| 	MessageModel, | ||||
| 	Snowflake, | ||||
| 	toObject, | ||||
| } from "fosscord-server-util"; | ||||
| } from "@fosscord/server-util"; | ||||
| import { HTTPError } from "lambert-server"; | ||||
| import { MessageCreateSchema } from "../../../../schema/Message"; | ||||
| import { check, instanceOf, Length } from "../../../../util/instanceOf"; | ||||
| @ -22,7 +22,7 @@ const router: Router = Router(); | ||||
| 
 | ||||
| export default router; | ||||
| 
 | ||||
| function isTextChannel(type: ChannelType): boolean { | ||||
| export function isTextChannel(type: ChannelType): boolean { | ||||
| 	switch (type) { | ||||
| 		case ChannelType.GUILD_VOICE: | ||||
| 		case ChannelType.GUILD_CATEGORY: | ||||
| @ -62,7 +62,8 @@ router.get("/", async (req, res) => { | ||||
| 
 | ||||
| 	if (channel.guild_id) { | ||||
| 		const permissions = await getPermission(req.user_id, channel.guild_id, channel_id, { channel }); | ||||
| 		if (!permissions.has("VIEW_CHANNEL")) throw new HTTPError("You don't have permission to view this channel", 401); | ||||
| 		permissions.hasThrow("VIEW_CHANNEL"); | ||||
| 
 | ||||
| 		if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]); | ||||
| 	} else if (channel.recipients) { | ||||
| 		// group/dm channel
 | ||||
| @ -106,11 +107,10 @@ router.post("/", check(MessageCreateSchema), async (req, res) => { | ||||
| 
 | ||||
| 	if (channel.guild_id) { | ||||
| 		const permissions = await getPermission(req.user_id, channel.guild_id, channel_id, { channel }); | ||||
| 		if (!permissions.has("SEND_MESSAGES")) throw new HTTPError("You don't have the SEND_MESSAGES permission"); | ||||
| 		if (body.tts && !permissions.has("SEND_TTS_MESSAGES")) throw new HTTPError("You are missing the SEND_TTS_MESSAGES permission"); | ||||
| 		permissions.hasThrow("SEND_MESSAGES"); | ||||
| 		if (body.tts) permissions.hasThrow("SEND_TTS_MESSAGES"); | ||||
| 		if (body.message_reference) { | ||||
| 			if (!permissions.has("READ_MESSAGE_HISTORY")) | ||||
| 				throw new HTTPError("You are missing the READ_MESSAGE_HISTORY permission to reply"); | ||||
| 			permissions.hasThrow("READ_MESSAGE_HISTORY"); | ||||
| 			if (body.message_reference.guild_id !== channel.guild_id) | ||||
| 				throw new HTTPError("You can only reference messages from this guild"); | ||||
| 		} | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| import { Router } from "express"; | ||||
| const router: Router = Router(); | ||||
| // TODO:
 | ||||
| 
 | ||||
| export default router; | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| import { Router } from "express"; | ||||
| const router: Router = Router(); | ||||
| // TODO:
 | ||||
| 
 | ||||
| export default router; | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| import { Router } from "express"; | ||||
| const router: Router = Router(); | ||||
| // TODO:
 | ||||
| 
 | ||||
| export default router; | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| import { Router } from "express"; | ||||
| const router: Router = Router(); | ||||
| // TODO:
 | ||||
| 
 | ||||
| export default router; | ||||
|  | ||||
| @ -1,4 +1,27 @@ | ||||
| import { Router } from "express"; | ||||
| import { check, Length } from "../../../util/instanceOf"; | ||||
| import { ChannelModel, getPermission, trimSpecial } from "@fosscord/server-util"; | ||||
| import { HTTPError } from "lambert-server"; | ||||
| import { isTextChannel } from "./messages/index"; | ||||
| 
 | ||||
| const router: Router = Router(); | ||||
| // TODO:
 | ||||
| 
 | ||||
| // TODO: use Image Data Type for avatar instead of String
 | ||||
| router.post("/", check({ name: new Length(String, 1, 80), $avatar: String }), async (req, res) => { | ||||
| 	const channel_id = req.params.channel_id; | ||||
| 	const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true, type: true }).exec(); | ||||
| 	if (!channel) throw new HTTPError("Channel not found", 404); | ||||
| 
 | ||||
| 	isTextChannel(channel.type); | ||||
| 	if (!channel.guild_id) throw new HTTPError("Not a guild channel", 400); | ||||
| 
 | ||||
| 	const permission = await getPermission(req.user_id, channel.guild_id); | ||||
| 	permission.hasThrow("MANAGE_WEBHOOKS"); | ||||
| 
 | ||||
| 	var { avatar, name } = req.body as { name: string; avatar?: string }; | ||||
| 	name = trimSpecial(name); | ||||
| 	if (name === "clyde") throw new HTTPError("Invalid name", 400); | ||||
| }); | ||||
| 
 | ||||
| export default router; | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import { Request, Response, Router } from "express"; | ||||
| import { BanModel, getPermission, GuildBanAddEvent, GuildBanRemoveEvent, GuildModel, toObject } from "fosscord-server-util"; | ||||
| import { BanModel, getPermission, GuildBanAddEvent, GuildBanRemoveEvent, GuildModel, toObject } from "@fosscord/server-util"; | ||||
| import { HTTPError } from "lambert-server"; | ||||
| import { getIpAdress } from "../../../middlewares/GlobalRateLimit"; | ||||
| import { BanCreateSchema } from "../../../schema/Ban"; | ||||
| @ -35,7 +35,7 @@ router.post("/:user_id", check(BanCreateSchema), async (req: Request, res: Respo | ||||
| 
 | ||||
| 	const banned_user = await getPublicUser(banned_user_id); | ||||
| 	const perms = await getPermission(req.user_id, guild_id); | ||||
| 	if (!perms.has("BAN_MEMBERS")) throw new HTTPError("You don't have the permission to ban members", 403); | ||||
| 	perms.hasThrow("BAN_MEMBERS"); | ||||
| 	if (req.user_id === banned_user_id) throw new HTTPError("You can't ban yourself", 400); | ||||
| 
 | ||||
| 	await removeMember(banned_user_id, guild_id); | ||||
| @ -69,9 +69,7 @@ router.delete("/:user_id", async (req: Request, res: Response) => { | ||||
| 	if (!guild) throw new HTTPError("Guild not found", 404); | ||||
| 
 | ||||
| 	const perms = await getPermission(req.user_id, guild_id); | ||||
| 	if (!perms.has("BAN_MEMBERS")) { | ||||
| 		throw new HTTPError("No permissions", 403); | ||||
| 	} | ||||
| 	perms.hasThrow("BAN_MEMBERS"); | ||||
| 
 | ||||
| 	await BanModel.deleteOne({ | ||||
| 		user_id: banned_user_id, | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import { Router } from "express"; | ||||
| import { ChannelCreateEvent, ChannelModel, ChannelType, GuildModel, Snowflake, toObject } from "fosscord-server-util"; | ||||
| import { ChannelCreateEvent, ChannelModel, ChannelType, GuildModel, Snowflake, toObject } from "@fosscord/server-util"; | ||||
| import { HTTPError } from "lambert-server"; | ||||
| import { ChannelModifySchema } from "../../../schema/Channel"; | ||||
| import { emitEvent } from "../../../util/Event"; | ||||
|  | ||||
| @ -12,7 +12,7 @@ import { | ||||
| 	RoleModel, | ||||
| 	toObject, | ||||
| 	UserModel, | ||||
| } from "fosscord-server-util"; | ||||
| } from "@fosscord/server-util"; | ||||
| import { HTTPError } from "lambert-server"; | ||||
| import { GuildUpdateSchema } from "../../../schema/Guild"; | ||||
| import { emitEvent } from "../../../util/Event"; | ||||
| @ -41,7 +41,7 @@ router.patch("/", check(GuildUpdateSchema), async (req: Request, res: Response) | ||||
| 	// TODO: guild update check image
 | ||||
| 
 | ||||
| 	const perms = await getPermission(req.user_id, guild_id); | ||||
| 	if (!perms.has("MANAGE_GUILD")) throw new HTTPError("You do not have the MANAGE_GUILD permission", 401); | ||||
| 	perms.hasThrow("MANAGE_GUILD"); | ||||
| 
 | ||||
| 	const guild = await GuildModel.findOneAndUpdate({ id: guild_id }, body) | ||||
| 		.populate({ path: "joined_at", match: { id: req.user_id } }) | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import { Request, Response, Router } from "express"; | ||||
| import { GuildModel, MemberModel, toObject } from "fosscord-server-util"; | ||||
| import { GuildModel, MemberModel, toObject } from "@fosscord/server-util"; | ||||
| import { HTTPError } from "lambert-server"; | ||||
| import { instanceOf, Length } from "../../../util/instanceOf"; | ||||
| import { PublicMemberProjection } from "../../../util/Member"; | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import { Router, Request, Response } from "express"; | ||||
| import { RoleModel, GuildModel, Snowflake, Guild } from "fosscord-server-util"; | ||||
| import { RoleModel, GuildModel, Snowflake, Guild } from "@fosscord/server-util"; | ||||
| import { HTTPError } from "lambert-server"; | ||||
| import { check } from "./../../util/instanceOf"; | ||||
| import { GuildCreateSchema } from "../../schema/Guild"; | ||||
| @ -9,6 +9,8 @@ import { addMember } from "../../util/Member"; | ||||
| 
 | ||||
| const router: Router = Router(); | ||||
| 
 | ||||
| //TODO: create default channel
 | ||||
| 
 | ||||
| router.post("/", check(GuildCreateSchema), async (req: Request, res: Response) => { | ||||
| 	const body = req.body as GuildCreateSchema; | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import { Router, Request, Response } from "express"; | ||||
| import { getPermission, InviteModel, toObject } from "fosscord-server-util"; | ||||
| import { getPermission, InviteModel, toObject } from "@fosscord/server-util"; | ||||
| import { HTTPError } from "lambert-server"; | ||||
| const router: Router = Router(); | ||||
| 
 | ||||
| @ -21,7 +21,8 @@ router.delete("/:invite_code", async (req: Request, res: Response) => { | ||||
| 	const { guild_id, channel_id } = invite; | ||||
| 	const perms = await getPermission(req.user_id, guild_id, channel_id); | ||||
| 
 | ||||
| 	if (!perms.has("MANAGE_GUILD") || !perms.has("MANAGE_CHANNELS")) throw new HTTPError("You aren't allow", 401); | ||||
| 	if (!perms.has("MANAGE_GUILD") && !perms.has("MANAGE_CHANNELS")) | ||||
| 		throw new HTTPError("You missing the MANAGE_GUILD or MANAGE_CHANNELS permission", 401); | ||||
| 
 | ||||
| 	await InviteModel.deleteOne({ code }).exec(); | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										19
									
								
								src/routes/users/#id/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/routes/users/#id/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| import { Router, Request, Response } from "express"; | ||||
| import { UserModel, toObject } from "@fosscord/server-util"; | ||||
| import { getPublicUser } from "../../../util/User"; | ||||
| import { HTTPError } from "lambert-server"; | ||||
| import { UserUpdateSchema } from "../../../schema/User"; | ||||
| import { check } from "../../../util/instanceOf"; | ||||
| 
 | ||||
| const router: Router = Router(); | ||||
| 
 | ||||
| router.get("/", async (req: Request, res: Response) => { | ||||
| 	const { id } = req.params; | ||||
| 	const user = await getPublicUser(id); | ||||
| 	if (!user) throw new HTTPError("User not found", 404); | ||||
| 
 | ||||
| 	res.json(user); | ||||
| }); | ||||
| 
 | ||||
| 
 | ||||
| export default router; | ||||
							
								
								
									
										69
									
								
								src/routes/users/@me/channels.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/routes/users/@me/channels.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | ||||
| import { | ||||
| 	Router, | ||||
| 	Request, | ||||
| 	Response | ||||
| } from "express"; | ||||
| import { | ||||
| 	ChannelModel, | ||||
| 	ChannelCreateEvent, | ||||
| 	DMChannel, | ||||
| 	UserModel, | ||||
| 	toObject, | ||||
| 	ChannelType, | ||||
| 	Snowflake | ||||
| } from "@fosscord/server-util"; | ||||
| import { | ||||
| 	HTTPError | ||||
| } from "lambert-server"; | ||||
| import { | ||||
| 	emitEvent | ||||
| } from "../../../util/Event"; | ||||
| import { | ||||
| 	getPublicUser | ||||
| } from "../../../util/User"; | ||||
| import { | ||||
| 	DmChannelCreateSchema | ||||
| } from "../../../schema/Channel"; | ||||
| import { | ||||
| 	check | ||||
| } from "../../../util/instanceOf"; | ||||
| 
 | ||||
| const router: Router = Router(); | ||||
| 
 | ||||
| router.get("/", async (req: Request, res: Response) => { | ||||
| 	const user = await UserModel.findOne({ | ||||
| 		id: req.user_id | ||||
| 	}, { | ||||
| 		guilds: true | ||||
| 	}).exec(); | ||||
| 	if (!user) throw new HTTPError("User not found", 404); | ||||
| 
 | ||||
| 	var testID = "829044530203328513"; //FOR TEST
 | ||||
| 
 | ||||
| 	var channels = await ChannelModel.find({ | ||||
| 		recipients: req.user_id, | ||||
| 		type: 1 | ||||
| 	}).exec(); | ||||
| 
 | ||||
| 	res.json(toObject(channels)); | ||||
| }); | ||||
| 
 | ||||
| router.post("/", check(DmChannelCreateSchema), async (req, res) => { | ||||
| 	const body = req.body as DmChannelCreateSchema; | ||||
| 
 | ||||
| 	const channel = { | ||||
| 		...body, | ||||
| 		owner_id: req.user_id, | ||||
| 		id: Snowflake.generate(), | ||||
| 		type: ChannelType.DM, | ||||
| 		created_at: new Date(), | ||||
| 	}; | ||||
| 	await new ChannelModel(channel).save(); | ||||
| 
 | ||||
| 	/*Event({ event: "CHANNEL_CREATE", data: channel } as ChannelCreateEvent);*/ | ||||
| 
 | ||||
| 
 | ||||
| 	res.json(channel); | ||||
| }); | ||||
| 
 | ||||
| export default router; | ||||
| @ -1,5 +1,5 @@ | ||||
| import { Router, Request, Response } from "express"; | ||||
| import { GuildModel, MemberModel, UserModel, GuildDeleteEvent, GuildMemberRemoveEvent, toObject } from "fosscord-server-util"; | ||||
| import { GuildModel, MemberModel, UserModel, GuildDeleteEvent, GuildMemberRemoveEvent, toObject } from "@fosscord/server-util"; | ||||
| import { HTTPError } from "lambert-server"; | ||||
| import { emitEvent } from "../../../util/Event"; | ||||
| import { getPublicUser } from "../../../util/User"; | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import { Router, Request, Response } from "express"; | ||||
| import { UserModel } from "fosscord-server-util"; | ||||
| import { UserModel } from "@fosscord/server-util"; | ||||
| import { HTTPError } from "lambert-server"; | ||||
| 
 | ||||
| const router: Router = Router(); | ||||
|  | ||||
| @ -1,3 +1,4 @@ | ||||
| import { ChannelType } from "@fosscord/server-util"; | ||||
| import { Length } from "../util/instanceOf"; | ||||
| 
 | ||||
| export const ChannelModifySchema = { | ||||
| @ -20,6 +21,24 @@ export const ChannelModifySchema = { | ||||
| 	$nsfw: Boolean, | ||||
| }; | ||||
| 
 | ||||
| export const DmChannelCreateSchema = { | ||||
| 	owner_id: String, | ||||
| 	$id: String, | ||||
|     $created_at: Date, | ||||
|     name: String, | ||||
|     type: Number, | ||||
| 	recipients: [String] | ||||
| } | ||||
| 
 | ||||
| export interface DmChannelCreateSchema { | ||||
| 	owner_id: String; | ||||
| 	id?: String; | ||||
|     created_at?: Date; | ||||
|     name: String; | ||||
|     type: Number; | ||||
| 	recipients: String[]; | ||||
| } | ||||
| 
 | ||||
| export interface ChannelModifySchema { | ||||
| 	name: string; | ||||
| 	type: number; | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import { ChannelSchema, GuildChannel } from "fosscord-server-util"; | ||||
| import { ChannelSchema, GuildChannel } from "@fosscord/server-util"; | ||||
| import { Length } from "../util/instanceOf"; | ||||
| 
 | ||||
| export const GuildCreateSchema = { | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import { Embed, EmbedImage } from "fosscord-server-util"; | ||||
| import { Embed, EmbedImage } from "@fosscord/server-util"; | ||||
| import { Length } from "../util/instanceOf"; | ||||
| 
 | ||||
| export const MessageCreateSchema = { | ||||
|  | ||||
							
								
								
									
										43
									
								
								src/schema/User.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/schema/User.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | ||||
| export const UserUpdateSchema = { | ||||
| 	id: String, | ||||
|     username: String, | ||||
|     discriminator: String, | ||||
|     avatar: String || null, | ||||
|     $phone: String, | ||||
|     desktop: Boolean, | ||||
|     mobile: Boolean, | ||||
|     premium: Boolean, | ||||
|     premium_type: Number, | ||||
|     bot: Boolean, | ||||
|     system: Boolean, | ||||
|     nsfw_allowed: Boolean, | ||||
|     mfa_enabled: Boolean, | ||||
|     created_at: Date, | ||||
|     verified: Boolean, | ||||
|     $email: String, | ||||
|     flags: BigInt, | ||||
|     public_flags: BigInt, | ||||
|     $guilds: [String], | ||||
| }; | ||||
| 
 | ||||
| export interface UserUpdateSchema { | ||||
| 	id: string; | ||||
|     username: string; | ||||
|     discriminator: string; | ||||
|     avatar: string | null; | ||||
|     phone?: string; | ||||
|     desktop: boolean; | ||||
|     mobile: boolean; | ||||
|     premium: boolean; | ||||
|     premium_type: number; | ||||
|     bot: boolean; | ||||
|     system: boolean; | ||||
|     nsfw_allowed: boolean; | ||||
|     mfa_enabled: boolean; | ||||
|     created_at: Date; | ||||
|     verified: boolean; | ||||
|     email?: string; | ||||
|     flags: bigint; | ||||
|     public_flags: bigint; | ||||
|     guilds: string[]; | ||||
| } | ||||
							
								
								
									
										33
									
								
								src/start.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/start.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| process.on("uncaughtException", console.error); | ||||
| process.on("unhandledRejection", console.error); | ||||
| 
 | ||||
| import "missing-native-js-functions"; | ||||
| import { config } from "dotenv"; | ||||
| config(); | ||||
| import { FosscordServer } from "./Server"; | ||||
| import cluster from "cluster"; | ||||
| import os from "os"; | ||||
| const cores = os.cpus().length; | ||||
| 
 | ||||
| if (cluster.isMaster && process.env.production == "true") { | ||||
| 	console.log(`Primary ${process.pid} is running`); | ||||
| 
 | ||||
| 	// Fork workers.
 | ||||
| 	for (let i = 0; i < cores; i++) { | ||||
| 		cluster.fork(); | ||||
| 	} | ||||
| 
 | ||||
| 	cluster.on("exit", (worker, code, signal) => { | ||||
| 		console.log(`worker ${worker.process.pid} died, restart worker`); | ||||
| 		cluster.fork(); | ||||
| 	}); | ||||
| } else { | ||||
| 	var port = Number(process.env.PORT); | ||||
| 	if (isNaN(port)) port = 1000; | ||||
| 
 | ||||
| 	const server = new FosscordServer({ port }); | ||||
| 	server.start().catch(console.error); | ||||
| 
 | ||||
| 	// @ts-ignore
 | ||||
| 	global.server = server; | ||||
| } | ||||
| @ -1,4 +1,4 @@ | ||||
| import { getPermission } from "fosscord-server-util"; | ||||
| import { getPermission } from "@fosscord/server-util"; | ||||
| 
 | ||||
| async function main() { | ||||
| 	const t = await getPermission("811642917432066048", "812327318532915201"); | ||||
|  | ||||
| @ -0,0 +1 @@ | ||||
| export {}; | ||||
| @ -1,4 +1,4 @@ | ||||
| import { Config, Snowflake } from "fosscord-server-util"; | ||||
| import { Config, Snowflake } from "@fosscord/server-util"; | ||||
| import crypto from "crypto"; | ||||
| import fs from "fs"; | ||||
| 
 | ||||
| @ -16,7 +16,7 @@ export default { | ||||
| 	setAll: Config.setAll, | ||||
| }; | ||||
| 
 | ||||
| export interface RateLimit { | ||||
| export interface RateLimitOptions { | ||||
| 	count: number; | ||||
| 	timespan: number; | ||||
| } | ||||
| @ -62,8 +62,8 @@ export interface DefaultOptions { | ||||
| 			}; | ||||
| 			routes: { | ||||
| 				auth?: { | ||||
| 					login?: RateLimit; | ||||
| 					register?: RateLimit; | ||||
| 					login?: RateLimitOptions; | ||||
| 					register?: RateLimitOptions; | ||||
| 				}; | ||||
| 				channel?: {}; | ||||
| 				// TODO: rate limit configuration for all routes
 | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import { Event, EventModel } from "fosscord-server-util"; | ||||
| import { Event, EventModel } from "@fosscord/server-util"; | ||||
| 
 | ||||
| export async function emitEvent(payload: Omit<Event, "created_at">) { | ||||
| 	const obj = { | ||||
|  | ||||
| @ -7,7 +7,7 @@ import { | ||||
| 	GuildModel, | ||||
| 	MemberModel, | ||||
| 	UserModel, | ||||
| } from "fosscord-server-util"; | ||||
| } from "@fosscord/server-util"; | ||||
| import { HTTPError } from "lambert-server"; | ||||
| import Config from "./Config"; | ||||
| import { emitEvent } from "./Event"; | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import { toObject, UserModel } from "fosscord-server-util"; | ||||
| import { toObject, UserModel } from "@fosscord/server-util"; | ||||
| import { HTTPError } from "lambert-server"; | ||||
| 
 | ||||
| export const PublicUserProjection = { | ||||
|  | ||||
| @ -34,6 +34,9 @@ export function FieldErrors(fields: Record<string, { code?: string; message: str | ||||
| 	); | ||||
| } | ||||
| 
 | ||||
| // TODO: implement Image data type: Data URI scheme that supports JPG, GIF, and PNG formats. An example Data URI format is: data:image/jpeg;base64,BASE64_ENCODED_JPEG_IMAGE_DATA
 | ||||
| // Ensure you use the proper content type (image/jpeg, image/png, image/gif) that matches the image data being provided.
 | ||||
| 
 | ||||
| export class FieldError extends Error { | ||||
| 	constructor(public code: string | number, public message: string, public errors?: any) { | ||||
| 		super(message); | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| { | ||||
| 	"include": ["src/**/*.ts"], | ||||
| 	"include": ["src/**/*.ts", "src/test/rethink_test.ts.disabled"], | ||||
| 	"compilerOptions": { | ||||
| 		/* Visit https://aka.ms/tsconfig.json to read more about this file */ | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Flam3rboy
						Flam3rboy