diff --git a/api/db/index.ts b/api/db/index.ts new file mode 100644 index 0000000..cd01b1d --- /dev/null +++ b/api/db/index.ts @@ -0,0 +1,14 @@ +import mysql from "mysql2"; + +// Create a MySQL connection pool +export const pool = mysql.createPool({ + host: process.env.MYSQL_ADDRESS as string, + port: parseInt(process.env.MYSQL_PORT as string), + user: process.env.MYSQL_USER as string, + password: process.env.MYSQL_PASSWORD as string, + database: process.env.MYSQL_DATABASE as string, +}); + +export * from './init'; +export * from './queries/guilds'; +export * from './queries/users'; diff --git a/api/db/init.ts b/api/db/init.ts new file mode 100644 index 0000000..14e121c --- /dev/null +++ b/api/db/init.ts @@ -0,0 +1,79 @@ +import { pool } from "."; + +export async function initTables() { + const createGuildsTable = ` + CREATE TABLE IF NOT EXISTS guilds ( + id VARCHAR(255) NOT NULL PRIMARY KEY, + name VARCHAR(255), + icon VARCHAR(255), + members INT, + updates_enabled BOOLEAN DEFAULT FALSE, + updates_channel JSON + ) + `; + const createUsersTable = ` + CREATE TABLE IF NOT EXISTS users ( + id VARCHAR(255) NOT NULL, + guild_id VARCHAR(255) NOT NULL, + name VARCHAR(255), + nickname VARCHAR(255), + pfp VARCHAR(255), + xp INT DEFAULT 0, + level INT DEFAULT 0, + xp_needed_next_level INT, + progress_next_level DECIMAL(6, 2), + PRIMARY KEY (id, guild_id), + FOREIGN KEY (guild_id) REFERENCES guilds(id) + ) + `; + const createRolesTable = ` + CREATE TABLE IF NOT EXISTS roles ( + id VARCHAR(255) NOT NULL PRIMARY KEY, + guild_id VARCHAR(255) NOT NULL, + name VARCHAR(255), + level INT NOT NULL, + FOREIGN KEY (guild_id) REFERENCES guilds(id) + ) + ` + const createUpdatesTable = ` + CREATE TABLE IF NOT EXISTS updates ( + guild_id VARCHAR(255) NOT NULL PRIMARY KEY, + channel_id VARCHAR(255) NOT NULL, + enabled BOOLEAN DEFAULT FALSE, + FOREIGN KEY (guild_id) REFERENCES guilds(id) + ) + ` + + pool.query(createGuildsTable, (err, results) => { + if (err) { + console.error("Error creating guilds table:", err); + } else { + console.log("Guilds table created:", results); + } + }); + + pool.query(createUsersTable, (err, results) => { + if (err) { + console.error("Error creating users table:", err); + } else { + console.log("Users table created:", results); + } + }); + + + pool.query(createRolesTable, (err, results) => { + if (err) { + console.error("Error creating roles table:", err); + } else { + console.log("Roles table created:", results); + } + }); + + pool.query(createUpdatesTable, (err, results) => { + if (err) { + console.error("Error creating updates table:", err); + } else { + console.log("Updates table created:", results); + } + }); +} diff --git a/api/db/queries/guilds.ts b/api/db/queries/guilds.ts new file mode 100644 index 0000000..1b6ed93 --- /dev/null +++ b/api/db/queries/guilds.ts @@ -0,0 +1,75 @@ +import type { QueryError } from "mysql2"; +import { pool } from ".."; + +export interface Guild { + id: string; + name: string; + icon: string; + members: number; + updates_enabled: boolean; + updates_channel: string; +} + +export async function getGuild(guildId: string): Promise<[QueryError | null, Guild | null]> { + return new Promise((resolve, reject) => { + pool.query("SELECT * FROM guilds WHERE id = ?", [guildId], (err, results) => { + if (err) { + reject([err, null]); + } else { + resolve([null, (results as Guild[])[0]]); + } + }); + }); +} + +export async function updateGuild(guild: Guild): Promise<[QueryError, null] | [null, Guild[]]> { + return new Promise((resolve, reject) => { + pool.query( + ` + INSERT INTO guilds (id, name, icon, members, updates_enabled, updates_channel) + VALUES (?, ?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + name = VALUES(name), + icon = VALUES(icon), + members = VALUES(members), + updates_enabled = VALUES(updates_enabled), + updates_channel = VALUES(updates_channel) + `, + [ + guild.id, + guild.name, + guild.icon, + guild.members, + guild.updates_enabled, + guild.updates_channel, + ], + (err, results) => { + if (err) { + reject([err, null]); + } else { + resolve([null, results as Guild[]]); + } + }, + ); + }); +} + +interface BotInfo { + total_guilds: number; + total_members: number; +} + +export async function getBotInfo(): Promise<[QueryError | null, BotInfo | null]> { + return new Promise((resolve, reject) => { + pool.query("SELECT COUNT(*) AS total_guilds, SUM(members) AS total_members FROM guilds", (err, results) => { + if (err) { + reject([err, null]); + } else { + resolve([null, { + total_guilds: (results as BotInfo[])[0].total_guilds, + total_members: (results as BotInfo[])[0].total_members ?? 0, + }]); + } + }) + }) +} diff --git a/api/db/queries/users.ts b/api/db/queries/users.ts new file mode 100644 index 0000000..b3cc2e3 --- /dev/null +++ b/api/db/queries/users.ts @@ -0,0 +1,38 @@ +import type { QueryError } from "mysql2"; +import { pool } from ".."; + +export interface User { + id: string; + guild_id: string; + name: string; + nickname: string; + pfp: string; + xp: number; + level: number; + xp_needed_next_level: number; + progress_next_level: number; +} + +export async function getUsers(guildId: string): Promise<[QueryError, null] | [null, User[]]> { + return new Promise((resolve, reject) => { + pool.query("SELECT * FROM users WHERE guild_id = ? ORDER BY xp DESC", [guildId], (err, results) => { + if (err) { + reject([err, null]); + } else { + resolve([null, (results as User[])]); + } + }); + }); +} + +export async function getUser(userId: string, guildId: string): Promise<[QueryError, null] | [null, User | null]> { + return new Promise((resolve, reject) => { + pool.query("SELECT * FROM users WHERE id = ? AND guild_id = ?", [userId, guildId], (err, results) => { + if (err) { + reject([err, null]); + } else { + resolve([null, (results as User[])[0]]); + } + }); + }); +} diff --git a/api/index.ts b/api/index.ts index ef57d6e..c9537d5 100644 --- a/api/index.ts +++ b/api/index.ts @@ -1,449 +1,291 @@ -import express from 'express'; -import cors from 'cors'; -import mysql from 'mysql2'; -import path from 'path'; +import express, { type NextFunction, type Request, type Response } from "express"; +import cors from "cors"; +import path from "path"; +import { getBotInfo, getGuild, getUser, getUsers, initTables, pool, updateGuild } from "./db"; const app = express(); const PORT = 18103; app.use(cors()); app.use(express.json()); -app.use(express.static(path.join(__dirname, 'public'))); -app.set('view engine', 'ejs'); -app.set('views', path.join(__dirname, 'views')); - -// Create a MySQL connection pool -const pool = mysql.createPool({ - host: process.env.MYSQL_ADDRESS as string, - port: parseInt(process.env.MYSQL_PORT as string), - user: process.env.MYSQL_USER as string, - password: process.env.MYSQL_PASSWORD as string, - database: process.env.MYSQL_DATABASE as string, -}); - -// Create the basic information tables -async function initInfoTables() { - const createUpdatesTable = ` - CREATE TABLE IF NOT EXISTS info_updates ( - guild_id VARCHAR(255) NOT NULL, - enabled BOOLEAN DEFAULT FALSE, - channel_id VARCHAR(255), - PRIMARY KEY (guild_id) - ) - `; - const createRolesTable = ` - CREATE TABLE IF NOT EXISTS info_roles ( - guild_id VARCHAR(255) NOT NULL, - role_id VARCHAR(255) NOT NULL, - level INT NOT NULL, - PRIMARY KEY (role_id) - ) - `; - const createExcludesTable = ` - CREATE TABLE IF NOT EXISTS info_excludes ( - channel_id VARCHAR(255) NOT NULL, - guild_id VARCHAR(255) NOT NULL, - PRIMARY KEY (channel_id) - ) - `; - const createGuildsTable = ` - CREATE TABLE IF NOT EXISTS info_guilds ( - guild_id VARCHAR(255) NOT NULL, - guild_name VARCHAR(255), - guild_icon VARCHAR(255), - guild_members INT, - PRIMARY KEY (guild_id) - ) - `; - - pool.query(createUpdatesTable, (err, results) => { - if (err) { - console.error('Error creating updates table:', err); - } else { - console.log('Updates table created:', results); - } - }); - - pool.query(createRolesTable, (err, results) => { - if (err) { - console.error('Error creating roles table:', err); - } else { - console.log('Roles table created:', results); - } - }); - - pool.query(createExcludesTable, (err, results) => { - if (err) { - console.error('Error creating excludes table:', err); - } else { - console.log('Excludes table created:', results); - } - }); - - pool.query(createGuildsTable, (err, results) => { - if (err) { - console.error('Error creating guilds info table:', err); - } else { - console.log('Guilds info table created:', results); - } - }); -} -console.log('Initializing info tables...'); -await initInfoTables(); -console.log('Info tables initialized'); - -// Ensure the table for a specific guild exists -async function ensureGuildTableExists(guild, callback) { - const createTableQuery = ` - CREATE TABLE IF NOT EXISTS \`${guild}\` ( - user_id VARCHAR(255) NOT NULL, - xp INT DEFAULT 0, - user_pfp TINYTEXT, - user_name TINYTEXT, - user_nickname TINYTEXT, - user_level INT DEFAULT 0, - user_xp_needed_next_level INT, - user_progress_next_level DECIMAL(6, 2), - PRIMARY KEY (user_id) - ) - `; - pool.query(createTableQuery, (err, results) => { - if (err) { - console.error(`Error creating table for guild ${guild}:`, err); - callback(err); - } - else { - console.log(`Table for guild ${guild} ensured:`, results); - callback(null); - } - }); -} - -async function updateGuildInfo(guild, name, icon, members, callback) { - const insertOrUpdateQuery = ` - INSERT INTO info_guilds (guild_id, guild_name, guild_icon, guild_members) - VALUES (?, ?, ?, ?) - ON DUPLICATE KEY UPDATE - guild_name = VALUES(guild_name), - guild_icon = VALUES(guild_icon), - guild_members = VALUES(guild_members) - `; - pool.query(insertOrUpdateQuery, [guild, name, icon, members], (err, results) => { - if (err) { - console.error('Error updating guild info:', err); - callback(err, null); - } - else { - console.log('Guild info updated:', results); - callback(null, results); - } - }); +app.use(express.static(path.join(__dirname, "public"))); +app.set("view engine", "ejs"); +app.set("views", path.join(__dirname, "views")); + +console.log("Initializing tables..."); +await initTables(); +console.log("Tables initialized"); + +function authMiddleware(req: Request, res: Response, next: NextFunction) { + if (!req.headers.authorization || req.headers.authorization !== process.env.AUTH) { + return res + .status(403) + .json({ message: "Access denied" }); + } + next(); } -app.post('/post/:guild/', async (req, res) => { +app.post("/post/:guild", authMiddleware, async (req, res) => { const { guild } = req.params; - const { name, icon, members, auth } = req.body; + const { name, icon, members } = req.body; + + const [err, results] = await updateGuild({ + id: guild, + name, + icon, + members, + updates_enabled: false, + updates_channel: "", + }); - if (auth !== process.env.AUTH) { - return res.status(403).json({ message: 'Access denied. Auth token is missing' }); + if (err) { + res.status(500).json({ message: "Internal server error" }); + } else { + res.status(200).json(results); } - - updateGuildInfo(guild, name, icon, members, (err, results) => { - if (err) { - res.status(500).json({ message: 'Internal server error' }); - } else { - res.status(200).json(results); - } - }); }); -app.post('/post/:guild/:user/:auth', (req, res) => { - const { guild, user, auth } = req.params; +app.post("/post/:guild/:user", authMiddleware, async (req, res) => { + const { guild, user } = req.params; const { name, pfp, xp, nickname } = req.body; console.log(req.body); const xpValue = parseInt(xp); - if (auth !== process.env.AUTH) { - return res.status(403).json({ message: 'Access denied. Auth token is missing' }); - } + const [err, result] = await getUser(user, guild); - ensureGuildTableExists(guild, (err) => { - if (err) { - return res.status(500).json({ message: 'Internal server error' }); - } + if (err) { + console.error("Error fetching XP:", err); + return res.status(500).json({ message: "Internal server error" }); + } - const getXpQuery = `SELECT xp, user_level FROM \`${guild}\` WHERE user_id = ?`; + const currentXp = result?.xp ?? 0; + const currentLevelSaved = result?.level ?? 0; + const newXp = currentXp + xpValue; + + const currentLevel = Math.floor(Math.sqrt(newXp / 100)); + const nextLevel = currentLevel + 1; + const nextLevelXp = Math.pow(nextLevel, 2) * 100; + const xpNeededForNextLevel = nextLevelXp - newXp; + const currentLevelXp = Math.pow(currentLevel, 2) * 100; + const progressToNextLevel = + ((newXp - currentLevelXp) / (nextLevelXp - currentLevelXp)) * 100; + + const updateQuery = ` + INSERT INTO users + (id, guild_id, xp, pfp, name, nickname, level, xp_needed_next_level, progress_next_level) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + xp = VALUES(xp), + pfp = VALUES(pfp), + name = VALUES(name), + nickname = VALUES(nickname), + level = VALUES(level), + xp_needed_next_level = VALUES(xp_needed_next_level), + progress_next_level = VALUES(progress_next_level) + `; - pool.query(getXpQuery, [user], (err, results) => { + pool.query( + updateQuery, + [ + user, + guild, + newXp, + pfp, + name, + nickname, + currentLevel, + xpNeededForNextLevel, + progressToNextLevel.toFixed(2), + ], + (err) => { if (err) { - console.error('Error fetching XP:', err); - return res.status(500).json({ message: 'Internal server error' }); + console.error("Error updating XP:", err); + return res + .status(500) + .json({ success: false, message: "Internal server error" }); + } else { + res + .status(200) + .json({ + success: true, + sendUpdateEvent: currentLevelSaved !== currentLevel, + level: currentLevel, + }); } - - const currentXp = results.length ? results[0].xp : 0; - const currentLevelSaved = results.length ? results[0].user_level : 0; - const newXp = currentXp + xpValue; - - const currentLevel = Math.floor(Math.sqrt(newXp / 100)); - const nextLevel = currentLevel + 1; - const nextLevelXp = Math.pow(nextLevel, 2) * 100; - const xpNeededForNextLevel = nextLevelXp - newXp; - const currentLevelXp = Math.pow(currentLevel, 2) * 100; - const progressToNextLevel = ((newXp - currentLevelXp) / (nextLevelXp - currentLevelXp)) * 100; - - const updateQuery = ` - INSERT INTO \`${guild}\` (user_id, xp, user_pfp, user_name, user_nickname, user_level, user_xp_needed_next_level, user_progress_next_level) - VALUES (?, ?, ?, ?, ?, ?, ?, ?) - ON DUPLICATE KEY UPDATE - xp = VALUES(xp), - user_pfp = VALUES(user_pfp), - user_name = VALUES(user_name), - user_nickname = VALUES(user_nickname), - user_level = VALUES(user_level), - user_xp_needed_next_level = VALUES(user_xp_needed_next_level), - user_progress_next_level = VALUES(user_progress_next_level) - `; - - pool.query(updateQuery, [user, newXp, pfp, name, nickname, currentLevel, xpNeededForNextLevel, progressToNextLevel.toFixed(2)], (err, results) => { - if (err) { - console.error('Error updating XP:', err); - return res.status(500).json({ success: false, message: 'Internal server error' }); - } else { - res.status(200).json({ success: true, sendUpdateEvent: currentLevelSaved !== currentLevel, level: currentLevel}); - } - }); - }); - }); + }, + ); }); -app.get('/get/:guild/:user', (req, res) => { +app.get("/get/:guild/:user", async (req, res) => { const { guild, user } = req.params; - const selectQuery = ` - SELECT * FROM \`${guild}\` WHERE user_id = ? - `; - pool.query(selectQuery, [user], (err, results) => { - if (err) { - console.error('Error fetching XP:', err); - res.status(500).json({ message: 'Internal server error' }); - } - else if (results.length > 0) { - res.status(200).json(results[0]); - } - else { - res.status(404).json({ message: 'User not found' }); - } - }); + const [err, result] = await getUser(user, guild); + + if (err) { + console.error("Error fetching user:", err); + res.status(500).json({ message: "Internal server error" }); + } else if (result) { + res.status(200).json(result); + } else { + res.status(404).json({ message: "User not found" }); + } }); -app.get('/get/:guild', async (req, res) => { +app.get("/get/:guild", async (req, res) => { const { guild } = req.params; - const returnData = { "guild": {}, "leaderboard": [] }; - - const selectQuery = ` - SELECT * FROM \`${guild}\` ORDER BY xp DESC; - `; - const selectQuery2 = ` - SELECT * FROM info_guilds WHERE guild_id = ${guild}; - `; - - try { - const results1 = await new Promise((resolve, reject) => { - pool.query(selectQuery, (err, results) => { - if (err) { - console.error('Error fetching XP:', err); - reject(err); - } else { - resolve(results); - } - }); - }); - const results2 = await new Promise((resolve, reject) => { - pool.query(selectQuery2, (err, results) => { - if (err) { - console.error('Error fetching XP:', err); - reject(err); - } else { - resolve(results); - } - }); + const [guildErr, guildData] = await getGuild(guild); + const [usersErr, usersData] = await getUsers(guild); + + if (guildErr) { + console.error("Error fetching guild:", guildErr); + res.status(500).json({ message: "Internal server error" }); + } else if (usersErr) { + console.error("Error fetching users:", usersErr); + res.status(500).json({ message: "Internal server error" }); + } else if (!guildData) { + res.status(404).json({ message: "Guild not found" }); + } else { + res.status(200).json({ + guild: guildData, + leaderboard: usersData, }); - - returnData.leaderboard = results1; - returnData.guild = results2[0]; - - return res.status(200).json(returnData); - } catch (error) { - console.error('Error fetching XP:', error); - return res.status(500).json({ message: 'Internal server error' }); } }); -app.post('/admin/:action/:guild/:target', async (req, res) => { +app.post("/admin/:action/:guild/:target", authMiddleware, async (req, res) => { const { guild, action, target } = req.params; - const { auth, extraData } = req.body; - - if (auth !== process.env.AUTH) { - return res.status(403).json({ message: 'Access denied. Auth token is missing' }); - } - - let apiSuccess; + const { extraData } = req.body; switch (action) { - case 'include': + case "include": // target: channel id // run function to include target to guild break; - case 'exclude': + case "exclude": // target: channel id // run function to exclude target from guild break; - case 'updates': - if (target !== 'enable' && target !== 'disable' && target !== 'get') { - return res.status(400).json({ message: 'Illegal request' }); + case "updates": + if (target !== "enable" && target !== "disable" && target !== "get") { + return res.status(400).json({ message: "Illegal request" }); } switch (target) { - case 'enable': + case "enable": if (!extraData || !extraData.channelId) { - return res.status(400).json({ message: 'Illegal request' }); + return res.status(400).json({ message: "Illegal request" }); } try { const data = await adminUpdatesAdd(guild, extraData.channelId); return res.status(200).json(data); } catch (error) { - return res.status(500).json({ message: 'Internal server error' }); + return res.status(500).json({ message: "Internal server error" }); } - case 'disable': + case "disable": try { const data = await adminUpdatesRemove(guild); return res.status(200).json(data); } catch (error) { - return res.status(500).json({ message: 'Internal server error' }); + return res.status(500).json({ message: "Internal server error" }); } default: try { const data = await adminUpdatesGet(guild); return res.status(200).json(data); } catch (error) { - return res.status(500).json({ message: 'Internal server error' }); + return res.status(500).json({ message: "Internal server error" }); } } - case 'roles': - if (target !== 'add' && target !== 'remove' && target !== 'get') { - return res.status(400).json({ message: 'Illegal request' }); + case "roles": + if (target !== "add" && target !== "remove" && target !== "get") { + return res.status(400).json({ message: "Illegal request" }); } - if ((target === 'add' || target === 'remove') && !extraData) { - return res.status(400).json({ message: 'Illegal request' }); + if ((target === "add" || target === "remove") && !extraData) { + return res.status(400).json({ message: "Illegal request" }); } switch (target) { - case 'get': + case "get": try { const data = await adminRolesGet(guild); return res.status(200).json(data); } catch (error) { - return res.status(500).json({ message: 'Internal server error' }); + return res.status(500).json({ message: "Internal server error" }); } - case 'remove': + case "remove": try { const data = await adminRolesRemove(guild, extraData.role); return res.status(200).json(data); } catch (error) { - return res.status(500).json({ message: 'Internal server error' }); + return res.status(500).json({ message: "Internal server error" }); } - case 'add': + case "add": try { - const data = await adminRolesAdd(guild, extraData.role, extraData.level); + const data = await adminRolesAdd( + guild, + extraData.role, + extraData.level, + ); return res.status(200).json(data); } catch (error) { - return res.status(500).json({ message: 'Internal server error' }); + return res.status(500).json({ message: "Internal server error" }); } default: - return res.status(500).json({ message: 'Internal server error' }); + return res.status(500).json({ message: "Internal server error" }); } default: - return res.status(400).json({ message: 'Illegal request' }); + return res.status(400).json({ message: "Illegal request" }); } }); -app.get('/leaderboard/:guild', async (req, res) => { +app.get("/leaderboard/:guild", async (req, res) => { const { guild } = req.params; - const response = await fetch(`http://localhost:18103/get/${guild}/`); - if (!response.ok) { - return res.status(404).json({ message: 'No guild was found with this ID' }); + const [guildErr, guildData] = await getGuild(guild); + const [usersErr, usersData] = await getUsers(guild); + + if (guildErr) { + console.error("Error fetching guild:", guildErr); + res.status(500).json({ message: "Internal server error" }); + } else if (usersErr) { + console.error("Error fetching users:", usersErr); + res.status(500).json({ message: "Internal server error" }); } - const data = await response.json(); - res.render('leaderboard', { guild: data.guild, leaderboard: data.leaderboard }); -}); - -async function getBotInfo() { - const selectGuildsCountQuery = ` - SELECT COUNT(*) AS total_guilds FROM info_guilds; - `; - - const selectTotalMembersQuery = ` - SELECT SUM(guild_members) AS total_members FROM info_guilds; - `; - - try { - const guildsCountResult = await new Promise((resolve, reject) => { - pool.query(selectGuildsCountQuery, (err, results) => { - if (err) { - console.error('Error fetching guilds count:', err); - reject(err); - } else { - resolve(results[0].total_guilds); - } - }); - }); - const totalMembersResult = await new Promise((resolve, reject) => { - pool.query(selectTotalMembersQuery, (err, results) => { - if (err) { - console.error('Error fetching total members:', err); - reject(err); - } else { - resolve(results[0].total_members); - } - }); - }); - - const botInfo = { - total_guilds: guildsCountResult, - total_members: totalMembersResult, - }; - - return botInfo - } catch (error) { - console.error('Error fetching bot info:', error); - return null - } -} + res.render("leaderboard", { + guild: guildData, + leaderboard: usersData, + }); +}); -app.get('/', async (req, res) => { - const botInfo = await getBotInfo(); - res.render('index', { botInfo: botInfo }); +app.get("/", async (_req, res) => { + // TODO: handle error + const [err, botInfo] = await getBotInfo(); + res.render("index", { botInfo }); }); -app.get('/invite', (req, res) => { - res.status(308).redirect('https://discord.com/oauth2/authorize?client_id=1245807579624378601&permissions=1099780115520&integration_type=0&scope=bot+applications.commands') -}) +app.get("/invite", (_req, res) => + res + .status(308) + .redirect( + "https://discord.com/oauth2/authorize?client_id=1245807579624378601&permissions=1099780115520&integration_type=0&scope=bot+applications.commands", + ) +); app.listen(PORT, () => { console.log(`Server running on http://localhost:${PORT}`); }); +// TODO: actually implement this in a real way //#region Admin: Roles async function adminRolesGet(guild: string) { - const selectRolesQuery = `SELECT role_id, level FROM info_roles WHERE guild_id = ?`; + const selectRolesQuery = `SELECT id, level FROM roles WHERE guild_id = ?`; return new Promise((resolve, reject) => { pool.query(selectRolesQuery, [guild], (err, results) => { if (err) { - console.error('Error fetching roles:', err); + console.error("Error fetching roles:", err); reject(err); } else { resolve(results); @@ -454,14 +296,14 @@ async function adminRolesGet(guild: string) { async function adminRolesRemove(guild: string, role: string) { const deleteRoleQuery = ` - DELETE FROM info_roles - WHERE guild_id = ? AND role_id = ? + DELETE FROM roles + WHERE id = ? AND guild_id = ? `; return new Promise((resolve, reject) => { - pool.query(deleteRoleQuery, [guild, role], (err, results) => { + pool.query(deleteRoleQuery, [role, guild], (err, results) => { if (err) { - console.error('Error removing role:', err); + console.error("Error removing role:", err); reject(err); } else { resolve(results); @@ -472,14 +314,14 @@ async function adminRolesRemove(guild: string, role: string) { async function adminRolesAdd(guild: string, role: string, level: number) { const insertRoleQuery = ` - INSERT INTO info_roles (guild_id, role_id, level) + INSERT INTO roles (id, guild_id, level) VALUES (?, ?, ?) `; return new Promise((resolve, reject) => { - pool.query(insertRoleQuery, [guild, role, level], (err, results) => { + pool.query(insertRoleQuery, [role, guild, level], (err, results) => { if (err) { - console.error('Error adding role:', err); + console.error("Error adding role:", err); reject(err); } else { resolve(results); @@ -489,14 +331,15 @@ async function adminRolesAdd(guild: string, role: string, level: number) { } //#endregion +// TODO: actually implement this in a real way //#region Admin: Updates async function adminUpdatesGet(guildId: string) { - const selectUpdatesQuery = `SELECT * FROM info_updates WHERE guild_id = ?`; + const selectUpdatesQuery = `SELECT * FROM updates WHERE guild_id = ?`; return new Promise((resolve, reject) => { pool.query(selectUpdatesQuery, [guildId], (err, results) => { if (err) { - console.error('Error fetching updates:', err); + console.error("Error fetching updates:", err); reject(err); } else { resolve(results); @@ -507,35 +350,39 @@ async function adminUpdatesGet(guildId: string) { async function adminUpdatesAdd(guildId: string, channelId: string) { const insertUpdatesQuery = ` - INSERT INTO info_updates (guild_id, enabled, channel_id) - VALUES (?, TRUE, ?) + INSERT INTO updates (guild_id, channel_id, enabled) + VALUES (?, ?, TRUE) ON DUPLICATE KEY UPDATE - enabled = TRUE, - channel_id = ? + enabled = TRUE, + channel_id = VALUES(channel_id) `; return new Promise((resolve, reject) => { - pool.query(insertUpdatesQuery, [guildId, channelId, channelId], (err, results) => { - if (err) { - console.error('Error enabling updates:', err); - reject(err); - } else { - resolve(results); - } - }); + pool.query( + insertUpdatesQuery, + [guildId, channelId], + (err, results) => { + if (err) { + console.error("Error enabling updates:", err); + reject(err); + } else { + resolve(results); + } + }, + ); }); } async function adminUpdatesRemove(guildId: string) { const deleteUpdatesQuery = ` - DELETE FROM info_updates + DELETE FROM updates WHERE guild_id = ? `; return new Promise((resolve, reject) => { pool.query(deleteUpdatesQuery, [guildId], (err, results) => { if (err) { - console.error('Error disabling updates:', err); + console.error("Error disabling updates:", err); reject(err); } else { resolve(results); @@ -544,4 +391,4 @@ async function adminUpdatesRemove(guildId: string) { }); } -//#endregion \ No newline at end of file +//#endregion diff --git a/api/views/leaderboard.ejs b/api/views/leaderboard.ejs index 2b43b6a..c935a14 100644 --- a/api/views/leaderboard.ejs +++ b/api/views/leaderboard.ejs @@ -4,7 +4,7 @@ - Leaderboard for <%= guild.guild_name %> + <title>Leaderboard for <%= guild.name %> @@ -14,9 +14,9 @@
- Guild Icon + Guild Icon

