DiscordTickets/src/http.js

169 lines
4.4 KiB
JavaScript
Raw Normal View History

2022-05-06 02:01:19 +03:00
const fastify = require('fastify')();
2022-05-06 23:17:53 +03:00
const oauth = require('@fastify/oauth2');
2022-05-06 02:12:38 +03:00
const { short } = require('leeks.js');
2022-05-06 02:01:19 +03:00
const { join } = require('path');
2022-07-18 15:34:29 +03:00
const { files } = require('node-dir');
2022-05-06 02:01:19 +03:00
2022-09-06 22:46:18 +03:00
process.env.PUBLIC_HOST = process.env.API_EXTERNAL; // the SvelteKit app expects `PUBLIC_HOST`
2022-09-06 22:30:28 +03:00
2022-09-06 22:46:18 +03:00
module.exports = async client => {
2022-09-04 21:55:32 +03:00
// cors plugin
2022-07-16 01:19:42 +03:00
fastify.register(require('@fastify/cors'), {
credentials: true,
methods: ['DELETE', 'GET', 'PATCH', 'PUT', 'POST'],
origin: true,
});
2022-05-06 23:17:53 +03:00
// oauth2 plugin
fastify.register(oauth, {
2022-09-06 22:46:18 +03:00
callbackUri: `${process.env.API_EXTERNAL}/auth/callback`,
2022-05-06 23:17:53 +03:00
credentials: {
auth: oauth.DISCORD_CONFIGURATION,
client: {
id: client.user.id,
secret: process.env.DISCORD_SECRET,
},
},
name: 'discord',
scope: ['identify'],
startRedirectPath: '/auth/login',
});
// cookies plugin
fastify.register(require('@fastify/cookie'));
// jwt plugin
fastify.register(require('@fastify/jwt'), {
cookie: {
cookieName: 'token',
signed: false,
},
2022-07-16 01:19:42 +03:00
secret: process.env.ENCRYPTION_KEY,
2022-05-06 23:17:53 +03:00
});
// auth
fastify.decorate('authenticate', async (req, res) => {
try {
const data = await req.jwtVerify();
2022-07-16 01:19:42 +03:00
if (data.payload.expiresAt < Date.now()) {
return res.code(401).send({
error: 'Unauthorised',
message: 'You are not authenticated.',
statusCode: 401,
});
}
2022-05-06 23:17:53 +03:00
} catch (err) {
res.send(err);
}
});
2022-05-07 20:28:38 +03:00
fastify.decorate('isAdmin', async (req, res) => {
try {
const userId = req.user.payload.id;
const guildId = req.params.guild;
const guild = client.guilds.cache.get(guildId);
2022-07-16 01:19:42 +03:00
if (!guild) {
return res.code(404).send({
error: 'Not Found',
message: 'The requested resource could not be found.',
statusCode: 404,
2022-05-07 20:28:38 +03:00
2022-07-16 01:19:42 +03:00
});
}
const guildMember = await guild.members.fetch(userId);
2022-07-18 23:53:17 +03:00
const isAdmin = guildMember?.permissions.has('MANAGE_GUILD') || client.supers.includes(userId);
if (!isAdmin) {
2022-07-16 01:19:42 +03:00
return res.code(403).send({
error: 'Forbidden',
message: 'You are not permitted for this action.',
statusCode: 403,
2022-05-07 20:28:38 +03:00
});
}
} catch (err) {
res.send(err);
}
});
2022-07-18 23:53:17 +03:00
// body processing
fastify.addHook('preHandler', (req, res, done) => {
if (req.body && typeof req.body === 'object') {
for (const prop in req.body) {
if (typeof req.body[prop] === 'string') {
req.body[prop] = req.body[prop].trim();
}
}
}
done();
});
2022-05-06 23:17:53 +03:00
// logging
2022-05-06 02:12:38 +03:00
fastify.addHook('onResponse', (req, res, done) => {
done();
const status = (res.statusCode >= 500
? '&4'
: res.statusCode >= 400
? '&6'
: res.statusCode >= 300
? '&3'
: res.statusCode >= 200
? '&2'
: '&f') + res.statusCode;
2022-09-04 21:55:32 +03:00
let responseTime = res.getResponseTime().toFixed(2);
responseTime = (responseTime >= 20
2022-05-06 02:12:38 +03:00
? '&c'
2022-09-04 21:55:32 +03:00
: responseTime >= 5
2022-05-06 02:12:38 +03:00
? '&e'
2022-09-04 21:55:32 +03:00
: '&a') + responseTime + 'ms';
2022-09-06 22:46:18 +03:00
client.log.info.http(short(`API ${req.ip} ${req.method} ${req.routerPath ?? '*'} &m-+>&r ${status}&b in ${responseTime}`));
2022-05-06 23:17:53 +03:00
done();
2022-05-06 02:12:38 +03:00
});
2022-05-06 23:17:53 +03:00
fastify.addHook('onError', async (req, res, err) => client.log.error.http(err));
// route loading
2022-05-06 02:01:19 +03:00
const dir = join(__dirname, '/routes');
2022-09-06 22:46:18 +03:00
2022-07-18 15:34:29 +03:00
files(dir, {
exclude: /^\./,
match: /.js$/,
sync: true,
}).forEach(file => {
const path = file
.substring(0, file.length - 3) // remove `.js`
.substring(dir.length) // remove higher directories
2022-08-26 01:35:17 +03:00
.replace(/\\/g, '/') // replace `\` with `/` because Windows is stupid
2022-07-18 15:34:29 +03:00
.replace(/\[(\w+)\]/gi, ':$1') // convert [] to :
.replace('/index', '') || '/'; // remove index
const route = require(file);
2022-09-06 22:46:18 +03:00
2022-07-18 15:34:29 +03:00
Object.keys(route).forEach(method => fastify.route({
config: { client },
method: method.toUpperCase(),
path,
...route[method](fastify),
})); // register route
});
2022-05-06 02:01:19 +03:00
2022-09-06 22:46:18 +03:00
// start server
fastify.listen({ port: process.env.API_BIND }, (err, addr) => {
if (err) client.log.error.http(err);
else client.log.success.http(`API Listening at ${addr}`);
});
2022-09-06 22:30:28 +03:00
const express = require('express')();
const { handler } = await import('@discord-tickets/settings/build/handler.js');
express.get('/api/client', (req, res) => {
res.end('ok');
});
2022-09-06 22:46:18 +03:00
express.use((req, res, next) => {
next();
// req.route?.path ?? '*'
client.log.verbose.http(short(`SETTINGS ${req.ip} ${req.method} ${req.path}`)); // verbose because little information & SvelteKit is very spammy (lots of routes)
});
2022-09-06 22:30:28 +03:00
express.use(handler);
2022-09-06 22:46:18 +03:00
express.listen(process.env.SETTINGS_BIND, () => {
client.log.success.http(`SETTINGS Listening at ${process.env.SETTINGS_BIND}`);
2022-07-18 15:34:29 +03:00
});
2022-05-06 02:01:19 +03:00
};