Merge pull request #368 from PretendoNetwork/eslint-and-fixes

This commit is contained in:
Jonathan Barrow
2025-01-21 12:42:04 -05:00
committed by GitHub
27 changed files with 4631 additions and 1565 deletions

View File

@@ -1,2 +0,0 @@
# web javascript causes a lot of eslint warnings
assets/js

View File

@@ -1,42 +0,0 @@
{
"env": {
"browser": true,
"node": true,
"commonjs": true,
"es6": true
},
"globals": {
"document": true
},
"parserOptions": {
"ecmaVersion": 2021
},
"extends": "eslint:recommended",
"rules": {
"no-case-declarations": "off",
"no-empty": "off",
"no-console": "off",
"linebreak-style": "off",
"prefer-const": "error",
"no-var": "error",
"one-var": [
"error",
"never"
],
"indent": [
"error",
"tab",
{
"SwitchCase": 1
}
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
}
}

23
.github/workflows/lint.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: Lint
on:
pull_request: {}
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint

26
eslint.config.mjs Normal file
View File

@@ -0,0 +1,26 @@
import eslintConfig from '@pretendonetwork/eslint-config';
import globals from 'globals';
export default [
...eslintConfig,
{
files: ['public/**'],
languageOptions: {
globals: {
...globals.browser,
...globals.node
}
}
},
{
files: ['src/**'],
languageOptions: {
globals: {
...globals.node
}
}
},
{
ignores: ['**/*.bundled.js']
}
];

5677
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,6 +6,8 @@
"scripts": {
"start": "node src/server.js",
"build": "npm run browserify",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"browserify": "npm run browserify-miieditor && npm run browserify-reset-password",
"browserify-miieditor": "browserify ./public/assets/js/miieditor.js -o ./public/assets/js/miieditor.bundled.js",
"browserify-reset-password": "browserify ./public/assets/js/reset-password.js -o ./public/assets/js/reset-password.bundled.js"
@@ -46,6 +48,8 @@
"stripe": "^9.9.0"
},
"devDependencies": {
"eslint": "^7.32.0"
"@pretendonetwork/eslint-config": "^0.0.3",
"eslint": "^9.18.0",
"globals": "^15.14.0"
}
}
}

View File

