.
This commit is contained in:
parent
43f721e0b7
commit
7e4cc4b1ad
@ -1,10 +1,14 @@
|
||||
import Users from '../data/users';
|
||||
import { REST } from '../rest/server';
|
||||
|
||||
interface Deps {
|
||||
export interface Deps {
|
||||
rest: REST;
|
||||
users: Users;
|
||||
}
|
||||
|
||||
export const deps: Deps = {
|
||||
const deps: Deps = {
|
||||
rest: new REST(),
|
||||
users: new Users(),
|
||||
};
|
||||
export default deps;
|
||||
|
||||
global['deps'] = deps;
|
21
backend/src/rest/functions/apply-error-handling.ts
Normal file
21
backend/src/rest/functions/apply-error-handling.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import { Application } from 'express-serve-static-core';
|
||||
import { APIError } from '../modules/api-error';
|
||||
|
||||
export default (app: Application, prefix: string) => {
|
||||
app.all(`${prefix}/*`, (req, res, next) => next(new APIError(404)));
|
||||
|
||||
app.use(`/`, () => {
|
||||
throw new TypeError('Invalid API version number');
|
||||
});
|
||||
|
||||
app.use((error: APIError, req: Request, res: Response, next: NextFunction) => {
|
||||
if (res.headersSent)
|
||||
return next(error);
|
||||
|
||||
const code = error.code || 400;
|
||||
return res
|
||||
.status(code)
|
||||
.json({ message: error.message });
|
||||
});
|
||||
}
|
21
backend/src/rest/functions/apply-middleware.ts
Normal file
21
backend/src/rest/functions/apply-middleware.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { Application } from 'express-serve-static-core';
|
||||
import bodyParser from 'body-parser';
|
||||
import { Strategy as LocalStrategy } from 'passport-local';
|
||||
import passport from 'passport';
|
||||
import cors from 'cors';
|
||||
import { User } from '../../data/models/user';
|
||||
import rateLimiter from '../modules/rate-limiter';
|
||||
|
||||
export default (app: Application) => {
|
||||
passport.use(new LocalStrategy(
|
||||
{ usernameField: 'email' },
|
||||
(User as any).authenticate(),
|
||||
));
|
||||
passport.serializeUser((User as any).serializeUser());
|
||||
passport.deserializeUser((User as any).deserializeUser());
|
||||
|
||||
app.use(cors());
|
||||
app.use(bodyParser.json());
|
||||
app.use(passport.initialize());
|
||||
app.use(rateLimiter);
|
||||
}
|
21
backend/src/rest/functions/apply-routes.ts
Normal file
21
backend/src/rest/functions/apply-routes.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { Application } from 'express-serve-static-core';
|
||||
import { router as apiRoutes } from '../routes/api-routes';
|
||||
import { router as authRoutes } from '../routes/auth-routes';
|
||||
import { router as channelsRoutes } from '../routes/channel-routes';
|
||||
import { router as guildsRoutes } from '../routes/guilds-routes';
|
||||
import { router as usersRoutes } from '../routes/users-routes';
|
||||
import { router as invitesRoutes } from '../routes/invites-routes';
|
||||
import { resolve } from 'path';
|
||||
import express from 'express';
|
||||
|
||||
export default (app: Application, prefix: string) => {
|
||||
app.use(`/assets`, express.static(resolve('./assets')));
|
||||
app.use(`${prefix}`, apiRoutes);
|
||||
|
||||
app.use(`${prefix}/auth`, authRoutes);
|
||||
app.use(`${prefix}/invites`, invitesRoutes);
|
||||
// app.use(`${prefix}/dev`, devRoutes);
|
||||
app.use(`${prefix}/channels`, channelsRoutes);
|
||||
app.use(`${prefix}/guilds`, guildsRoutes);
|
||||
app.use(`${prefix}/users`, usersRoutes);
|
||||
}
|
6
backend/src/rest/middleware/update-guild.ts
Normal file
6
backend/src/rest/middleware/update-guild.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
|
||||
export default async (req: Request, res: Response, next: NextFunction) => {
|
||||
res.locals.guild = await deps.guilds.get(req.params.id);
|
||||
return next();
|
||||
}
|
12
backend/src/rest/middleware/update-user.ts
Normal file
12
backend/src/rest/middleware/update-user.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
|
||||
export default async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const token = req.get('Authorization') as string;
|
||||
const id = await deps.users.idFromToken(token);
|
||||
|
||||
res.locals.user = await deps.users.getSelf(id);
|
||||
} finally {
|
||||
return next();
|
||||
}
|
||||
}
|
9
backend/src/rest/middleware/validate-guild-exists.ts
Normal file
9
backend/src/rest/middleware/validate-guild-exists.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import { Guild } from '../../data/models/guild';
|
||||
|
||||
export default async (req: Request, res: Response, next: NextFunction) => {
|
||||
const exists = await Guild.exists({ _id: req.params.id });
|
||||
return (exists)
|
||||
? next()
|
||||
: res.status(404).json({ message: 'Guild does not exist' });
|
||||
}
|
9
backend/src/rest/middleware/validate-guild-owner.ts
Normal file
9
backend/src/rest/middleware/validate-guild-owner.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import { APIError } from '../modules/api-error';
|
||||
|
||||
export default async (req: Request, res: Response, next: NextFunction) => {
|
||||
const userOwnsGuild = res.locals.guild.ownerId === res.locals.user.id;
|
||||
if (userOwnsGuild)
|
||||
return next();
|
||||
throw new APIError(401, 'You do not own this guild!');
|
||||
}
|
19
backend/src/rest/middleware/validate-has-permission.ts
Normal file
19
backend/src/rest/middleware/validate-has-permission.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import { GuildDocument } from '../../data/models/guild';
|
||||
import { PermissionTypes } from '../../types/permission-types';
|
||||
import { APIError } from '../modules/api-error';
|
||||
|
||||
export default (permission: PermissionTypes.Permission) =>
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
const guild: GuildDocument = res.locals.guild;
|
||||
const members = await deps.guilds.getMembers(guild.id);
|
||||
const member = members.find(m => m.userId === res.locals.user.id);
|
||||
if (!member)
|
||||
throw new APIError(401, 'You are not a guild member');
|
||||
|
||||
const isOwner = guild.ownerId === res.locals.user.id;
|
||||
const hasPerm = await deps.roles.hasPermission(guild, member, permission);
|
||||
if (hasPerm || isOwner) return next();
|
||||
|
||||
throw new APIError(401, `Missing Permissions: ${permission}`);
|
||||
};
|
8
backend/src/rest/middleware/validate-user.ts
Normal file
8
backend/src/rest/middleware/validate-user.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import { APIError } from '../modules/api-error';
|
||||
|
||||
export default async (req: Request, res: Response, next: NextFunction) => {
|
||||
if (res.locals.user)
|
||||
return next();
|
||||
throw new APIError(401, 'User not logged in');
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
import Guilds from '../../data/guilds';
|
||||
import { Guild, GuildDocument } from '../../data/models/guild';
|
||||
import Roles from '../../data/roles';
|
||||
import Users from '../../data/users';
|
||||
import Deps from '../../utils/deps';
|
||||
import { APIError } from './api-error';
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import { PermissionTypes } from '../../types/permission-types';
|
||||
|
||||
const guilds = Deps.get<Guilds>(Guilds);
|
||||
const roles = Deps.get<Roles>(Roles);
|
||||
const users = Deps.get<Users>(Users);
|
||||
|
||||
export async function updateUser(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const token = req.get('Authorization') as string;
|
||||
const id = await users.idFromToken(token);
|
||||
|
||||
res.locals.user = await users.getSelf(id);
|
||||
} finally {
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
export function validateUser(req: Request, res: Response, next: NextFunction) {
|
||||
if (res.locals.user)
|
||||
return next();
|
||||
throw new APIError(401, 'User not logged in');
|
||||
}
|
||||
|
||||
export async function updateGuild(req: Request, res: Response, next: NextFunction) {
|
||||
res.locals.guild = await guilds.get(req.params.id);
|
||||
return next();
|
||||
}
|
||||
|
||||
export async function validateGuildExists(req: Request, res: Response, next: NextFunction) {
|
||||
const exists = await Guild.exists({ _id: req.params.id });
|
||||
return (exists)
|
||||
? next()
|
||||
: res.status(404).json({ message: 'Guild does not exist' });
|
||||
}
|
||||
|
||||
export async function validateGuildOwner(req: Request, res: Response, next: NextFunction) {
|
||||
const userOwnsGuild = res.locals.guild.ownerId === res.locals.user.id;
|
||||
if (userOwnsGuild)
|
||||
return next();
|
||||
throw new APIError(401, 'You do not own this guild!');
|
||||
}
|
||||
|
||||
export function validateHasPermission(permission: PermissionTypes.Permission) {
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
const guild: GuildDocument = res.locals.guild;
|
||||
const members = await guilds.getMembers(guild.id);
|
||||
const member = members.find(m => m.userId === res.locals.user.id);
|
||||
if (!member)
|
||||
throw new APIError(401, 'You are not a guild member');
|
||||
|
||||
const isOwner = guild.ownerId === res.locals.user.id;
|
||||
const hasPerm = await roles.hasPermission(guild, member, permission);
|
||||
if (hasPerm || isOwner) return next();
|
||||
|
||||
throw new APIError(401, `Missing Permissions: ${permission}`);
|
||||
};
|
||||
}
|
@ -1,81 +1,22 @@
|
||||
import express, { NextFunction, Request, Response } from 'express';
|
||||
import express from 'express';
|
||||
import 'express-async-errors';
|
||||
|
||||
import bodyParser from 'body-parser';
|
||||
import { Strategy as LocalStrategy } from 'passport-local';
|
||||
import passport from 'passport';
|
||||
import { router as apiRoutes } from './routes/api-routes';
|
||||
import { router as authRoutes } from './routes/auth-routes';
|
||||
import { router as channelsRoutes } from './routes/channel-routes';
|
||||
import { router as guildsRoutes } from './routes/guilds-routes';
|
||||
import { router as usersRoutes } from './routes/users-routes';
|
||||
import { router as invitesRoutes } from './routes/invites-routes';
|
||||
import { User } from '../data/models/user';
|
||||
import cors from 'cors';
|
||||
import { resolve } from 'path';
|
||||
import Deps from '../utils/deps';
|
||||
import { WebSocket } from '../ws/websocket';
|
||||
import { APIError } from './modules/api-error';
|
||||
import rateLimiter from './modules/rate-limiter';
|
||||
import applyMiddleware from './functions/apply-middleware';
|
||||
import applyRoutes from './functions/apply-routes';
|
||||
import applyErrorHandling from './functions/apply-error-handling';
|
||||
|
||||
export class REST {
|
||||
public app = express();
|
||||
private prefix = `/v2`;
|
||||
|
||||
constructor(private ws = Deps.get<WebSocket>(WebSocket)) {
|
||||
this.setupMiddleware();
|
||||
this.setupRoutes();
|
||||
this.setupErrorHandling();
|
||||
this.listen();
|
||||
}
|
||||
|
||||
private setupMiddleware() {
|
||||
passport.use(new LocalStrategy(
|
||||
{ usernameField: 'email' },
|
||||
(User as any).authenticate(),
|
||||
));
|
||||
passport.serializeUser((User as any).serializeUser());
|
||||
passport.deserializeUser((User as any).deserializeUser());
|
||||
|
||||
this.app.use(cors());
|
||||
this.app.use(bodyParser.json());
|
||||
this.app.use(passport.initialize());
|
||||
this.app.use(rateLimiter);
|
||||
}
|
||||
|
||||
private setupRoutes() {
|
||||
this.app.use(`/assets`, express.static(resolve('./assets')));
|
||||
this.app.use(`${this.prefix}`, apiRoutes);
|
||||
const app = express();
|
||||
const prefix = `/v2`;
|
||||
|
||||
this.app.use(`${this.prefix}/auth`, authRoutes);
|
||||
this.app.use(`${this.prefix}/invites`, invitesRoutes);
|
||||
// this.app.use(`${this.prefix}/dev`, devRoutes);
|
||||
this.app.use(`${this.prefix}/channels`, channelsRoutes);
|
||||
this.app.use(`${this.prefix}/guilds`, guildsRoutes);
|
||||
this.app.use(`${this.prefix}/users`, usersRoutes);
|
||||
}
|
||||
applyMiddleware(app);
|
||||
applyRoutes(app, prefix);
|
||||
applyErrorHandling(app, prefix);
|
||||
|
||||
private setupErrorHandling() {
|
||||
this.app.all(`${this.prefix}/*`, (req, res, next) => next(new APIError(404)));
|
||||
|
||||
this.app.use(`/`, () => {
|
||||
throw new TypeError('Invalid API version number');
|
||||
});
|
||||
|
||||
this.app.use((error: APIError, req: Request, res: Response, next: NextFunction) => {
|
||||
if (res.headersSent)
|
||||
return next(error);
|
||||
|
||||
const code = error.code || 400;
|
||||
return res
|
||||
.status(code)
|
||||
.json({ message: error.message });
|
||||
});
|
||||
}
|
||||
|
||||
private listen() {
|
||||
const port = process.env.PORT || 8080;
|
||||
const server = this.app.listen(port, async () => {
|
||||
const server = app.listen(port, async () => {
|
||||
log.info(`API is running on port ${port}`);
|
||||
await this.ws.init(server);
|
||||
});
|
||||
|
1
types/global.d.ts
vendored
1
types/global.d.ts
vendored
@ -1,3 +1,4 @@
|
||||
export declare global {
|
||||
const log: import('winston').Logger;
|
||||
const deps: import('../modules/deps').Deps;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user