- <%= guild.guild_name %> + <%= guild.name %>

@@ -25,14 +25,14 @@

#<%= index + 1 %>

- User image for <%= user.user_name %> + User image for <%= user.user_name %>
-

- <%= user.user_nickname %> +

+ <%= user.nickname %>

XP: <%= user.xp.toLocaleString() %>

-

Level <%= user.user_level.toLocaleString() %> | <%= user.xp.toLocaleString() %>/<%= (user.xp + user.user_xp_needed_next_level).toLocaleString() %> points to next level (<%= user.user_progress_next_level %>%) +

Level <%= user.level.toLocaleString() %> | <%= user.xp.toLocaleString() %>/<%= (user.xp + user.xp_needed_next_level).toLocaleString() %> points to next level (<%= user.progress_next_level %>%)

diff --git a/bot/utils/requestAPI.ts b/bot/utils/requestAPI.ts index 5b2fa91..d4bb543 100644 --- a/bot/utils/requestAPI.ts +++ b/bot/utils/requestAPI.ts @@ -1,9 +1,10 @@ import handleLevelChange from "./handleLevelChange"; export async function makePOSTRequest(guild: string, user: string, xp: number, pfp: string, name: string, nickname: string) { - await fetch(`http://localhost:18103/post/${guild}/${user}/${process.env.AUTH}`, { + await fetch(`http://localhost:18103/post/${guild}/${user}`, { headers: { 'Content-Type': 'application/json', + 'Authorization': process.env.AUTH as string, }, method: 'POST', body: JSON.stringify({ xp, pfp, name, nickname }), @@ -42,9 +43,10 @@ export async function updateGuildInfo(guild: string, name: string, icon: string, await fetch(`http://localhost:18103/post/${guild}`, { headers: { 'Content-Type': 'application/json', + 'Authorization': process.env.AUTH as string, }, method: 'POST', - body: JSON.stringify({ name, icon, members, auth: process.env.AUTH }), + body: JSON.stringify({ name, icon, members }), }).then(res => { return res.json() }).then(data => { @@ -57,11 +59,10 @@ export async function getRoles(guild: string) { const response = await fetch(`http://localhost:18103/admin/roles/${guild}/get`, { headers: { 'Content-Type': 'application/json', + 'Authorization': process.env.AUTH as string, }, + body: JSON.stringify({}), referrerPolicy: 'strict-origin-when-cross-origin', - body: JSON.stringify({ - auth: process.env.AUTH, - }), method: 'POST', }); @@ -74,9 +75,9 @@ export async function removeRole(guild: string, role: string): Promise const response = await fetch(`http://localhost:18103/admin/roles/${guild}/remove`, { "headers": { 'Content-Type': 'application/json', + 'Authorization': process.env.AUTH as string, }, "body": JSON.stringify({ - auth: process.env.AUTH, extraData: { role: role, } @@ -90,9 +91,9 @@ export async function addRole(guild: string, role: string, level: number): Promi const response = await fetch(`http://localhost:18103/admin/roles/${guild}/add`, { "headers": { 'Content-Type': 'application/json', + 'Authorization': process.env.AUTH as string, }, "body": JSON.stringify({ - auth: process.env.AUTH, extraData: { role: role, level: level @@ -111,26 +112,35 @@ export async function addRole(guild: string, role: string, level: number): Promi //#region Updates export async function checkIfGuildHasUpdatesEnabled(guild: string) { const response = await fetch(`http://localhost:18103/admin/updates/${guild}/get`, { - "headers": { 'Content-Type': 'application/json' }, - "body": JSON.stringify({ auth: process.env.AUTH }), + "headers": { + 'Content-Type': 'application/json', + 'Authorization': process.env.AUTH as string + }, + "body": JSON.stringify({}), "method": "POST" }); return response.status === 200; } export async function enableUpdates(guild: string, channelId: string) { const response = await fetch(`http://localhost:18103/admin/updates/${guild}/enable`, { - "headers": { 'Content-Type': 'application/json' }, - "body": JSON.stringify({ auth: process.env.AUTH, extraData: { channelId } }), + "headers": { + 'Content-Type': 'application/json', + 'Authorization': process.env.AUTH as string, + }, + "body": JSON.stringify({ extraData: { channelId } }), "method": "POST" }); return response.status === 200; } export async function disableUpdates(guild: string) { const response = await fetch(`http://localhost:18103/admin/updates/${guild}/disable`, { - "headers": { 'Content-Type': 'application/json' }, - "body": JSON.stringify({ auth: process.env.AUTH }), + "headers": { + 'Content-Type': 'application/json', + 'Authorization': process.env.AUTH as string, + }, + "body": JSON.stringify({}), "method": "POST" }); return response.status === 200; } -//#endregion \ No newline at end of file +//#endregion