@@ -7,7 +7,7 @@ editSettingsModalButtonClose?.addEventListener('click', () => {
editSettingsModal.classList.add('hidden');
});
document.addEventListener('click', event => {
document.addEventListener('click', (event) => {
if (event.target.classList.contains('edit')) {
event.preventDefault();
@@ -15,7 +15,7 @@ document.addEventListener('click', event => {
}
});
serverSelectionSaveButton.addEventListener('click', event => {
serverSelectionSaveButton.addEventListener('click', (event) => {
event.preventDefault();
const checkedInput = updateServerEnvironmentForm.querySelector('input:checked');
@@ -35,7 +35,7 @@ serverSelectionSaveButton.addEventListener('click', event => {
})
})
.then(response => response.json())
.then(json => {
.then((json) => {
if (!json.error) {
// TODO - Make this prettier
alert('Saved server environment');
@@ -44,7 +44,7 @@ serverSelectionSaveButton.addEventListener('click', event => {
alert('Failed to server environment');
}
})
.catch(error => {
.catch((error) => {
console.log(error);
// TODO - Make this prettier
alert('Failed to server environment');

View File

@@ -1,6 +1,6 @@
const openSidebarBtn = document.querySelector('#openSidebar');
const content = document.querySelector('div.content');
openSidebarBtn.addEventListener('click', function() {
openSidebarBtn.addEventListener('click', function () {
const sidebar = document.querySelector('.sidebar');
sidebar.classList.toggle('open');
content.classList.toggle('open-sidebar');

View File

@@ -1,5 +1,3 @@
/* eslint-disable no-undef, no-unused-vars */
const header = document.querySelector('header');
const dropdownButtonWrapper = document.querySelector('.dropdown-button-wrapper');
const dropdown = document.querySelector('header div.dropdown');
@@ -68,10 +66,9 @@ dropdownAnchors.forEach((a) => {
});
});
// make the header background transparent if near the top of the page
function makeHeaderBackgroundTransparent() {
if(window.pageYOffset < 100) {
if (window.pageYOffset < 100) {
header.classList.add('transparent');
} else {
header.classList.remove('transparent');
@@ -83,7 +80,7 @@ window.addEventListener('scroll', () => {
});
desktopDropdownBtns.forEach((btn) => {
[ 'click', 'mouseover' ].forEach((event) => {
['click', 'mouseover'].forEach((event) => {
btn.addEventListener(event, () => {
const id = btn.id.replace('-button', '');
navbarDropdownHandler(id);
@@ -114,7 +111,7 @@ dropdown.addEventListener('mouseleave', (e) => {
});
// Account widget handler
const userWidgetToggle = document.querySelector('.user-widget-toggle') ;
const userWidgetToggle = document.querySelector('.user-widget-toggle');
const userWidget = document.querySelector('.user-widget');
// Open widget on click, close locale dropdown
@@ -159,7 +156,7 @@ localeOptionsList.forEach((option) => {
// close all dropdowns on scroll
document.addEventListener('scroll', () => {
localeDropdownOptions.forEach((el) => el.classList.remove('active'));
localeDropdownOptions.forEach(el => el.classList.remove('active'));
localeDropdownToggle.classList.remove('active');
userWidget?.classList.remove('active');
@@ -180,9 +177,9 @@ document.addEventListener('click', (e) => {
if (
userWidget == targetElement ||
userWidget?.contains(targetElement) ||
userWidgetToggle == targetElement ||
userWidgetToggle?.contains(targetElement)
userWidget?.contains(targetElement) ||
userWidgetToggle == targetElement ||
userWidgetToggle?.contains(targetElement)
) {
found = true;
localeDropdownToggle.classList.remove('active');

View File

@@ -62,12 +62,12 @@ function initializeMiiData(encodedUserMiiData) {
console.log('grabbing rendered miis for later use');
const miiStudioNeutralUrl = mii.studioUrl({
width: 512,
bgColor: '13173300',
bgColor: '13173300'
});
const miiStudioSorrowUrl = mii.studioUrl({
width: 512,
bgColor: '13173300',
expression: 'sorrow',
expression: 'sorrow'
});
document.querySelector('.mii-comparison img.old-mii').src = miiStudioNeutralUrl;
document.querySelector('.mii-comparison.confirmed img.old-mii').src = miiStudioSorrowUrl;
@@ -118,12 +118,12 @@ function renderMii(heightOverride, buildOverride) {
baldMiiFaceImg.src = baldMii.studioUrl({
width: 512,
bgColor: '13173300',
type: 'face_only',
type: 'face_only'
});
miiFaceImg.src = mii.studioUrl({
width: 512,
bgColor: '13173300',
type: 'face_only',
type: 'face_only'
});
miiBodyImg.src = mii.studioAssetUrlBody();
}
@@ -174,7 +174,7 @@ function renderMii(heightOverride, buildOverride) {
if (!heightOverride && !buildOverride) {
const faceMiiStudioUrl = mii.studioUrl({
width: 512,
bgColor: '13173300',
bgColor: '13173300'
});
const faceMiiStudioSmileUrl = mii.studioUrl({
@@ -280,7 +280,7 @@ document
'mustacheType',
'moleEnabled',
'gender',
'favoriteColor',
'favoriteColor'
].forEach((prop) => {
const el = document.querySelector(`#${prop}${mii[prop]}`);
if (el) {
@@ -326,7 +326,7 @@ console.log('[info] preselected value for disableSharing');
'height',
'build',
'miiName',
'creatorName',
'creatorName'
].forEach((prop) => {
document.querySelector(`#${prop}`).value = mii[prop];
document.querySelector(`#${prop}`).defaultValue = mii[prop];
@@ -390,10 +390,10 @@ function openTab(e, tabType) {
// Here we bind all of the functions to the corresponding buttons
document.querySelectorAll('.tabs button.tabbtn').forEach((el) => {
el.addEventListener('click', (e) => openTab(e, 'tab'));
el.addEventListener('click', e => openTab(e, 'tab'));
});
document.querySelectorAll('.subtabs button.subtabbtn').forEach((el) => {
el.addEventListener('click', (e) => openTab(e, 'subtab'));
el.addEventListener('click', e => openTab(e, 'subtab'));
});
// SUBPAGES
@@ -487,10 +487,10 @@ document
mii: {
name: mii.miiName,
primary: 'Y',
data: miiData,
data: miiData
}
})
}) .then(({ status }) => {
}).then(({ status }) => {
// TODO - Make this prettier
alert('Mii has been updated. It may take some time for the cached image on the website to update');

View File

@@ -1,6 +1,6 @@
/* eslint-disable no-undef */
document.querySelectorAll('.feature-list-wrapper').forEach(progressListElement => {
/* global Chart -- chart.js */
document.querySelectorAll('.feature-list-wrapper').forEach((progressListElement) => {
// Find and generate all relevant data
const percentageOverride = progressListElement.querySelector('canvas.percentage-chart').dataset.percentageoverride;
const allFeatureNodes = progressListElement.querySelectorAll('.feature');
@@ -12,7 +12,7 @@ document.querySelectorAll('.feature-list-wrapper').forEach(progressListElement =
const remainingPercentage = 100 - progressPercentage;
// Set inner paragraph
progressListElement.querySelectorAll('.percentage-label').forEach(p => {
progressListElement.querySelectorAll('.percentage-label').forEach((p) => {
if (progressPercentage === 0) {
p.innerText = progressPercentage.toString() + '%';
} else {

View File

@@ -9,7 +9,7 @@ document.querySelector('form').addEventListener('submit', function (event) {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
password: passwordInput.value,
@@ -18,7 +18,7 @@ document.querySelector('form').addEventListener('submit', function (event) {
})
})
.then(response => response.json())
.then(body => {
.then((body) => {
if (body.error) {
alert(`Error: ${body.error}. TODO: red error message thing`);
} else {

View File

@@ -3,13 +3,13 @@ const buttons = {
unsubModal: {
show: document.getElementById('unsubModalShowButton'),
close: document.getElementById('unsubModalCloseButton'),
confirm: document.getElementById('unsubModalConfirmButton'),
confirm: document.getElementById('unsubModalConfirmButton')
},
switchTierModal: {
show: document.getElementById('switchTierShowButton'),
close: document.getElementById('switchTierCloseButton'),
confirm: document.getElementById('switchTierConfirmButton'),
},
confirm: document.getElementById('switchTierConfirmButton')
}
};
const currentTierID = document.querySelector('form').dataset.currentTier || undefined;
@@ -52,7 +52,7 @@ if (currentTierElement) {
}
// If a tier is selected, conditionally enable the submit button.
document.querySelector('form').addEventListener('change', function(e) {
document.querySelector('form').addEventListener('change', function (e) {
e.preventDefault();
// If the selected tier is the current tier, set the button to disabled. Else we enable the button
@@ -60,7 +60,7 @@ document.querySelector('form').addEventListener('change', function(e) {
});
// handle the submit button
buttons.submit.addEventListener('click', function(e) {
buttons.submit.addEventListener('click', function (e) {
e.preventDefault();
// If the user is already subscribed to another tier, we show the confirm modal, else if this is a new subscription we submit the form.
@@ -77,7 +77,7 @@ buttons.submit.addEventListener('click', function(e) {
}
});
buttons.unsubModal.show.addEventListener('click', function(e) {
buttons.unsubModal.show.addEventListener('click', function (e) {
e.preventDefault();
const tierNameSpan = document.querySelector('#unsub .modal-caption span');
@@ -87,27 +87,27 @@ buttons.unsubModal.show.addEventListener('click', function(e) {
document.body.classList.add('modal-open');
document.querySelector('.modal-wrapper#unsub').classList.remove('hidden');
});
buttons.unsubModal.close.addEventListener('click', function(e) {
buttons.unsubModal.close.addEventListener('click', function (e) {
e.preventDefault();
// Hide the unsubscribe modal
document.body.classList.remove('modal-open');
document.querySelector('.modal-wrapper#unsub').classList.add('hidden');
});
buttons.unsubModal.confirm.addEventListener('click', function(e) {
buttons.unsubModal.confirm.addEventListener('click', function (e) {
e.preventDefault();
submitForm(true);
});
buttons.switchTierModal.close.addEventListener('click', function(e) {
buttons.switchTierModal.close.addEventListener('click', function (e) {
e.preventDefault();
// Hide the switch tier modal
document.body.classList.remove('modal-open');
document.querySelector('.modal-wrapper#switchtier').classList.add('hidden');
});
buttons.switchTierModal.confirm.addEventListener('click', function(e) {
buttons.switchTierModal.confirm.addEventListener('click', function (e) {
e.preventDefault();
submitForm(false);

View File

@@ -1,11 +1,11 @@
const { GraphQLClient, gql } = require('graphql-request');
const Stripe = require('stripe');
const config = require('./config');
const logger = require('./logger');
const config = require('../config.json');
const github = new GraphQLClient('https://api.github.com/graphql', {
headers: {
Authorization: `bearer ${config.github.graphql_token}`,
Authorization: `bearer ${config.github.graphql_token}`
}
});
@@ -82,7 +82,7 @@ let stripeDonationCache = {
sections: []
};
async function getGitHubProjectsV2(after='') {
async function getGitHubProjectsV2(after = '') {
let projects = [];
const data = await github.request(getProjectsV2GQL, {
@@ -94,7 +94,7 @@ async function getGitHubProjectsV2(after='') {
projects.push({
id: node.id,
title: node.title,
url: node.repositories.nodes[0]?.url,
url: node.repositories.nodes[0]?.url
});
}
@@ -108,7 +108,7 @@ async function getGitHubProjectsV2(after='') {
return projects;
}
async function getGitHubProjectsV2Fields(id, after='') {
async function getGitHubProjectsV2Fields(id, after = '') {
let fields = [];
const data = await github.request(getProjectsV2FieldsGQL, {

54
src/config.js Normal file
View File

@@ -0,0 +1,54 @@
const jsonConfig = require('../config.json');
const disableEmail = !jsonConfig.email?.from?.trim() || !jsonConfig.email?.ses?.region?.trim() || !jsonConfig.email?.ses?.key?.trim() || !jsonConfig.email?.ses?.secret?.trim();
const email = disableEmail
? false
: {
from: jsonConfig.email.from,
ses: {
region: jsonConfig.email.ses.region,
key: jsonConfig.email.ses.key,
secret: jsonConfig.email.ses.secret
}
};
module.exports = {
api_base: jsonConfig.api_base,
http: {
base_url: jsonConfig.http.base_url,
port: jsonConfig.http.port,
allowed_redirection_suffixes: Array.isArray(jsonConfig.http.allowed_redirection_suffixes) ? jsonConfig.http.allowed_redirection_suffixes : ['pretendo.network']
},
github: {
graphql_token: jsonConfig.github.graphql_token
},
stripe: {
secret_key: jsonConfig.stripe.secret_key,
webhook_secret: jsonConfig.stripe.webhook_secret,
goal_cents: jsonConfig.stripe.goal_cents,
notification_emails: jsonConfig.stripe.notification_emails
},
database: {
account: {
connection_string: jsonConfig.database.account.connection_string,
options: jsonConfig.database.account.options
}
},
discord: {
bot_token: jsonConfig.discord.bot_token,
client_id: jsonConfig.discord.client_id,
client_secret: jsonConfig.discord.client_secret,
guild_id: jsonConfig.discord.guild_id,
roles: {
supporter: jsonConfig.discord.roles.supporter,
tester: jsonConfig.discord.roles.tester
}
},
discourse: {
sso: {
secret: jsonConfig.discourse.sso.secret
}
},
email
};

View File

@@ -1,6 +1,6 @@
const mongoose = require('mongoose');
const config = require('./config');
const PNIDSchema = require('./schema/pnid');
const config = require('../config.json');
const accountServerConfig = config.database.account;
const { connection_string, options } = accountServerConfig;

View File

@@ -1,15 +1,10 @@
const nodemailer = require('nodemailer');
const aws = require('@aws-sdk/client-ses');
const config = require('../config.json');
const config = require('./config');
let disableEmail = false;
let transporter;
if (!config.email?.from?.trim() || !config.email?.ses?.region?.trim() || !config.email?.ses?.key?.trim() || !config.email?.ses?.secret?.trim()) {
disableEmail = true;
}
if (!disableEmail) {
if (config.email) {
const ses = new aws.SES({
apiVersion: '2010-12-01',
region: config.email.ses.region,

View File

@@ -1,14 +1,31 @@
const config = require('../config');
function isValidRedirect(redirect) {
if (!redirect) return false;
if (redirect.startsWith('/')) return true;
if (redirect.startsWith('http://') || redirect.startsWith('https://')) {
try {
const url = new URL(redirect);
return config.http.valid_redirection_domains.some(domain => url.hostname.endsWith(domain));
} catch {
return false;
}
}
return false;
}
async function redirectMiddleware(request, response, next) {
if (request.path.startsWith('/account/logout')) {
return next();
}
if (request.method === 'POST') {
request.redirect = request.body.redirect?.startsWith('/') ? request.body.redirect : null;
if (request.method === 'POST' && request.body) {
request.redirect = isValidRedirect(request.body.redirect) ? request.body.redirect : null;
}
if (request.query.redirect) {
response.locals.redirect = request.query.redirect?.startsWith('/') ? request.query.redirect : null;
response.locals.redirect = isValidRedirect(request.query.redirect) ? request.query.redirect : null;
}
return next();

View File

@@ -1,6 +1,6 @@
const fs = require('fs');
const util = require('../util');
const database = require('../database');
const fs = require('fs');
const localeFileNames = fs.readdirSync(`${__dirname}/../../locales`);
async function renderDataMiddleware(request, response, next) {
@@ -16,7 +16,7 @@ async function renderDataMiddleware(request, response, next) {
const reqLocale = request.cookies.preferredLocale || request.locale.toString();
const locale = util.getLocale(reqLocale);
let localeList = localeFileNames.map(locale => {
let localeList = localeFileNames.map((locale) => {
const code = locale.replace('.json', '').replace('_', '-');
// Check if it's a real language code, or a custom one

View File

@@ -1,5 +1,5 @@
const express = require('express');
const crypto = require('crypto');
const express = require('express');
const DiscordOauth2 = require('discord-oauth2');
const Stripe = require('stripe');
const { REST: DiscordRest } = require('@discordjs/rest');
@@ -10,8 +10,8 @@ const cache = require('../cache');
const util = require('../util');
const { handleStripeEvent } = require('../stripe');
const logger = require('../logger');
const config = require('../../config.json');
const editorJSON = require('../json/miieditor.json');
const editorJSON = require('../json/miieditor.json');
const config = require('../config');
const { Router } = express;
@@ -60,7 +60,7 @@ router.get('/', requireLoginMiddleware, async (request, response) => {
// If no Discord account linked, generate an auth URL
const discordAuthURL = discordOAuth.generateAuthUrl({
scope: ['identify', 'guilds'],
state: crypto.randomBytes(16).toString('hex'),
state: crypto.randomBytes(16).toString('hex')
});
renderData.discordAuthURL = discordAuthURL;
@@ -155,8 +155,8 @@ router.get('/forgot-password', async (request, response) => {
const renderData = {
input: request.cookies.input,
success_message: request.cookies.success_message,
error_message: request.cookies.error_message,
}
error_message: request.cookies.error_message
};
response.clearCookie('input', { domain: '.pretendo.network' });
@@ -172,7 +172,7 @@ router.post('/forgot-password', async (request, response) => {
await util.forgotPassword({
input,
hCaptchaResponse
})
});
response.clearCookie('input', { domain: '.pretendo.network' });
@@ -201,9 +201,9 @@ router.get('/connect/discord', requireLoginMiddleware, async (request, response)
tokens = await discordOAuth.tokenRequest({
code: request.query.code,
scope: 'identify guilds',
grantType: 'authorization_code',
grantType: 'authorization_code'
});
} catch (error) {
} catch {
response.cookie('error_message', 'Invalid Discord authorization code. Please try again', { domain: '.pretendo.network' });
return response.redirect('/account');
}
@@ -291,7 +291,7 @@ router.get('/upgrade', requireLoginMiddleware, async (request, response) => {
renderData.tiers = products
.filter(product => product.active)
.sort((a, b) => +a.metadata.tier_level - +b.metadata.tier_level)
.map(product => {
.map((product) => {
const price = prices.find(price => price.id === product.default_price);
const perks = [];
@@ -309,7 +309,7 @@ router.get('/upgrade', requireLoginMiddleware, async (request, response) => {
name: product.name,
description: product.description,
perks,
price: (price.unit_amount / 100).toLocaleString('en-US', { style: 'currency', currency: 'USD' }),
price: (price.unit_amount / 100).toLocaleString('en-US', { style: 'currency', currency: 'USD' })
};
});
@@ -358,8 +358,8 @@ router.post('/stripe/checkout/:priceId', requireLoginMiddleware, async (request,
line_items: [
{
price: priceId,
quantity: 1,
},
quantity: 1
}
],
customer: customer.id,
mode: 'subscription',
@@ -391,9 +391,9 @@ router.post('/stripe/unsubscribe', requireLoginMiddleware, async (request, respo
const updateData = {
'connections.stripe.subscription_id': null,
'connections.stripe.price_id': null,
'connections.stripe.price_id': null,
'connections.stripe.tier_level': 0,
'connections.stripe.tier_name': null,
'connections.stripe.tier_name': null
};
if (pnid.get('access_level') < 2) {

View File

@@ -1,41 +1,42 @@
const { Router } = require('express');
const logger = require('../logger');
const router = new Router();
const fs = require('fs');
const path = require('path');
const fs = require('fs-extra');
const { Router } = require('express');
const { marked } = require('marked');
const matter = require('gray-matter');
const logger = require('../logger');
const postList = () => {
const files = fs.readdirSync('blogposts');
const router = new Router();
// We get the info for each blogpost, ignoring the ones starting with _
const posts = files
.filter(filename => !filename.startsWith('_'))
.filter(filename => filename.endsWith('.md')) // Ignores other files/folders
.map((filename) => {
const slug = filename.replace('.md', '');
const rawPost = fs.readFileSync(
path.join('blogposts', `${filename}`),
'utf-8'
);
const { data: postInfo } = matter(rawPost);
return {
slug,
postInfo,
};
});
async function getPostsList() {
const files = await fs.readdir('blogposts');
const filteredFiles = files
.filter(filename => !filename.startsWith('_')) // Ignore files starting with _
.filter(filename => filename.endsWith('.md')); // Ignores files that don't end with .md
const posts = await Promise.all(filteredFiles.map(async (filename) => {
const slug = filename.replace('.md', '');
const rawPost = await fs.readFile(
path.join('blogposts', `${filename}`),
'utf-8'
);
const { data: postInfo } = matter(rawPost);
return {
slug,
postInfo
};
}));
posts.sort((a, b) => {
return new Date(b.postInfo.date) - new Date(a.postInfo.date);
});
return posts;
};
}
router.get('/', async (request, response) => {
const renderData = {
const postList = await getPostsList();
const renderData = {
postList
};
@@ -44,9 +45,10 @@ router.get('/', async (request, response) => {
// RSS feed
router.get('/feed.xml', async (request, response) => {
const postList = await getPostsList();
// Adds the pubDate and the cover_extension to the post array
const posts = postList().map((post) => {
const posts = postList.map((post) => {
post.postInfo.pubDate = new Date(post.postInfo.date).toUTCString();
post.postInfo.cover_extension = post.postInfo.cover_image.substring(post.postInfo.cover_image.lastIndexOf('.') + 1);
return post;
@@ -60,10 +62,8 @@ router.get('/feed.xml', async (request, response) => {
});
router.get('/:slug', async (request, response, next) => {
const renderData = {
layout: 'blog-opengraph',
postList,
const renderData = {
layout: 'blog-opengraph'
};
// Get the name of the post from the URL
@@ -72,14 +72,14 @@ router.get('/:slug', async (request, response, next) => {
// Get the markdown file corresponding to the post
let rawPost;
try {
rawPost = fs.readFileSync(path.join('blogposts', `${postName}.md`), 'utf-8');
} catch(err) {
rawPost = await fs.readFile(path.join('blogposts', `${postName}.md`), 'utf-8');
} catch (err) {
logger.error(err);
next();
return;
}
// Convert the post info into JSON and separate it and the content
// eslint-disable-next-line prefer-const
let { data: postInfo, content } = matter(rawPost);
renderData.postInfo = postInfo;

View File

@@ -13,7 +13,7 @@ router.get('/', async (request, response) => {
router.get('/welcome', async (request, response) => {
const renderData = {
currentPage: 'welcome',
currentPage: 'welcome'
};
response.render('docs/welcome', renderData);
@@ -21,7 +21,7 @@ router.get('/welcome', async (request, response) => {
router.get('/install', async (request, response) => {
const renderData = {
currentPage: 'install',
currentPage: 'install'
};
response.render('docs/install', renderData);
@@ -29,11 +29,11 @@ router.get('/install', async (request, response) => {
router.get([
'/search', // TODO - Deprecate search page
'/errors',
'/errors'
], async (_request, response) => {
const renderData = {
errorList: JSON.stringify(errorList),
currentPage: 'errors',
currentPage: 'errors'
};
response.render('docs/search', renderData);
@@ -44,7 +44,7 @@ router.get('/error', async (_request, response) => {
});
router.get('/:page', async (request, response, next) => {
const renderData = {};
const renderData = {};
const locale = response.locals.localeString;
const pageName = request.params.page;
@@ -63,12 +63,12 @@ router.get('/:page', async (request, response, next) => {
router.get([
'/errors/:errorCode', // TODO - Deprecate "errors" route
'/error/:errorCode',
'/error/:errorCode'
], async (request, response, next) => {
const locale = response.locals.localeString;
const error = request.params.errorCode;
const [ sysmodule, errorCode ] = error.split('-');
const [sysmodule, errorCode] = error.split('-');
if (!errorCode) {
return next();

View File

@@ -1,10 +1,10 @@
const { Router } = require('express');
const router = new Router();
const { getGithubProjectsCache } = require('../cache');
const router = new Router();
router.get('/', async (request, response) => {
const renderData = {};
const renderData = {};
const githubProjectsCache = await getGithubProjectsCache();
@@ -48,7 +48,7 @@ router.get('/', async (request, response) => {
};
// Calculates individual completion percentages and progress states
githubProjectsCache.sections.forEach(section => {
githubProjectsCache.sections.forEach((section) => {
const { todo, in_progress, done } = section.cards;
// Calculates the completion percentage of the project, and sums it to the total
@@ -57,7 +57,7 @@ router.get('/', async (request, response) => {
const sectionTitle = `${section.title} [${Math.floor(sectionCompletionPercentage * 100)}%]`;
switch(sectionCompletionPercentage) {
switch (sectionCompletionPercentage) {
case 0:
totalProgress.cards.todo.push(sectionTitle);
break;

View File

@@ -1,10 +1,10 @@
const { Router } = require('express');
const router = new Router();
const { getGithubProjectsCache, getStripeDonationCache } = require('../cache');
const router = new Router();
router.get('/', async (request, response) => {
const renderData = {
const renderData = {
progressLists: await getGithubProjectsCache(),
donationCache: await getStripeDonationCache()
};

View File

@@ -8,55 +8,58 @@ const handlebars = require('express-handlebars');
const morgan = require('morgan');
const expressLocale = require('express-locale');
const cookieParser = require('cookie-parser');
//const Stripe = require('stripe');
// const Stripe = require('stripe');
const config = require('./config');
const redirectMiddleware = require('./middleware/redirect');
const renderDataMiddleware = require('./middleware/render-data');
const database = require('./database');
const util = require('./util');
const logger = require('./logger');
const config = require('../config.json');
const { http: { port } } = config;
const app = express();
//const stripe = new Stripe(config.stripe.secret_key);
// const stripe = new Stripe(config.stripe.secret_key);
logger.info('Setting up Middleware');
app.use(morgan('dev'));
//app.use(express.json());
app.use(express.json({ verify: (req, res, buf) => { req.rawBody = buf; } }));
// app.use(express.json());
app.use(express.json({
verify: (req, res, buf) => {
req.rawBody = buf;
}
}));
app.use(express.urlencoded({
extended: true
}));
app.use(cookieParser());
app.use(expressLocale({
'priority': ['cookie', 'accept-language', 'map', 'default'],
priority: ['cookie', 'accept-language', 'map', 'default'],
cookie: { name: 'preferredLocale' },
// Map unavailable regions to available locales from the same language
map: {
/* TODO: map more regions to the available locales */
en: 'en-US', 'en-AU': 'en-US', 'en-CA': 'en-US',
ar: 'ar-AR',
ca: 'ca-ES',
cs: 'cs-CZ',
cn: 'zh-CN',
de: 'de-DE',
nl: 'nl-NL',
es: 'es-ES',
fr: 'fr-FR', 'fr-CA': 'fr-FR', 'fr-CH': 'fr-FR',
fi: 'fi-FI',
it: 'it-IT', 'it-CH': 'it-IT',
ja: 'ja-JP',
kk: 'kk-KZ',
ko: 'ko-KR',
nb: 'nb-NO',
no: 'nb-NO',
pl: 'pl-PL',
pt: 'pt-BR',
ro: 'ro-RO',
ru: 'ru-RU',
sr: 'sr-RS',
tr: 'tr-TR',
uk: 'uk-UA',
'en': 'en-US', 'en-AU': 'en-US', 'en-CA': 'en-US',
'ar': 'ar-AR',
'ca': 'ca-ES',
'cs': 'cs-CZ',
'cn': 'zh-CN',
'de': 'de-DE',
'nl': 'nl-NL',
'es': 'es-ES',
'fr': 'fr-FR', 'fr-CA': 'fr-FR', 'fr-CH': 'fr-FR',
'fi': 'fi-FI',
'it': 'it-IT', 'it-CH': 'it-IT',
'ja': 'ja-JP',
'kk': 'kk-KZ',
'ko': 'ko-KR',
'nb': 'nb-NO',
'no': 'nb-NO',
'pl': 'pl-PL',
'pt': 'pt-BR',
'ro': 'ro-RO',
'ru': 'ru-RU',
'sr': 'sr-RS',
'tr': 'tr-TR',
'uk': 'uk-UA'
},
allowed: [
'en', 'en-US', 'en-GB', 'en-AU', 'en-CA',
@@ -84,7 +87,7 @@ app.use(expressLocale({
'uk', 'uk-UA',
'en@uwu'
],
'default': 'en-US'
default: 'en-US'
}));
app.use(redirectMiddleware);
app.use(renderDataMiddleware);
@@ -117,7 +120,6 @@ logger.info('Creating 404 status handler');
// This works because it is the last router created
// Meaning the request could not find a valid router
app.use((request, response) => {
const fullUrl = util.fullUrl(request);
response.render('404');
});

View File

@@ -1,9 +1,9 @@
const Stripe = require('stripe');
const config = require('./config');
const mailer = require('./mailer');
const util = require('./util');
const database = require('./database');
const logger = require('./logger');
const config = require('../config.json');
const stripe = new Stripe(config.stripe.secret_key);
@@ -95,12 +95,12 @@ async function handleStripeEvent(event) {
if (subscription.status === 'canceled' && currentSubscriptionId && subscription.id !== currentSubscriptionId) {
// Canceling old subscription, do nothing but update webhook date and remove Discord roles
if (product.metadata.beta === 'true') {
util.removeDiscordMemberTesterRole(discordId).catch(error => {
util.removeDiscordMemberTesterRole(discordId).catch((error) => {
logger.error(`Error removing user Discord tester role | ${customer.id}, ${discordId}, ${pid} | - ${error.message}`);
});
}
util.removeDiscordMemberSupporterRole(discordId, product.metadata.discord_role_id).catch(error => {
util.removeDiscordMemberSupporterRole(discordId, product.metadata.discord_role_id).catch((error) => {
logger.error(`Error removing user Discord supporter role | ${customer.id}, ${discordId}, ${pid}, ${product.metadata.discord_role_id} | - ${error.message}`);
});
@@ -123,7 +123,7 @@ async function handleStripeEvent(event) {
'connections.stripe.price_id': subscription.status === 'active' ? subscription.plan.id : null,
'connections.stripe.tier_level': subscription.status === 'active' ? Number(product.metadata.tier_level || 0) : 0,
'connections.stripe.tier_name': subscription.status === 'active' ? product.name : null,
'connections.stripe.latest_webhook_timestamp': event.created,
'connections.stripe.latest_webhook_timestamp': event.created
};
if (product.metadata.beta === 'true') {
@@ -133,7 +133,7 @@ async function handleStripeEvent(event) {
updateData.server_access_level = 'test';
}
util.assignDiscordMemberTesterRole(discordId).catch(error => {
util.assignDiscordMemberTesterRole(discordId).catch((error) => {
logger.error(`Error assigning user Discord tester role | ${customer.id}, ${discordId}, ${pid} | - ${error.message}`);
});
} else {
@@ -144,7 +144,7 @@ async function handleStripeEvent(event) {
updateData.server_access_level = 'prod';
}
util.removeDiscordMemberTesterRole(discordId).catch(error => {
util.removeDiscordMemberTesterRole(discordId).catch((error) => {
logger.error(`Error removing user Discord tester role | ${customer.id}, ${discordId}, ${pid} | - ${error.message}`);
});
}
@@ -188,7 +188,7 @@ async function handleStripeEvent(event) {
logger.error(`Error sending email | ${customer.id}, ${customer.email}, ${pid} | - ${error.message}`);
}
util.assignDiscordMemberSupporterRole(discordId, product.metadata.discord_role_id).catch(error => {
util.assignDiscordMemberSupporterRole(discordId, product.metadata.discord_role_id).catch((error) => {
logger.error(`Error assigning user Discord supporter role | ${customer.id}, ${discordId}, ${pid}, ${product.metadata.discord_role_id} | - ${error.message}`);
});
@@ -215,7 +215,7 @@ async function handleStripeEvent(event) {
logger.error(`Error sending email | ${customer.id}, ${customer.email}, ${pid} | - ${error.message}`);
}
util.removeDiscordMemberSupporterRole(discordId, product.metadata.discord_role_id).catch(error => {
util.removeDiscordMemberSupporterRole(discordId, product.metadata.discord_role_id).catch((error) => {
logger.error(`Error removing user Discord supporter role | ${customer.id}, ${discordId}, ${pid}, ${product.metadata.discord_role_id} | - ${error.message}`);
});
@@ -242,7 +242,7 @@ async function handleStripeEvent(event) {
logger.error(`Error sending email | ${customer.id}, ${customer.email}, ${pid} | - ${error.message}`);
}
util.removeDiscordMemberSupporterRole(discordId, product.metadata.discord_role_id).catch(error => {
util.removeDiscordMemberSupporterRole(discordId, product.metadata.discord_role_id).catch((error) => {
logger.error(`Error removing user Discord supporter role | ${customer.id}, ${discordId}, ${pid}, ${product.metadata.discord_role_id} | - ${error.message}`);
});
@@ -269,7 +269,7 @@ async function handleStripeEvent(event) {
logger.error(`Error sending email | ${customer.id}, ${customer.email}, ${pid} | - ${error.message}`);
}
util.removeDiscordMemberSupporterRole(discordId, product.metadata.discord_role_id).catch(error => {
util.removeDiscordMemberSupporterRole(discordId, product.metadata.discord_role_id).catch((error) => {
logger.error(`Error removing user Discord supporter role | ${customer.id}, ${discordId}, ${pid}, ${product.metadata.discord_role_id} | - ${error.message}`);
});

View File

@@ -1,13 +1,13 @@
const path = require('path');
const crypto = require('crypto');
const fs = require('fs-extra');
const got = require('got');
const crypto = require('crypto');
const { marked } = require('marked');
const { REST: DiscordRest } = require('@discordjs/rest');
const { Routes: DiscordRoutes } = require('discord-api-types/v10');
const merge = require('lodash.merge');
const config = require('./config');
const logger = require('./logger');
const config = require('../config.json');
const baseLocale = require(`${__dirname}/../locales/en_US.json`);
const discordRest = new DiscordRest({ version: '10' }).setToken(config.discord.bot_token);
@@ -34,14 +34,13 @@ function getLocale(locale) {
}
function getRawDocs(locale, subpath, pageName) {
const localePath = path.join(__dirname, '../docs', locale.replace('-', '_'), subpath, `${pageName}.md`);
const defaultPath = path.join(__dirname, '../docs', 'en_US', subpath, `${pageName}.md`);
if (fs.existsSync(localePath)) {
return {
content: parseDocs(fs.readFileSync(localePath, 'utf8')),
MDLocale: locale,
MDLocale: locale
};
} else if (fs.existsSync(defaultPath)) {
return {
@@ -77,7 +76,7 @@ function apiGetRequest(path, headers) {
responseType: 'json',
throwHttpErrors: false,
https: {
rejectUnauthorized: false, // Needed for self-signed certificates on localhost testing
rejectUnauthorized: false // Needed for self-signed certificates on localhost testing
},
headers
});
@@ -88,7 +87,7 @@ function apiPostRequest(path, headers, json) {
responseType: 'json',
throwHttpErrors: false,
https: {
rejectUnauthorized: false, // Needed for self-signed certificates on localhost testing
rejectUnauthorized: false // Needed for self-signed certificates on localhost testing
},
headers,
json
@@ -99,7 +98,7 @@ function apiDeleteRequest(path, headers, json) {
return got.delete(`${config.api_base}${path}`, {
throwHttpErrors: false,
https: {
rejectUnauthorized: false, // Needed for self-signed certificates on localhost testing
rejectUnauthorized: false // Needed for self-signed certificates on localhost testing
},
headers,
json
@@ -162,9 +161,9 @@ async function refreshLogin(request, response) {
request.cookies.token_type = tokens.token_type;
}
async function getUserAccountData(request, response, fromRetry=false) {
async function getUserAccountData(request, response, fromRetry = false) {
const apiResponse = await apiGetRequest('/v1/user', {
'Authorization': `${request.cookies.token_type} ${request.cookies.access_token}`
Authorization: `${request.cookies.token_type} ${request.cookies.access_token}`
});
if (apiResponse.statusCode !== 200 && fromRetry === true) {
@@ -180,9 +179,9 @@ async function getUserAccountData(request, response, fromRetry=false) {
return apiResponse.body;
}
async function updateDiscordConnection(discordUser, request, response, fromRetry=false) {
async function updateDiscordConnection(discordUser, request, response, fromRetry = false) {
const apiResponse = await apiPostRequest('/v1/connections/add/discord', {
'Authorization': `${request.cookies.token_type} ${request.cookies.access_token}`
Authorization: `${request.cookies.token_type} ${request.cookies.access_token}`
}, {
data: {
id: discordUser.id
@@ -202,7 +201,7 @@ async function updateDiscordConnection(discordUser, request, response, fromRetry
async function removeDiscordConnection(request, response, fromRetry = false) {
const apiResponse = await apiDeleteRequest('/v1/connections/remove/discord', {
'Authorization': `${request.cookies.token_type} ${request.cookies.access_token}`
Authorization: `${request.cookies.token_type} ${request.cookies.access_token}`
});
if (apiResponse.statusCode !== 200 && fromRetry === true) {