Merge branch 'feat-archives'

This commit is contained in:
Isaac 2024-11-16 02:55:18 +00:00
commit 8d0317c60e
No known key found for this signature in database
GPG Key ID: 17700D08381EA590
13 changed files with 149 additions and 16 deletions

View File

@ -4,7 +4,7 @@ const { randomBytes } = require('crypto');
const { short } = require('leeks.js'); const { short } = require('leeks.js');
const { join } = require('path'); const { join } = require('path');
const { files } = require('node-dir'); const { files } = require('node-dir');
const { PermissionsBitField } = require('discord.js'); const { getPrivilegeLevel } = require('./lib/users');
process.env.ORIGIN = process.env.HTTP_INTERNAL || process.env.HTTP_EXTERNAL; process.env.ORIGIN = process.env.HTTP_INTERNAL || process.env.HTTP_EXTERNAL;
@ -65,6 +65,33 @@ module.exports = async client => {
} }
}); });
fastify.decorate('isMember', async (req, res) => {
try {
const userId = req.user.id;
const guildId = req.params.guild;
const guild = client.guilds.cache.get(guildId);
if (!guild) {
return res.code(404).send({
error: 'Not Found',
message: 'The requested resource could not be found.',
statusCode: 404,
});
}
const guildMember = await guild.members.fetch(userId);
if (!guildMember) {
return res.code(403).send({
error: 'Forbidden',
message: 'You are not permitted for this action.',
statusCode: 403,
});
}
} catch (err) {
res.send(err);
}
});
fastify.decorate('isAdmin', async (req, res) => { fastify.decorate('isAdmin', async (req, res) => {
try { try {
const userId = req.user.id; const userId = req.user.id;
@ -79,7 +106,7 @@ module.exports = async client => {
}); });
} }
const guildMember = await guild.members.fetch(userId); const guildMember = await guild.members.fetch(userId);
const isAdmin = guildMember?.permissions.has(PermissionsBitField.Flags.ManageGuild) || client.supers.includes(userId); const isAdmin = await getPrivilegeLevel(guildMember) >= 2;
if (!isAdmin) { if (!isAdmin) {
return res.code(403).send({ return res.code(403).send({
error: 'Forbidden', error: 'Forbidden',

View File

@ -1,3 +1,6 @@
const { createHash } = require('crypto'); const { createHash } = require('crypto');
module.exports.md5 = str => createHash('md5').update(str).digest('hex'); module.exports.md5 = str => createHash('md5').update(str).digest('hex');
module.exports.msToMins = ms => Number((ms / 1000 / 60).toFixed(2)); module.exports.msToMins = ms => Number((ms / 1000 / 60).toFixed(2));
module.exports.iconURL = guildLike => guildLike.icon
? guildLike.client.rest.cdn.icon(guildLike.id, guildLike.icon)
: `https://api.dicebear.com/8.x/initials/png?seed=${encodeURIComponent(guildLike.name)}&size=96&backgroundType=gradientLinear&fontWeight=600`;

View File

@ -46,3 +46,23 @@ module.exports.isStaff = async (guild, userId) => {
const staffRoles = await client.keyv.get(`cache/guild-staff:${guild.id}`) || await updateStaffRoles(guild); const staffRoles = await client.keyv.get(`cache/guild-staff:${guild.id}`) || await updateStaffRoles(guild);
return staffRoles.some(r => guildMember.roles.cache.has(r)); return staffRoles.some(r => guildMember.roles.cache.has(r));
}; };
/**
*
* @param {import("discord.js")} member
* @returns {Promise<number>}
* - `4` = OPERATOR (SUPER)
* - `3` = GUILD_OWNER
* - `2` = GUILD_ADMIN
* - `1` = GUILD_STAFF
* - `0` = GUILD_MEMBER
* - `-1` = NONE (NOT A MEMBER)
*/
module.exports.getPrivilegeLevel = async member => {
if (!member) return -1;
else if (member.guild.client.supers.includes(member.id)) return 4;
else if (member.guild.ownerId === member.id) return 3;
else if (member.permissions.has(PermissionsBitField.Flags.ManageGuild)) return 2;
else if (await this.isStaff(member.guild, member.id)) return 1;
else return 0;
};

View File

@ -9,7 +9,7 @@ module.exports.delete = fastify => ({
const guild = client.guilds.cache.get(req.params.guild); const guild = client.guilds.cache.get(req.params.guild);
const categoryId = Number(req.params.category); const categoryId = Number(req.params.category);
const original = categoryId && await client.prisma.category.findUnique({ where: { id: categoryId } }); const original = categoryId && await client.prisma.category.findUnique({ where: { id: categoryId } });
if (!original || original.guildId !== guild.id) return res.status(404).send(new Error('Not Found')); if (!original || original.guildId !== guild.id) return res.status(400).send(new Error('Bad Request'));
const category = await client.prisma.category.delete({ where: { id: categoryId } }); const category = await client.prisma.category.delete({ where: { id: categoryId } });
await updateStaffRoles(guild); await updateStaffRoles(guild);
@ -58,7 +58,7 @@ module.exports.get = fastify => ({
where: { id: categoryId }, where: { id: categoryId },
}); });
if (!category || category.guildId !== guildId) return res.status(404).send(new Error('Not Found')); if (!category || category.guildId !== guildId) return res.status(400).send(new Error('Bad Request'));
return category; return category;
}, },
@ -118,7 +118,7 @@ module.exports.patch = fastify => ({
where: { id: categoryId }, where: { id: categoryId },
}); });
if (!original || original.guildId !== guildId) return res.status(404).send(new Error('Not Found')); if (!original || original.guildId !== guildId) return res.status(400).send(new Error('Bad Request'));
if (data.hasOwnProperty('id')) delete data.id; if (data.hasOwnProperty('id')) delete data.id;
if (data.hasOwnProperty('createdAt')) delete data.createdAt; if (data.hasOwnProperty('createdAt')) delete data.createdAt;

View File

@ -9,7 +9,7 @@ module.exports.delete = fastify => ({
const questionId = req.params.question; const questionId = req.params.question;
const original = questionId && await client.prisma.question.findUnique({ where: { id: questionId } }); const original = questionId && await client.prisma.question.findUnique({ where: { id: questionId } });
const category = categoryId && await client.prisma.category.findUnique({ where: { id: categoryId } }); const category = categoryId && await client.prisma.category.findUnique({ where: { id: categoryId } });
if (original?.categoryId !== categoryId || category.guildId !== guildId) return res.status(404).send(new Error('Not Found')); if (original?.categoryId !== categoryId || category.guildId !== guildId) return res.status(400).send(new Error('Bad Request'));
const question = await client.prisma.question.delete({ where: { id: questionId } }); const question = await client.prisma.question.delete({ where: { id: questionId } });
logAdminEvent(client, { logAdminEvent(client, {

View File

@ -1,4 +1,5 @@
/* eslint-disable no-underscore-dangle */ /* eslint-disable no-underscore-dangle */
const { iconURL } = require('../../../../../lib/misc');
const { const {
getAvgResolutionTime, getAvgResolutionTime,
getAvgResponseTime, getAvgResponseTime,
@ -37,7 +38,7 @@ module.exports.get = fastify => ({
cached = { cached = {
createdAt: settings.createdAt, createdAt: settings.createdAt,
id: guild.id, id: guild.id,
logo: guild.iconURL(), logo: iconURL(guild),
name: guild.name, name: guild.name,
stats: { stats: {
avgResolutionTime: ms(getAvgResolutionTime(closedTickets)), avgResolutionTime: ms(getAvgResolutionTime(closedTickets)),

View File

@ -8,7 +8,7 @@ module.exports.delete = fastify => ({
const guildId = req.params.guild; const guildId = req.params.guild;
const tagId = Number(req.params.tag); const tagId = Number(req.params.tag);
const original = tagId && await client.prisma.tag.findUnique({ where: { id: tagId } }); const original = tagId && await client.prisma.tag.findUnique({ where: { id: tagId } });
if (original.guildId !== guildId) return res.status(404).send(new Error('Not Found')); if (original.guildId !== guildId) return res.status(400).send(new Error('Bad Request'));
const tag = await client.prisma.tag.delete({ where: { id: tagId } }); const tag = await client.prisma.tag.delete({ where: { id: tagId } });
const cacheKey = `cache/guild-tags:${guildId}`; const cacheKey = `cache/guild-tags:${guildId}`;
@ -46,7 +46,7 @@ module.exports.get = fastify => ({
const tagId = Number(req.params.tag); const tagId = Number(req.params.tag);
const tag = await client.prisma.tag.findUnique({ where: { id: tagId } }); const tag = await client.prisma.tag.findUnique({ where: { id: tagId } });
if (!tag || tag.guildId !== guildId) return res.status(404).send(new Error('Not Found')); if (!tag || tag.guildId !== guildId) return res.status(400).send(new Error('Bad Request'));
return tag; return tag;
}, },
@ -64,7 +64,7 @@ module.exports.patch = fastify => ({
const original = req.params.tag && await client.prisma.tag.findUnique({ where: { id: tagId } }); const original = req.params.tag && await client.prisma.tag.findUnique({ where: { id: tagId } });
if (!original || original.guildId !== guildId) return res.status(404).send(new Error('Not Found')); if (!original || original.guildId !== guildId) return res.status(400).send(new Error('Bad Request'));
if (data.hasOwnProperty('id')) delete data.id; if (data.hasOwnProperty('id')) delete data.id;
if (data.hasOwnProperty('createdAt')) delete data.createdAt; if (data.hasOwnProperty('createdAt')) delete data.createdAt;

View File

@ -1,4 +1,5 @@
const { PermissionsBitField } = require('discord.js'); const { PermissionsBitField } = require('discord.js');
const { iconURL } = require('../../../../lib/misc');
module.exports.get = fastify => ({ module.exports.get = fastify => ({
handler: async (req, res) => { handler: async (req, res) => {
@ -10,7 +11,14 @@ module.exports.get = fastify => ({
.map(guild => ({ .map(guild => ({
added: client.guilds.cache.has(guild.id), added: client.guilds.cache.has(guild.id),
id: guild.id, id: guild.id,
logo: `https://cdn.discordapp.com/icons/${guild.id}/${guild.icon}.webp`, logo: iconURL(
client.guilds.cache.get(guild.id) ||
{
client,
icon: guild.icon,
id: guild.id,
},
),
name: guild.name, name: guild.name,
})), })),
); );

View File

@ -0,0 +1,17 @@
const { getPrivilegeLevel } = require('../../../../lib/users');
const { iconURL } = require('../../../../lib/misc');
module.exports.get = fastify => ({
handler: async (req, res) => {
const { client } = req.routeOptions.config;
const guild = client.guilds.cache.get(req.params.guild);
res.send({
id: guild.id,
logo: iconURL(guild),
name: guild.name,
privilegeLevel: await getPrivilegeLevel(await guild.members.fetch(req.user.id)),
});
},
onRequest: [fastify.authenticate, fastify.isMember],
});

View File

@ -0,0 +1,18 @@
module.exports.get = fastify => ({
handler: async (req, res) => {
const { client } = req.routeOptions.config;
/** @type {import("@prisma/client").PrismaClient} */
const prisma = client.prisma;
const guild = client.guilds.cache.get(req.params.guild);
res.send(
await prisma.ticket.findMany({
where: {
createdById: req.user.id,
guildId: guild.id,
},
}),
);
},
onRequest: [fastify.authenticate, fastify.isMember],
});

View File

@ -0,0 +1,25 @@
const { getPrivilegeLevel } = require('../../../lib/users');
const { iconURL } = require('../../../lib/misc');
module.exports.get = fastify => ({
handler: async (req, res) => {
const { client } = req.routeOptions.config;
const guilds = await (await fetch('https://discordapp.com/api/users/@me/guilds', { headers: { 'Authorization': `Bearer ${req.user.accessToken}` } })).json();
res.send(
await Promise.all(
guilds
.filter(partialGuild => client.guilds.cache.has(partialGuild.id))
.map(async partialGuild => {
const guild = client.guilds.cache.get(partialGuild.id);
return {
id: guild.id,
logo: iconURL(guild),
name: guild.name,
privilegeLevel: await getPrivilegeLevel(await guild.members.fetch(req.user.id)),
};
}),
),
);
},
onRequest: [fastify.authenticate],
});

View File

@ -19,7 +19,6 @@ module.exports.get = () => ({
const token = this.jwt.sign({ const token = this.jwt.sign({
accessToken: data.access_token, accessToken: data.access_token,
avatar: user.avatar, avatar: user.avatar,
discriminator: user.discriminator,
expiresAt: Date.now() + (data.expires_in * 1000), expiresAt: Date.now() + (data.expires_in * 1000),
id: user.id, id: user.id,
locale: user.locale, locale: user.locale,
@ -30,9 +29,16 @@ module.exports.get = () => ({
httpOnly: true, httpOnly: true,
maxAge: data.expires_in, maxAge: data.expires_in,
path: '/', path: '/',
sameSite: 'Lax', sameSite: 'Strict',
secure: false, secure: false,
}); });
return res.redirect(redirect, 303); res.header('Content-Type', 'text/html');
return res.send(`
<!DOCTYPE html>
<html>
<head><meta http-equiv="refresh" content="0; url='${redirect}'"></head>
<body></body>
</html>
`);
}, },
}); });

View File

@ -18,9 +18,17 @@ module.exports.get = fastify => ({
domain, domain,
httpOnly: true, httpOnly: true,
path: '/', path: '/',
sameSite: 'Lax', sameSite: 'Strict',
secure: false, secure: false,
}).send('The token has been revoked.'); });
res.header('Content-Type', 'text/html');
return res.send(`
<!DOCTYPE html>
<html>
<head><meta http-equiv="refresh" content="0; url='/'"></head>
<body></body>
</html>
`);
}, },
onRequest: [fastify.authenticate], onRequest: [fastify.authenticate],
}); });