diff --git a/package-lock.json b/package-lock.json index 681e34c..cc64560 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,12 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@discordjs/rest": "^0.5.0", "adm-zip": "^0.5.9", "browserify": "^17.0.0", "colors": "^1.4.0", "cookie-parser": "^1.4.5", + "discord-api-types": "^0.36.1", "discord-oauth2": "github:ryanblenis/discord-oauth2", "express": "^4.17.1", "express-handlebars": "^5.3.1", @@ -136,6 +138,35 @@ "node": ">=4" } }, + "node_modules/@discordjs/collection": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.7.0.tgz", + "integrity": "sha512-R5i8Wb8kIcBAFEPLLf7LVBQKBDYUL+ekb23sOgpkpyGT+V4P7V83wTxcsqmX+PbqHt4cEHn053uMWfRqh/Z/nA==", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/@discordjs/rest": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-0.5.0.tgz", + "integrity": "sha512-S4E1YNz1UxgUfMPpMeqzPPkCfXE877zOsvKM5WEmwIhcpz1PQV7lzqlEOuz194UuwOJLLjQFBgQELnQfCX9UfA==", + "dependencies": { + "@discordjs/collection": "^0.7.0", + "@sapphire/async-queue": "^1.3.1", + "@sapphire/snowflake": "^3.2.2", + "discord-api-types": "^0.33.3", + "tslib": "^2.4.0", + "undici": "^5.4.0" + }, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/@discordjs/rest/node_modules/discord-api-types": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.33.5.tgz", + "integrity": "sha512-dvO5M52v7m7Dy96+XUnzXNsQ/0npsYpU6dL205kAtEDueswoz3aU3bh1UMoK4cQmcGtB1YRyLKqp+DXi05lzFg==" + }, "node_modules/@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -222,6 +253,24 @@ "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", "dev": true }, + "node_modules/@sapphire/async-queue": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.3.1.tgz", + "integrity": "sha512-FFTlPOWZX1kDj9xCAsRzH5xEJfawg1lNoYAA+ecOWJMHOfiZYb1uXOI3ne9U4UILSEPwfE68p3T9wUHwIQfR0g==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@sapphire/snowflake": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.2.2.tgz", + "integrity": "sha512-ula2O0kpSZtX9rKXNeQMrHwNd7E4jPDJYUXmEGTFdMRfyfMw+FPyh04oKMjAiDuOi64bYgVkOV3MjK+loImFhQ==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, "node_modules/@sindresorhus/is": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", @@ -1255,6 +1304,11 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, + "node_modules/discord-api-types": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.36.1.tgz", + "integrity": "sha512-PTDinUU574hXA9Ko9wrftL1iww1raNiRVKjuPIWQ5Li1g7vQPArpZWw9x01kh/IXLPdzSAJ6H8T0eAYzxzFzIg==" + }, "node_modules/discord-oauth2": { "version": "2.9.0", "resolved": "git+ssh://git@github.com/ryanblenis/discord-oauth2.git#ca0093d5b34efdd6ec2ce45a4a2678b3282cea31", @@ -4116,6 +4170,11 @@ "node": ">= 0.10.x" } }, + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, "node_modules/tty-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", @@ -4214,6 +4273,14 @@ "undeclared-identifiers": "bin.js" } }, + "node_modules/undici": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.6.1.tgz", + "integrity": "sha512-yYVqywdCbNb99f/w045wqmv++WExXDjY0FEvLSB7QUZZH6izxrVkF4dJn1aimcvN0+WAhv75Gg7v6VJoqmRtJQ==", + "engines": { + "node": ">=12.18" + } + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -4519,6 +4586,31 @@ } } }, + "@discordjs/collection": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.7.0.tgz", + "integrity": "sha512-R5i8Wb8kIcBAFEPLLf7LVBQKBDYUL+ekb23sOgpkpyGT+V4P7V83wTxcsqmX+PbqHt4cEHn053uMWfRqh/Z/nA==" + }, + "@discordjs/rest": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-0.5.0.tgz", + "integrity": "sha512-S4E1YNz1UxgUfMPpMeqzPPkCfXE877zOsvKM5WEmwIhcpz1PQV7lzqlEOuz194UuwOJLLjQFBgQELnQfCX9UfA==", + "requires": { + "@discordjs/collection": "^0.7.0", + "@sapphire/async-queue": "^1.3.1", + "@sapphire/snowflake": "^3.2.2", + "discord-api-types": "^0.33.3", + "tslib": "^2.4.0", + "undici": "^5.4.0" + }, + "dependencies": { + "discord-api-types": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.33.5.tgz", + "integrity": "sha512-dvO5M52v7m7Dy96+XUnzXNsQ/0npsYpU6dL205kAtEDueswoz3aU3bh1UMoK4cQmcGtB1YRyLKqp+DXi05lzFg==" + } + } + }, "@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -4587,6 +4679,16 @@ "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", "dev": true }, + "@sapphire/async-queue": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.3.1.tgz", + "integrity": "sha512-FFTlPOWZX1kDj9xCAsRzH5xEJfawg1lNoYAA+ecOWJMHOfiZYb1uXOI3ne9U4UILSEPwfE68p3T9wUHwIQfR0g==" + }, + "@sapphire/snowflake": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.2.2.tgz", + "integrity": "sha512-ula2O0kpSZtX9rKXNeQMrHwNd7E4jPDJYUXmEGTFdMRfyfMw+FPyh04oKMjAiDuOi64bYgVkOV3MjK+loImFhQ==" + }, "@sindresorhus/is": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", @@ -5439,6 +5541,11 @@ } } }, + "discord-api-types": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.36.1.tgz", + "integrity": "sha512-PTDinUU574hXA9Ko9wrftL1iww1raNiRVKjuPIWQ5Li1g7vQPArpZWw9x01kh/IXLPdzSAJ6H8T0eAYzxzFzIg==" + }, "discord-oauth2": { "version": "git+ssh://git@github.com/ryanblenis/discord-oauth2.git#ca0093d5b34efdd6ec2ce45a4a2678b3282cea31", "integrity": "sha512-J3w3djV6I4PtnLp3lZga4m41DPjqXtyETV8rMG9FSIev5i5mxpkrsfXRni3Jc+qYxH86LcLa7zUwnWHHXpcu0g==", @@ -7632,6 +7739,11 @@ "restler": "~3.3.0" } }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, "tty-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", @@ -7703,6 +7815,11 @@ "xtend": "^4.0.1" } }, + "undici": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.6.1.tgz", + "integrity": "sha512-yYVqywdCbNb99f/w045wqmv++WExXDjY0FEvLSB7QUZZH6izxrVkF4dJn1aimcvN0+WAhv75Gg7v6VJoqmRtJQ==" + }, "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", diff --git a/package.json b/package.json index 590c825..a7769cb 100644 --- a/package.json +++ b/package.json @@ -17,10 +17,12 @@ }, "homepage": "https://github.com/PretendoNetwork/website#readme", "dependencies": { + "@discordjs/rest": "^0.5.0", "adm-zip": "^0.5.9", "browserify": "^17.0.0", "colors": "^1.4.0", "cookie-parser": "^1.4.5", + "discord-api-types": "^0.36.1", "discord-oauth2": "github:ryanblenis/discord-oauth2", "express": "^4.17.1", "express-handlebars": "^5.3.1", diff --git a/public/assets/js/account.js b/public/assets/js/account.js index 2e6da46..1ca1b5d 100644 --- a/public/assets/js/account.js +++ b/public/assets/js/account.js @@ -5,7 +5,7 @@ document.getElementById('remove-discord-connection')?.addEventListener('click', const tokenType = document.cookie.split('; ').find(row => row.startsWith('token_type=')).split('=')[1]; const accessToken = document.cookie.split('; ').find(row => row.startsWith('access_token=')).split('=')[1]; - fetch('https://api.pretendo.cc/v1/connections/remove/discord', { + fetch('http://api.pretendo.cc/v1/connections/remove/discord', { method: 'DELETE', headers: { 'Content-Type': 'application/json', diff --git a/src/routers/account.js b/src/routers/account.js index 0918d67..d42617a 100644 --- a/src/routers/account.js +++ b/src/routers/account.js @@ -4,6 +4,8 @@ const DiscordOauth2 = require('discord-oauth2'); const { v4: uuidv4 } = require('uuid'); const AdmZip = require('adm-zip'); const Stripe = require('stripe'); +const { REST: DiscordRest } = require('@discordjs/rest'); +const { Routes: DiscordRoutes } = require('discord-api-types/v10'); const database = require('../database'); const cache = require('../cache'); const util = require('../util'); @@ -15,13 +17,14 @@ const aesKey = Buffer.from(config.aes_key, 'hex'); const stripe = new Stripe(config.stripe.secret_key); const router = new Router(); +const discordRest = new DiscordRest({ version: '10' }).setToken(config.discord.bot_token); // Create OAuth client const discordOAuth = new DiscordOauth2({ clientId: config.discord.client_id, clientSecret: config.discord.client_secret, redirectUri: `${config.http.base_url}/account/connect/discord`, - version: 'v9' + version: 'v10' }); router.get('/', async (request, response) => { @@ -94,89 +97,10 @@ router.get('/', async (request, response) => { // Check if a Discord account is linked to the PNID if (account.connections.discord.id && account.connections.discord.id.trim() !== '') { - // If Discord account is linked, then get user info try { - renderData.discordUser = await discordOAuth.getUser(account.connections.discord.access_token); + renderData.discordUser = await discordRest.get(DiscordRoutes.user(account.connections.discord.id)); } catch (error) { - // Assume expired, refresh and retry Discord request - let tokens; - try { - tokens = await discordOAuth.tokenRequest({ - scope: 'identify guilds', - grantType: 'refresh_token', - refreshToken: account.connections.discord.refresh_token, - }); - } catch (error) { - renderData.error = 'Invalid Discord refresh token. Remove account and relink'; - return response.render('account/account', renderData); - } - - // TODO: Add a dedicated endpoint for updating connections? - apiResponse = await util.apiPostGetRequest('/v1/connections/add/discord', { - 'Authorization': `${request.cookies.token_type} ${request.cookies.access_token}` - }, { - data: { - id: account.connections.discord.id, - access_token: tokens.access_token, - refresh_token: tokens.refresh_token, - } - }); - - if (apiResponse.statusCode !== 200) { - // Assume expired, refresh and retry request - apiResponse = await util.apiPostGetRequest('/v1/login', {}, { - refresh_token: request.cookies.refresh_token, - grant_type: 'refresh_token' - }); - - if (apiResponse.statusCode !== 200) { - // TODO: Error message - return response.status(apiResponse.statusCode).json({ - error: 'Bad' - }); - } - - const tokens = apiResponse.body; - - response.cookie('refresh_token', tokens.refresh_token, { domain : '.pretendo.network' }); - response.cookie('access_token', tokens.access_token, { domain : '.pretendo.network' }); - response.cookie('token_type', tokens.token_type, { domain : '.pretendo.network' }); - - apiResponse = await util.apiPostGetRequest('/v1/connections/add/discord', { - 'Authorization': `${tokens.token_type} ${tokens.access_token}` - }, { - data: { - id: account.connections.discord.id, - access_token: tokens.access_token, - refresh_token: tokens.refresh_token, - } - }); - - // If still failed, something went horribly wrong - if (apiResponse.statusCode !== 200) { - // TODO: Error message - return response.status(apiResponse.statusCode).json({ - error: 'Bad' - }); - } - } - - account.connections.discord.access_token = tokens.access_token; - account.connections.discord.refresh_token = tokens.refresh_token; - } - - // Get the users Discord roles to check if they are a tester - const { roles } = await discordOAuth.getMemberRolesForGuild({ - userId: account.connections.discord.id, - guildId: config.discord.guild_id, - botToken: config.discord.bot_token - }); - - // Only run this check if not already a tester (edge case) - if (!renderData.isTester) { - // 409116477212459008 = Developer - // 882247322933801030 = Super Mario (Patreon tier) - renderData.isTester = roles.some(role => config.discord.tester_roles.includes(role)); + response.cookie('error_message', error.message, { domain: '.pretendo.network' }); } } else { // If no Discord account linked, generate an auth URL @@ -310,9 +234,7 @@ router.get('/connect/discord', async (request, response) => { 'Authorization': `${request.cookies.token_type} ${request.cookies.access_token}` }, { data: { - id: user.id, - access_token: tokens.access_token, - refresh_token: tokens.refresh_token, + id: user.id // Only care about user ID. Bot will get details we need } }); @@ -340,9 +262,7 @@ router.get('/connect/discord', async (request, response) => { 'Authorization': `${tokens.token_type} ${tokens.access_token}` }, { data: { - id: user.id, - access_token: tokens.access_token, - refresh_token: tokens.refresh_token, + id: user.id } }); @@ -648,8 +568,8 @@ router.post('/stripe/checkout/:priceId', async (request, response) => { const pnid = await database.PNID.findOne({ pid }); if (pnid.get('access_level') >= 2) { - response.cookie('error_message', 'Staff members do not need to purchase tiers', { domain: '.pretendo.network' }); - return response.redirect('/account'); + //response.cookie('error_message', 'Staff members do not need to purchase tiers', { domain: '.pretendo.network' }); + //return response.redirect('/account'); } try { diff --git a/src/schema/pnid.js b/src/schema/pnid.js index a488521..63a2c47 100644 --- a/src/schema/pnid.js +++ b/src/schema/pnid.js @@ -9,6 +9,9 @@ const PNIDSchema = new Schema({ server_access_level: String, access_level: Number, connections: { + discord: { + id: String + }, stripe: { customer_id: String, subscription_id: String, diff --git a/views/account/account.handlebars b/views/account/account.handlebars index d530e91..33fb3db 100644 --- a/views/account/account.handlebars +++ b/views/account/account.handlebars @@ -139,7 +139,7 @@
Connected to Discord as {{ discordUser.username }}#{{ discordUser.discriminator }}
{{else}} -To link your Discord account, run /whatever in the Discord server
+No Discord account linked. Link Discord account here
{{/if}}