Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

init m2m client #504

Merged
merged 1 commit into from
Dec 1, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 4
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -4,6 +4,8 @@ API_URL=https://api.test.faforever.com
OAUTH_URL=https://hydra.test.faforever.com
WP_URL=https://direct.faforever.com
CALLBACK=auth
OAUTH_M2M_CLIENT_ID=faf-website-public
OAUTH_M2M_CLIENT_SECRET=banana
OAUTH_CLIENT_ID=faf-website
OAUTH_CLIENT_SECRET=banana
SESSION_SECRET_KEY=banana
3 changes: 2 additions & 1 deletion .env.faf-stack
Original file line number Diff line number Diff line change
@@ -20,7 +20,8 @@ OAUTH_PUBLIC_URL=http://localhost:4444

# unsing the "xyz" wordpress because the faf-local-stack is just an empty instance without any news etc.
WP_URL=https://direct.faforever.xyz

OAUTH_M2M_CLIENT_ID=faf-website-public
OAUTH_M2M_CLIENT_SECRET=banana
OAUTH_CLIENT_ID=faf-website
OAUTH_CLIENT_SECRET=banana
SESSION_SECRET_KEY=banana
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -19,5 +19,5 @@ COPY --from=builder --chown=node:node /code /code
WORKDIR /code
USER node

CMD ["dumb-init", "node", "express.js"]
CMD ["dumb-init", "node", "src/backend/index.js"]

4 changes: 0 additions & 4 deletions ExpressApp.js

This file was deleted.

6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -35,9 +35,9 @@ Development-Container:
`````bash
cd ../website # replace path if needed
cp -n .env.faf-stack .env
docker compose build
docker compose build
docker compose run website yarn install
docker compose up
docker compose up
`````

this should start the express-server on http://localhost:8020/.
@@ -53,5 +53,3 @@ As of March 2022 the main 4 Languages that are set up on POEditor are:
- Russian
- French
- German


9 changes: 0 additions & 9 deletions express.js

This file was deleted.

144 changes: 0 additions & 144 deletions fafApp.js

This file was deleted.

14 changes: 7 additions & 7 deletions grunt/concurrent.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
module.exports = {
dev: {
tasks: [['run:webpack', 'sass:dev','nodemon'], 'watch'],
options: {
logConcurrentOutput: true
}
}
};
dev: {
tasks: [['run:webpack', 'sass:dev', 'nodemon'], 'watch'],
options: {
logConcurrentOutput: true
}
}
}
27 changes: 14 additions & 13 deletions grunt/nodemon.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
//Runs express script and sets default port to 3000 if environment is not set.
// Runs express script and sets default port to 3000 if environment is not set.
module.exports = {
debug: {
options: {
delay: 500,
ignore: [
'dist/js/*.js',
'sessions/**',
'node_modules/**',
'grunt/**',
'Gruntfile.js',
]
debug: {
script: 'src/backend/index.js',
options: {
delay: 500,
ignore: [
'dist/js/*.js',
'sessions/**',
'node_modules/**',
'grunt/**',
'Gruntfile.js'
]
}
}
}
};
}
4 changes: 2 additions & 2 deletions grunt/run.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module.exports = {
webpack: {
args: ['node_modules/.bin/webpack'],
args: ['node_modules/.bin/webpack']
}
};
}
40 changes: 20 additions & 20 deletions grunt/sass.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
const sass = require('dart-sass');
const sass = require('dart-sass')

module.exports = {
dev: {
options: {
implementation: sass,
style: 'expanded',
compass: true
dev: {
options: {
implementation: sass,
style: 'expanded',
compass: true
},
files: {
'public/styles/css/site.min.css': 'public/styles/site.sass'
}
},
files: {
'public/styles/css/site.min.css': 'public/styles/site.sass',
dist: {
options: {
implementation: sass,
style: 'compressed',
compass: true
},
files: {
'public/styles/css/site.min.css': 'public/styles/site.sass'
}
}
},
dist: {
options: {
implementation: sass,
style: 'compressed',
compass: true
},
files: {
'public/styles/css/site.min.css': 'public/styles/site.sass',
}
}
};
}
10 changes: 5 additions & 5 deletions grunt/watch.js
Original file line number Diff line number Diff line change
@@ -3,8 +3,8 @@ module.exports = {
files: ['src/frontend/**/*.js'],
tasks: ['run:webpack']
},
sass: {
files: ['public/styles/**/*.{scss,sass}'],
tasks: ['sass:dev']
}
};
sass: {
files: ['public/styles/**/*.{scss,sass}'],
tasks: ['sass:dev']
}
}
69 changes: 0 additions & 69 deletions lib/JavaApiClientFactory.js

This file was deleted.

15 changes: 0 additions & 15 deletions lib/LoggedInUserService.js

This file was deleted.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
"express-validator": "7.0.1",
"moment": "^2.29.4",
"node-cache": "^5.1.2",
"node-dependency-injection": "^3.1.2",
"node-fetch": "^2.6.7",
"npm-check": "^6.0.1",
"passport": "^0.6.0",
@@ -23,6 +24,7 @@
"request": "2.88.2",
"session-file-store": "^1.5.0",
"showdown": "^2.1.0",
"simple-oauth2": "^5.0.0",
"supertest-session": "^5.0.1",
"url-slug": "^4.0.1"
},
@@ -43,12 +45,12 @@
"grunt-sass": "3.1.0",
"highcharts": "^11.2.0",
"jest": "^29.7.0",
"jquery": "^3.7.1",
"load-grunt-config": "4.0.1",
"load-grunt-tasks": "5.1.0",
"nock": "^13.3.8",
"octokit": "^3.1.2",
"supertest": "^6.3.3",
"typescript": "^5.3.2",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-manifest-plugin": "^5.0.0"
@@ -58,7 +60,6 @@
"yarn": ">=1.22.0"
},
"scripts": {
"start": "node express.js",
"test": "jest",
"lint": "eslint --ignore-path .gitignore src tests",
"lint:fix": "eslint --fix --ignore-path .gitignore src tests"
104 changes: 104 additions & 0 deletions public/styles/awesomplete.css
77 changes: 0 additions & 77 deletions routes/middleware.js

This file was deleted.

18 changes: 0 additions & 18 deletions routes/views/account/get/activate.js

This file was deleted.

14 changes: 0 additions & 14 deletions routes/views/account/get/changeEmail.js

This file was deleted.

14 changes: 0 additions & 14 deletions routes/views/account/get/changePassword.js

This file was deleted.

14 changes: 0 additions & 14 deletions routes/views/account/get/changeUsername.js

This file was deleted.

19 changes: 0 additions & 19 deletions routes/views/account/get/checkUsername.js

This file was deleted.

18 changes: 0 additions & 18 deletions routes/views/account/get/confirmPasswordReset.js

This file was deleted.

45 changes: 0 additions & 45 deletions routes/views/account/get/connectSteam.js

This file was deleted.

27 changes: 0 additions & 27 deletions routes/views/account/get/createAccount.js

This file was deleted.

50 changes: 0 additions & 50 deletions routes/views/account/get/linkGog.js

This file was deleted.

33 changes: 0 additions & 33 deletions routes/views/account/get/linkSteam.js

This file was deleted.

16 changes: 0 additions & 16 deletions routes/views/account/get/register.js

This file was deleted.

107 changes: 0 additions & 107 deletions routes/views/account/get/report.js

This file was deleted.

34 changes: 0 additions & 34 deletions routes/views/account/get/requestPasswordReset.js

This file was deleted.

34 changes: 0 additions & 34 deletions routes/views/account/get/resync.js

This file was deleted.

55 changes: 0 additions & 55 deletions routes/views/account/post/activate.js

This file was deleted.

58 changes: 0 additions & 58 deletions routes/views/account/post/changeEmail.js

This file was deleted.

63 changes: 0 additions & 63 deletions routes/views/account/post/changePassword.js

This file was deleted.

54 changes: 0 additions & 54 deletions routes/views/account/post/changeUsername.js

This file was deleted.

56 changes: 0 additions & 56 deletions routes/views/account/post/confirmPasswordReset.js

This file was deleted.

21 changes: 0 additions & 21 deletions routes/views/account/post/error.js

This file was deleted.

75 changes: 0 additions & 75 deletions routes/views/account/post/linkGog.js

This file was deleted.

90 changes: 0 additions & 90 deletions routes/views/account/post/register.js

This file was deleted.

212 changes: 0 additions & 212 deletions routes/views/account/post/report.js

This file was deleted.

56 changes: 0 additions & 56 deletions routes/views/account/post/requestPasswordReset.js

This file was deleted.

19 changes: 0 additions & 19 deletions routes/views/checkUsername.js

This file was deleted.

95 changes: 0 additions & 95 deletions routes/views/clans/get/accept_invite.js

This file was deleted.

52 changes: 0 additions & 52 deletions routes/views/clans/get/create.js

This file was deleted.

134 changes: 0 additions & 134 deletions routes/views/clans/get/manage.js

This file was deleted.

130 changes: 0 additions & 130 deletions routes/views/clans/post/create.js

This file was deleted.

111 changes: 0 additions & 111 deletions routes/views/clans/post/destroy.js

This file was deleted.

156 changes: 0 additions & 156 deletions routes/views/clans/post/invite.js

This file was deleted.

87 changes: 0 additions & 87 deletions routes/views/clans/post/join.js

This file was deleted.

96 changes: 0 additions & 96 deletions routes/views/clans/post/kick.js

This file was deleted.

111 changes: 0 additions & 111 deletions routes/views/clans/post/leave.js

This file was deleted.

159 changes: 0 additions & 159 deletions routes/views/clans/post/transfer.js

This file was deleted.

156 changes: 0 additions & 156 deletions routes/views/clans/post/update.js

This file was deleted.

38 changes: 0 additions & 38 deletions routes/views/dataRouter.js

This file was deleted.

61 changes: 0 additions & 61 deletions routes/views/leaderboardRouter.js

This file was deleted.

53 changes: 0 additions & 53 deletions routes/views/news.js

This file was deleted.

23 changes: 0 additions & 23 deletions routes/views/staticMarkdownRouter.js

This file was deleted.

27 changes: 0 additions & 27 deletions scripts/cron-jobs.js

This file was deleted.

143 changes: 143 additions & 0 deletions src/backend/AppKernel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
const { appContainer } = require('./dependency-injection/AppContainer')
const { RequestContainer } = require('./dependency-injection/RequestContainer')
const { RequestContainerCompilerPass } = require('./dependency-injection/RequestContainerCompilerPass')
const { webpackAsset } = require('./middleware/webpackAsset')
const { bootPassport } = require('./security/bootPassport')
const express = require('./ExpressApp')
const appConfig = require('./config/app')
const bodyParser = require('body-parser')
const session = require('express-session')
const flash = require('connect-flash')
const FileStore = require('session-file-store')(session)
const wordpressCacheCrawler = require('./cron-jobs/wordpressCacheCrawler')
const leaderboardCacheCrawler = require('./cron-jobs/leaderboardCacheCrawler')
const defaultRouter = require('./routes/views/defaultRouter')
const authRouter = require('./routes/views/auth')
const staticMarkdownRouter = require('./routes/views/staticMarkdownRouter')
const newsRouter = require('./routes/views/news')
const leaderboardRouter = require('./routes/views/leaderboardRouter')
const clanRouter = require('./routes/views/clanRouter')
const accountRouter = require('./routes/views/accountRouter')
const dataRouter = require('./routes/views/dataRouter')

class AppKernel {
constructor (nodeEnv = 'production') {
this.env = nodeEnv
this.config = appConfig
this.expressApp = null
this.appContainer = null
this.schedulers = []
}

async boot () {
await this.compileContainer(this.config)
this.bootstrapExpress()
return this
}

async compileContainer (config) {
this.appContainer = appContainer(config)
await this.appContainer.compile()
}

bootstrapExpress () {
this.expressApp = express()

this.expressApp.locals.clanInvitations = {}
this.expressApp.use((req, res, next) => {
res.locals.navLinks = []
res.locals.cNavLinks = []
res.locals.appGlobals = {
loggedInUser: null
}
next()
})

this.expressApp.set('views', 'src/backend/templates/views')
this.expressApp.set('view engine', 'pug')
this.expressApp.use(express.static('public', {
immutable: true,
maxAge: 4 * 60 * 60 * 1000 // 4 hours
}))

this.expressApp.use('/dist', express.static('dist', {
immutable: true,
maxAge: 4 * 60 * 60 * 1000 // 4 hours, could be longer since we got cache-busting
}))

this.expressApp.use(express.json())
this.expressApp.use(bodyParser.json())
this.expressApp.use(bodyParser.urlencoded({ extended: false }))
this.expressApp.use(webpackAsset(this.appContainer.getParameter('webpackManifestJS')))

this.expressApp.use(session({
resave: false,
saveUninitialized: true,
secret: appConfig.session.key,
store: new FileStore({
retries: 0,
ttl: appConfig.session.tokenLifespan,
secret: appConfig.session.key
})
}))
bootPassport(this.expressApp, this.config)

this.expressApp.use(async (req, res, next) => {
req.appContainer = this.appContainer
req.requestContainer = RequestContainer(this.appContainer, req)
req.requestContainer.addCompilerPass(new RequestContainerCompilerPass(this.config, req))
await req.requestContainer.compile()

if (req.requestContainer.fafThrownException) {
return next(req.requestContainer.fafThrownException)

Check warning on line 92 in src/backend/AppKernel.js

Codecov / codecov/patch

src/backend/AppKernel.js#L92

Added line #L92 was not covered by tests
}

next()
})

this.expressApp.use(flash())
this.expressApp.use((req, res, next) => {
res.locals.message = req.flash()
next()
})

this.expressApp.use(function (req, res, next) {
if (req.isAuthenticated()) {
res.locals.appGlobals.loggedInUser = req.requestContainer.get('UserService').getUser()
}
next()
})
}

startCronJobs () {
this.schedulers.push(leaderboardCacheCrawler(this.appContainer.get('LeaderboardService')))
this.schedulers.push(wordpressCacheCrawler(this.appContainer.get('WordpressService')))

Check warning on line 114 in src/backend/AppKernel.js

Codecov / codecov/patch

src/backend/AppKernel.js#L112-L114

Added lines #L112 - L114 were not covered by tests
}

loadControllers () {
this.expressApp.use('/', defaultRouter)
this.expressApp.use('/', authRouter)
this.expressApp.use('/', staticMarkdownRouter)
this.expressApp.use('/news', newsRouter)
this.expressApp.use('/leaderboards', leaderboardRouter)
this.expressApp.use('/clans', clanRouter)
this.expressApp.use('/account', accountRouter)
this.expressApp.use('/data', dataRouter)

this.expressApp.use((req, res) => {
res.status(404).render('errors/404')

Check warning on line 128 in src/backend/AppKernel.js

Codecov / codecov/patch

src/backend/AppKernel.js#L128

Added line #L128 was not covered by tests
})
this.expressApp.use((err, req, res, next) => {
console.error('[error] Incoming request to"', req.originalUrl, '"failed with error "', err.toString(), '"')
console.error(err.stack)

Check warning on line 132 in src/backend/AppKernel.js

Codecov / codecov/patch

src/backend/AppKernel.js#L131-L132

Added lines #L131 - L132 were not covered by tests

if (res.headersSent) {
return next(err)

Check warning on line 135 in src/backend/AppKernel.js

Codecov / codecov/patch

src/backend/AppKernel.js#L135

Added line #L135 was not covered by tests
}

res.status(500).render('errors/500')

Check warning on line 138 in src/backend/AppKernel.js

Codecov / codecov/patch

src/backend/AppKernel.js#L138

Added line #L138 was not covered by tests
})
}
}

module.exports.AppKernel = AppKernel
4 changes: 4 additions & 0 deletions src/backend/ExpressApp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const express = require('express')
require('express-async-errors')

module.exports = express
9 changes: 7 additions & 2 deletions config/app.js → src/backend/config/app.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
require('dotenv').config();
require('dotenv').config()

const oauthUrl = process.env.OAUTH_URL || 'https://hydra.faforever.com'

@@ -16,7 +16,12 @@ const appConfig = {
clientSecret: process.env.OAUTH_CLIENT_SECRET || '12345',
url: oauthUrl,
publicUrl: process.env.OAUTH_PUBLIC_URL || oauthUrl,
callback: process.env.CALLBACK || 'callback',
callback: process.env.CALLBACK || 'callback'
},
m2mOauth: {
clientId: process.env.OAUTH_M2M_CLIENT_ID || 'faf-website-public',
clientSecret: process.env.OAUTH_M2M_CLIENT_SECRET || 'banana',
url: oauthUrl
},
apiUrl: process.env.API_URL || 'https://api.faforever.com',
wordpressUrl: process.env.WP_URL || 'https://direct.faforever.com',
45 changes: 45 additions & 0 deletions src/backend/cron-jobs/leaderboardCacheCrawler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const Scheduler = require('../services/Scheduler')

const successHandler = (name) => {
console.debug('[debug] Cache updated', { name })

Check warning on line 4 in src/backend/cron-jobs/leaderboardCacheCrawler.js

Codecov / codecov/patch

src/backend/cron-jobs/leaderboardCacheCrawler.js#L4

Added line #L4 was not covered by tests
}
const errorHandler = (e, name) => {
console.error(e.toString(), { name, entrypoint: 'leaderboardCacheCrawler.js' })
console.error(e.stack)

Check warning on line 8 in src/backend/cron-jobs/leaderboardCacheCrawler.js

Codecov / codecov/patch

src/backend/cron-jobs/leaderboardCacheCrawler.js#L7-L8

Added lines #L7 - L8 were not covered by tests
}

const warmupLeaderboard = async (leaderboardService) => {
try {
await leaderboardService.getLeaderboard(1, true)
.then(() => successHandler('leaderboardService::getLeaderboard(global)'))
.catch((e) => errorHandler(e, 'leaderboardService::getLeaderboard(global)'))

Check warning on line 15 in src/backend/cron-jobs/leaderboardCacheCrawler.js

Codecov / codecov/patch

src/backend/cron-jobs/leaderboardCacheCrawler.js#L12-L15

Added lines #L12 - L15 were not covered by tests

await leaderboardService.getLeaderboard(2, true)
.then(() => successHandler('leaderboardService::getLeaderboard(1v1)'))
.catch((e) => errorHandler(e, 'leaderboardService::getLeaderboard(1v1)'))

Check warning on line 19 in src/backend/cron-jobs/leaderboardCacheCrawler.js

Codecov / codecov/patch

src/backend/cron-jobs/leaderboardCacheCrawler.js#L17-L19

Added lines #L17 - L19 were not covered by tests

await leaderboardService.getLeaderboard(3, true)
.then(() => successHandler('leaderboardService::getLeaderboard(2v2)'))
.catch((e) => errorHandler(e, 'leaderboardService::getLeaderboard(2v2)'))

Check warning on line 23 in src/backend/cron-jobs/leaderboardCacheCrawler.js

Codecov / codecov/patch

src/backend/cron-jobs/leaderboardCacheCrawler.js#L21-L23

Added lines #L21 - L23 were not covered by tests

await leaderboardService.getLeaderboard(4, true)
.then(() => successHandler('leaderboardService::getLeaderboard(4v4)'))
.catch((e) => errorHandler(e, 'leaderboardService::getLeaderboard(4v4)'))

Check warning on line 27 in src/backend/cron-jobs/leaderboardCacheCrawler.js

Codecov / codecov/patch

src/backend/cron-jobs/leaderboardCacheCrawler.js#L25-L27

Added lines #L25 - L27 were not covered by tests
} catch (e) {
console.error('Error: leaderboardCacheCrawler::warmupLeaderboard failed with "' + e.toString() + '"', { entrypoint: 'leaderboardCacheCrawler.js' })
console.error(e.stack)

Check warning on line 30 in src/backend/cron-jobs/leaderboardCacheCrawler.js

Codecov / codecov/patch

src/backend/cron-jobs/leaderboardCacheCrawler.js#L29-L30

Added lines #L29 - L30 were not covered by tests
}
}

/**
* @param {LeaderboardService} leaderboardService
* @return {Scheduler[]}
*/
module.exports = (leaderboardService) => {
warmupLeaderboard(leaderboardService).then(() => {})

Check warning on line 39 in src/backend/cron-jobs/leaderboardCacheCrawler.js

Codecov / codecov/patch

src/backend/cron-jobs/leaderboardCacheCrawler.js#L39

Added line #L39 was not covered by tests

const leaderboardScheduler = new Scheduler('createLeaderboardCaches', () => warmupLeaderboard(leaderboardService).then(() => {}), 60 * 59 * 1000)
leaderboardScheduler.start()

Check warning on line 42 in src/backend/cron-jobs/leaderboardCacheCrawler.js

Codecov / codecov/patch

src/backend/cron-jobs/leaderboardCacheCrawler.js#L41-L42

Added lines #L41 - L42 were not covered by tests

return leaderboardScheduler

Check warning on line 44 in src/backend/cron-jobs/leaderboardCacheCrawler.js

Codecov / codecov/patch

src/backend/cron-jobs/leaderboardCacheCrawler.js#L44

Added line #L44 was not covered by tests
}
50 changes: 50 additions & 0 deletions src/backend/cron-jobs/wordpressCacheCrawler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const Scheduler = require('../services/Scheduler')

const successHandler = (name) => {
console.debug('[debug] Cache updated', { name })

Check warning on line 4 in src/backend/cron-jobs/wordpressCacheCrawler.js

Codecov / codecov/patch

src/backend/cron-jobs/wordpressCacheCrawler.js#L4

Added line #L4 was not covered by tests
}
const errorHandler = (e, name) => {
console.error(e.toString(), { name, entrypoint: 'wordpressCacheCrawler.js' })
console.error(e.stack)

Check warning on line 8 in src/backend/cron-jobs/wordpressCacheCrawler.js

Codecov / codecov/patch

src/backend/cron-jobs/wordpressCacheCrawler.js#L7-L8

Added lines #L7 - L8 were not covered by tests
}

const warmupWordpressCache = (wordpressService) => {
try {
wordpressService.getNews(true)
.then(() => successHandler('wordpressService::getNews'))
.catch((e) => errorHandler(e, 'wordpressService::getNews'))

Check warning on line 15 in src/backend/cron-jobs/wordpressCacheCrawler.js

Codecov / codecov/patch

src/backend/cron-jobs/wordpressCacheCrawler.js#L12-L15

Added lines #L12 - L15 were not covered by tests

wordpressService.getNewshub(true)
.then(() => successHandler('wordpressService::getNewshub'))
.catch((e) => errorHandler(e, 'wordpressService::getNewshub'))

Check warning on line 19 in src/backend/cron-jobs/wordpressCacheCrawler.js

Codecov / codecov/patch

src/backend/cron-jobs/wordpressCacheCrawler.js#L17-L19

Added lines #L17 - L19 were not covered by tests

wordpressService.getContentCreators(true)
.then(() => successHandler('wordpressService::getContentCreators'))
.catch((e) => errorHandler(e, 'wordpressService::getContentCreators'))

Check warning on line 23 in src/backend/cron-jobs/wordpressCacheCrawler.js

Codecov / codecov/patch

src/backend/cron-jobs/wordpressCacheCrawler.js#L21-L23

Added lines #L21 - L23 were not covered by tests

wordpressService.getTournamentNews(true)
.then(() => successHandler('wordpressService::getTournamentNews'))
.catch((e) => errorHandler(e, 'wordpressService::getTournamentNews'))

Check warning on line 27 in src/backend/cron-jobs/wordpressCacheCrawler.js

Codecov / codecov/patch

src/backend/cron-jobs/wordpressCacheCrawler.js#L25-L27

Added lines #L25 - L27 were not covered by tests

wordpressService.getFafTeams(true)
.then(() => successHandler('wordpressService::getFafTeams'))
.catch((e) => errorHandler(e, 'wordpressService::getFafTeams'))

Check warning on line 31 in src/backend/cron-jobs/wordpressCacheCrawler.js

Codecov / codecov/patch

src/backend/cron-jobs/wordpressCacheCrawler.js#L29-L31

Added lines #L29 - L31 were not covered by tests
} catch (e) {
console.error('Error: wordpressCacheCrawler::warmupWordpressCache failed with "' + e.toString() + '"', { entrypoint: 'wordpressCacheCrawler.js' })
console.error(e.stack)

Check warning on line 34 in src/backend/cron-jobs/wordpressCacheCrawler.js

Codecov / codecov/patch

src/backend/cron-jobs/wordpressCacheCrawler.js#L33-L34

Added lines #L33 - L34 were not covered by tests
}
}

/**
* @param {WordpressService} wordpressService
* @param {LeaderboardService} leaderboardService
* @return {Scheduler[]}
*/
module.exports = (wordpressService, leaderboardService) => {
warmupWordpressCache(wordpressService)

Check warning on line 44 in src/backend/cron-jobs/wordpressCacheCrawler.js

Codecov / codecov/patch

src/backend/cron-jobs/wordpressCacheCrawler.js#L44

Added line #L44 was not covered by tests

const wordpressScheduler = new Scheduler('createWordpressCaches', () => warmupWordpressCache(wordpressService), 60 * 59 * 1000)
wordpressScheduler.start()

Check warning on line 47 in src/backend/cron-jobs/wordpressCacheCrawler.js

Codecov / codecov/patch

src/backend/cron-jobs/wordpressCacheCrawler.js#L46-L47

Added lines #L46 - L47 were not covered by tests

return wordpressScheduler

Check warning on line 49 in src/backend/cron-jobs/wordpressCacheCrawler.js

Codecov / codecov/patch

src/backend/cron-jobs/wordpressCacheCrawler.js#L49

Added line #L49 was not covered by tests
}
54 changes: 54 additions & 0 deletions src/backend/dependency-injection/AppContainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const { ContainerBuilder, Reference } = require('node-dependency-injection')
const { LeaderboardRepository } = require('../services/LeaderboardRepository')
const { LeaderboardService } = require('../services/LeaderboardService')
const { JavaApiM2MClient } = require('../services/JavaApiM2MClient')
const { WordpressService } = require('../services/WordpressService')
const { WordpressRepository } = require('../services/WordpressRepository')
const NodeCache = require('node-cache')
const { Axios } = require('axios')
const fs = require('fs')
const webpackManifestJS = JSON.parse(fs.readFileSync('dist/js/manifest.json', 'utf8'))

/**
* @param {object} appConfig
* @return {ContainerBuilder}
*/
module.exports.appContainer = function (appConfig) {
const container = new ContainerBuilder()

container.setParameter('webpackManifestJS', webpackManifestJS)

container.register('NodeCache', NodeCache)
.addArgument({
stdTTL: 300, // use 5 min for all caches if not changed with ttl
checkperiod: 600 // cleanup memory every 10 min
})

container.register('WordpressClient', Axios)
.addArgument({
baseURL: appConfig.wordpressUrl
})

container.register('WordpressRepository', WordpressRepository)
.addArgument(new Reference('WordpressClient'))

container.register('WordpressService', WordpressService)
.addArgument(new Reference('NodeCache'))
.addArgument(new Reference('WordpressRepository'))

container.register('JavaApiM2MClient')
.addArgument(appConfig.m2mOauth.clientId)
.addArgument(appConfig.m2mOauth.clientSecret)
.addArgument(appConfig.m2mOauth.url)
.addArgument(appConfig.apiUrl)
.setFactory(JavaApiM2MClient, 'createInstance')

container.register('LeaderboardRepository', LeaderboardRepository)
.addArgument(new Reference('JavaApiM2MClient'))

container.register('LeaderboardService', LeaderboardService)
.addArgument(new Reference('NodeCache'))
.addArgument(new Reference('LeaderboardRepository'))

return container
}
19 changes: 19 additions & 0 deletions src/backend/dependency-injection/RequestContainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const { ContainerBuilder, Reference } = require('node-dependency-injection')
const { UserRepository } = require('../services/UserRepository')
const { UserService } = require('../services/UserService')

module.exports.RequestContainer = (appContainer, request) => {
const container = new ContainerBuilder()

container.setParameter('request', request)

container.register('UserRepository', UserRepository)
.addArgument(new Reference('JavaApiClient'))
.lazy = true

container.register('JavaApiClient')
.synthetic = true
container.register('UserService', UserService)

return container
}
24 changes: 24 additions & 0 deletions src/backend/dependency-injection/RequestContainerCompilerPass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const { JavaApiClientFactory } = require('../services/JavaApiClientFactory')

class RequestContainerCompilerPass {
constructor (appConfig, request = null) {
this.request = request
this.appConfig = appConfig
}

async process (container) {
try {
if (this.request.user) {
container.get('UserService').setUserFromRequest(this.request)
container.set('JavaApiClient', JavaApiClientFactory.createInstance(container.get('UserService'), this.appConfig.apiUrl, this.request.user.oAuthPassport, this.appConfig.oauth.strategy))
}

return container
} catch (e) {
// https://github.com/zazoomauro/node-dependency-injection/issues/208
container.fafThrownException = e

Check warning on line 19 in src/backend/dependency-injection/RequestContainerCompilerPass.js

Codecov / codecov/patch

src/backend/dependency-injection/RequestContainerCompilerPass.js#L19

Added line #L19 was not covered by tests
}
}
}

module.exports.RequestContainerCompilerPass = RequestContainerCompilerPass
11 changes: 11 additions & 0 deletions src/backend/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const { AppKernel } = require('./AppKernel')

const kernel = new AppKernel()
kernel.boot()
.then((bootedKernel) => {
bootedKernel.startCronJobs()
bootedKernel.loadControllers()
bootedKernel.expressApp.listen(bootedKernel.config.expressPort, () => {
console.log(`Express listening on port ${bootedKernel.config.expressPort}`)
})
})
12 changes: 12 additions & 0 deletions src/backend/middleware/webpackAsset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module.exports.webpackAsset = (webpackManifestJS) => {
return (req, res, next) => {
res.locals.webpackAssetJS = (asset) => {
if (asset in webpackManifestJS) {
return webpackManifestJS[asset]
}

throw new Error('[error] middleware::webpackAsset Failed to find asset "' + asset + '"')

Check warning on line 8 in src/backend/middleware/webpackAsset.js

Codecov / codecov/patch

src/backend/middleware/webpackAsset.js#L8

Added line #L8 was not covered by tests
}
next()
}
}
17 changes: 17 additions & 0 deletions src/backend/routes/middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
exports.isAuthenticated = (redirectUrlAfterLogin = null, isApiRequest = false) => {
return (req, res, next) => {
if (req.isAuthenticated()) {
return next()

Check warning on line 4 in src/backend/routes/middleware.js

Codecov / codecov/patch

src/backend/routes/middleware.js#L4

Added line #L4 was not covered by tests
}

if (req.xhr || req.headers?.accept?.indexOf('json') > -1 || isApiRequest) {
return res.status(401).json({ error: 'Unauthorized' })
}

if (req.session) {
req.session.returnTo = redirectUrlAfterLogin || req.originalUrl
}

return res.redirect('/login')
}
}
16 changes: 16 additions & 0 deletions src/backend/routes/views/account/get/activate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
exports = module.exports = function (req, res) {
const locals = res.locals

// locals.section is used to set the currently selected
// item in the header navigation.
locals.section = 'account'

locals.formData = req.body || {}

const flash = null

// Render the view
locals.username = req.query.username
locals.token = req.query.token
res.render('account/activate', { flash })
}
12 changes: 12 additions & 0 deletions src/backend/routes/views/account/get/changeEmail.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
exports = module.exports = function (req, res) {
const locals = res.locals

Check warning on line 2 in src/backend/routes/views/account/get/changeEmail.js

Codecov / codecov/patch

src/backend/routes/views/account/get/changeEmail.js#L2

Added line #L2 was not covered by tests

// locals.section is used to set the currently selected
// item in the header navigation.
locals.section = 'account'

Check warning on line 6 in src/backend/routes/views/account/get/changeEmail.js

Codecov / codecov/patch

src/backend/routes/views/account/get/changeEmail.js#L6

Added line #L6 was not covered by tests

locals.formData = req.body || {}

// Render the view
res.render('account/changeEmail')

Check warning on line 11 in src/backend/routes/views/account/get/changeEmail.js

Codecov / codecov/patch

src/backend/routes/views/account/get/changeEmail.js#L11

Added line #L11 was not covered by tests
}
12 changes: 12 additions & 0 deletions src/backend/routes/views/account/get/changePassword.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
exports = module.exports = function (req, res) {
const locals = res.locals

Check warning on line 2 in src/backend/routes/views/account/get/changePassword.js

Codecov / codecov/patch

src/backend/routes/views/account/get/changePassword.js#L2

Added line #L2 was not covered by tests

// locals.section is used to set the currently selected
// item in the header navigation.
locals.section = 'account'

Check warning on line 6 in src/backend/routes/views/account/get/changePassword.js

Codecov / codecov/patch

src/backend/routes/views/account/get/changePassword.js#L6

Added line #L6 was not covered by tests

locals.formData = req.body || {}

// Render the view
res.render('account/changePassword')

Check warning on line 11 in src/backend/routes/views/account/get/changePassword.js

Codecov / codecov/patch

src/backend/routes/views/account/get/changePassword.js#L11

Added line #L11 was not covered by tests
}
12 changes: 12 additions & 0 deletions src/backend/routes/views/account/get/changeUsername.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
exports = module.exports = function (req, res) {
const locals = res.locals

Check warning on line 2 in src/backend/routes/views/account/get/changeUsername.js

Codecov / codecov/patch

src/backend/routes/views/account/get/changeUsername.js#L2

Added line #L2 was not covered by tests

// locals.section is used to set the currently selected
// item in the header navigation.
locals.section = 'account'

Check warning on line 6 in src/backend/routes/views/account/get/changeUsername.js

Codecov / codecov/patch

src/backend/routes/views/account/get/changeUsername.js#L6

Added line #L6 was not covered by tests

locals.formData = req.body || {}

// Render the view
res.render('account/changeUsername')

Check warning on line 11 in src/backend/routes/views/account/get/changeUsername.js

Codecov / codecov/patch

src/backend/routes/views/account/get/changeUsername.js#L11

Added line #L11 was not covered by tests
}
19 changes: 19 additions & 0 deletions src/backend/routes/views/account/get/checkUsername.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const request = require('request')

exports = module.exports = function (req, res) {
const name = req.query.username

request(process.env.API_URL + '/data/player?filter=login==' + encodeURI(name), function (error, response, body) {
if (error) {
console.error(error)
return res.status(500).send(error)
}

try {
const userNameFree = JSON.parse(body).data.length === 0
return res.status(userNameFree ? 200 : 400).send(userNameFree)
} catch (e) {
return res.status(500).send(e)
}
})
}
16 changes: 16 additions & 0 deletions src/backend/routes/views/account/get/confirmPasswordReset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
exports = module.exports = function (req, res) {
const locals = res.locals

// locals.section is used to set the currently selected
// item in the header navigation.
locals.section = 'account'

locals.formData = req.body || {}

const flash = null

// Render the view
locals.username = req.query.username
locals.token = req.query.token
res.render('account/confirmPasswordReset', { flash })
}
50 changes: 50 additions & 0 deletions src/backend/routes/views/account/get/connectSteam.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const request = require('request')
const flash = {}

exports = module.exports = function (req, res) {
const locals = res.locals

Check warning on line 5 in src/backend/routes/views/account/get/connectSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/connectSteam.js#L5

Added line #L5 was not covered by tests

locals.section = 'account'
const overallRes = res

Check warning on line 8 in src/backend/routes/views/account/get/connectSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/connectSteam.js#L7-L8

Added lines #L7 - L8 were not covered by tests

request.post({

Check warning on line 10 in src/backend/routes/views/account/get/connectSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/connectSteam.js#L10

Added line #L10 was not covered by tests
url: process.env.API_URL + '/users/buildSteamLinkUrl',
headers: { Authorization: 'Bearer ' + req.services.userService.getUser()?.oAuthPassport.token },
form: { callbackUrl: req.protocol + '://' + req.get('host') + '/account/link?done' }
}, function (err, res, body) {

Check warning on line 14 in src/backend/routes/views/account/get/connectSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/connectSteam.js#L14

Added line #L14 was not covered by tests
if (err) {
flash.class = 'alert-danger'
flash.messages = [{ msg: 'Your steam account was not successfully linked! Please verify you logged into the website correctly.' }]
flash.type = 'Error!'

Check warning on line 18 in src/backend/routes/views/account/get/connectSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/connectSteam.js#L16-L18

Added lines #L16 - L18 were not covered by tests

return overallRes.render('account/linkSteam', { flash })

Check warning on line 20 in src/backend/routes/views/account/get/connectSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/connectSteam.js#L20

Added line #L20 was not covered by tests
}
// Must not be valid, check to see if errors, otherwise return generic error.
try {
body = JSON.parse(body)

Check warning on line 24 in src/backend/routes/views/account/get/connectSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/connectSteam.js#L23-L24

Added lines #L23 - L24 were not covered by tests

if (body.steamUrl) {
return overallRes.redirect(body.steamUrl)

Check warning on line 27 in src/backend/routes/views/account/get/connectSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/connectSteam.js#L27

Added line #L27 was not covered by tests
}

const errorMessages = []

Check warning on line 30 in src/backend/routes/views/account/get/connectSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/connectSteam.js#L30

Added line #L30 was not covered by tests

for (let i = 0; i < body.errors.length; i++) {
const error = body.errors[i]
errorMessages.push({ msg: error.detail })

Check warning on line 34 in src/backend/routes/views/account/get/connectSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/connectSteam.js#L32-L34

Added lines #L32 - L34 were not covered by tests
}

flash.class = 'alert-danger'
flash.messages = errorMessages
flash.type = 'Error!'

Check warning on line 39 in src/backend/routes/views/account/get/connectSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/connectSteam.js#L37-L39

Added lines #L37 - L39 were not covered by tests

overallRes.render('account/linkSteam', { flash })

Check warning on line 41 in src/backend/routes/views/account/get/connectSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/connectSteam.js#L41

Added line #L41 was not covered by tests
} catch (e) {
flash.class = 'alert-danger'
flash.messages = [{ msg: 'Your steam account was not successfully linked! Please verify you logged into the website correctly.' }]
flash.type = 'Error!'

Check warning on line 45 in src/backend/routes/views/account/get/connectSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/connectSteam.js#L43-L45

Added lines #L43 - L45 were not covered by tests

overallRes.render('account/linkSteam', { flash })

Check warning on line 47 in src/backend/routes/views/account/get/connectSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/connectSteam.js#L47

Added line #L47 was not covered by tests
}
})
}
24 changes: 24 additions & 0 deletions src/backend/routes/views/account/get/createAccount.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
exports = module.exports = function (req, res) {
const locals = res.locals

// locals.section is used to set the currently selected
// item in the header navigation.
locals.section = 'account'

if (req.query.token) {
locals.tokenURL = process.env.API_URL + '/users/activate?token=' + req.query.token

Check warning on line 9 in src/backend/routes/views/account/get/createAccount.js

Codecov / codecov/patch

src/backend/routes/views/account/get/createAccount.js#L9

Added line #L9 was not covered by tests
} else {
const flash = {}
flash.type = 'Error!'
flash.class = 'alert-danger'
flash.messages = [{ msg: 'Invalid or missing account token' }]

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return res.redirect('/?flash=' + data)
}

// Render the view
res.render('account/create')

Check warning on line 23 in src/backend/routes/views/account/get/createAccount.js

Codecov / codecov/patch

src/backend/routes/views/account/get/createAccount.js#L23

Added line #L23 was not covered by tests
}
45 changes: 45 additions & 0 deletions src/backend/routes/views/account/get/linkGog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const request = require('request')
const error = require('../post/error')

exports = module.exports = function (req, res) {
const locals = res.locals

Check warning on line 5 in src/backend/routes/views/account/get/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/get/linkGog.js#L5

Added line #L5 was not covered by tests

// locals.section is used to set the currently selected
// item in the header navigation.
locals.section = 'account'

Check warning on line 9 in src/backend/routes/views/account/get/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/get/linkGog.js#L9

Added line #L9 was not covered by tests

locals.formData = req.body || {}

let flash = {}

Check warning on line 13 in src/backend/routes/views/account/get/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/get/linkGog.js#L13

Added line #L13 was not covered by tests
if (req.query.done !== undefined) {
if (req.query.errors) {
const errors = JSON.parse(req.query.errors)

Check warning on line 16 in src/backend/routes/views/account/get/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/get/linkGog.js#L16

Added line #L16 was not covered by tests

flash.class = 'alert-danger'
flash.messages = errors.map(error => ({ msg: error.detail }))
flash.type = 'Error'

Check warning on line 20 in src/backend/routes/views/account/get/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/get/linkGog.js#L18-L20

Added lines #L18 - L20 were not covered by tests
}
} else {
flash = null

Check warning on line 23 in src/backend/routes/views/account/get/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/get/linkGog.js#L23

Added line #L23 was not covered by tests
}

const overallRes = res

Check warning on line 26 in src/backend/routes/views/account/get/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/get/linkGog.js#L26

Added line #L26 was not covered by tests

request.get({

Check warning on line 28 in src/backend/routes/views/account/get/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/get/linkGog.js#L28

Added line #L28 was not covered by tests
url: process.env.API_URL + '/users/buildGogProfileToken',
headers: { Authorization: 'Bearer ' + req.services.userService.getUser()?.oAuthPassport.token },
form: {}
}, function (err, res, body) {
locals.gogToken = 'unable to obtain token'

Check warning on line 33 in src/backend/routes/views/account/get/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/get/linkGog.js#L32-L33

Added lines #L32 - L33 were not covered by tests
if (err || res.statusCode !== 200) {
flash = {}
error.parseApiErrors(body, flash)
return overallRes.render('account/linkGog', { flash })

Check warning on line 37 in src/backend/routes/views/account/get/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/get/linkGog.js#L35-L37

Added lines #L35 - L37 were not covered by tests
}

locals.gogToken = JSON.parse(body).gogToken

Check warning on line 40 in src/backend/routes/views/account/get/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/get/linkGog.js#L40

Added line #L40 was not covered by tests

// Render the view
overallRes.render('account/linkGog', { flash })

Check warning on line 43 in src/backend/routes/views/account/get/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/get/linkGog.js#L43

Added line #L43 was not covered by tests
})
}
32 changes: 32 additions & 0 deletions src/backend/routes/views/account/get/linkSteam.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
exports = module.exports = function (req, res) {
const locals = res.locals

Check warning on line 2 in src/backend/routes/views/account/get/linkSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/linkSteam.js#L2

Added line #L2 was not covered by tests

// locals.section is used to set the currently selected
// item in the header navigation.
locals.section = 'account'

Check warning on line 6 in src/backend/routes/views/account/get/linkSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/linkSteam.js#L6

Added line #L6 was not covered by tests

locals.formData = req.body || {}

let flash = {}

Check warning on line 10 in src/backend/routes/views/account/get/linkSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/linkSteam.js#L10

Added line #L10 was not covered by tests
if (req.query.done !== undefined) {
if (req.query.errors) {
const errors = JSON.parse(req.query.errors)

Check warning on line 13 in src/backend/routes/views/account/get/linkSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/linkSteam.js#L13

Added line #L13 was not covered by tests

flash.class = 'alert-danger'
flash.messages = errors.map(error => ({ msg: error.detail }))
flash.type = 'Error'

Check warning on line 17 in src/backend/routes/views/account/get/linkSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/linkSteam.js#L15-L17

Added lines #L15 - L17 were not covered by tests
} else {
flash.class = 'alert-success'
flash.messages = [{ msg: 'Your steam account has successfully been linked.' }]
flash.type = 'Success'

Check warning on line 21 in src/backend/routes/views/account/get/linkSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/linkSteam.js#L19-L21

Added lines #L19 - L21 were not covered by tests
}
} else {
flash = null

Check warning on line 24 in src/backend/routes/views/account/get/linkSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/linkSteam.js#L24

Added line #L24 was not covered by tests
}

// locals.steam = process.env.API_URL + '/users/linkToSteam';
locals.steamConnect = req.protocol + '://' + req.get('host') + '/account/connect'

Check warning on line 28 in src/backend/routes/views/account/get/linkSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/linkSteam.js#L28

Added line #L28 was not covered by tests

// Render the view
res.render('account/linkSteam', { flash })

Check warning on line 31 in src/backend/routes/views/account/get/linkSteam.js

Codecov / codecov/patch

src/backend/routes/views/account/get/linkSteam.js#L31

Added line #L31 was not covered by tests
}
14 changes: 14 additions & 0 deletions src/backend/routes/views/account/get/register.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
exports = module.exports = function (req, res) {
const locals = res.locals

// locals.section is used to set the currently selected
// item in the header navigation.
locals.section = 'account'

locals.formData = req.body || {}

const flash = null

// Render the view
res.render('account/register', { flash, recaptchaSiteKey: process.env.RECAPTCHA_SITE_KEY })
}
99 changes: 99 additions & 0 deletions src/backend/routes/views/account/get/report.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
const getReports = async (javaApiClient) => {
const maxDescriptionLength = 48

Check warning on line 2 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L2

Added line #L2 was not covered by tests

const response = await javaApiClient.get('/data/moderationReport?include=reportedUsers,lastModerator&sort=-createTime')

Check warning on line 4 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L4

Added line #L4 was not covered by tests

if (response.status !== 200) {
return []

Check warning on line 7 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L7

Added line #L7 was not covered by tests
}

const reports = JSON.parse(response.data)
const cleanReports = []
for (const k in reports.data) {
const report = reports.data[k]

Check warning on line 13 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L10-L13

Added lines #L10 - L13 were not covered by tests

const offenders = []
for (const l in report.relationships.reportedUsers.data) {
const offender = report.relationships.reportedUsers.data[l]
for (const m in reports.included) {
const user = reports.included[m]

Check warning on line 19 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L15-L19

Added lines #L15 - L19 were not covered by tests
if (user.type === 'player' && user.id === offender.id) {
offenders.push(user.attributes.login)

Check warning on line 21 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L21

Added line #L21 was not covered by tests
}
}
}

let moderator = ''

Check warning on line 26 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L26

Added line #L26 was not covered by tests
if (report.relationships.lastModerator.data) {
for (const l in reports.included) {
const user = reports.included[l]

Check warning on line 29 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L28-L29

Added lines #L28 - L29 were not covered by tests
if (user.type === 'player' && user.id === report.relationships.lastModerator.data.id) {
moderator = user.attributes.login
break

Check warning on line 32 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L31-L32

Added lines #L31 - L32 were not covered by tests
}
}
}

let statusStyle = {}

Check warning on line 37 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L37

Added line #L37 was not covered by tests
switch (report.attributes.reportStatus) {
case 'AWAITING':
statusStyle = { color: '#806A15', 'background-color': '#FAD147' }
break

Check warning on line 41 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L40-L41

Added lines #L40 - L41 were not covered by tests
case 'PROCESSING':
statusStyle = { color: '#213A4D', 'background-color': '#3A85BF' }
break

Check warning on line 44 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L43-L44

Added lines #L43 - L44 were not covered by tests
case 'COMPLETED':
statusStyle = { color: 'green', 'background-color': 'lightgreen' }
break

Check warning on line 47 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L46-L47

Added lines #L46 - L47 were not covered by tests
case 'DISCARDED':
statusStyle = { color: '#444444', 'background-color': '#AAAAAA' }
break

Check warning on line 50 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L49-L50

Added lines #L49 - L50 were not covered by tests
}
statusStyle['font-weight'] = 'bold'

Check warning on line 52 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L52

Added line #L52 was not covered by tests

cleanReports.push({

Check warning on line 54 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L54

Added line #L54 was not covered by tests
id: report.id,
offenders: offenders.join(' '),
creationTime: report.attributes.createTime,
game: report.relationships.game.data != null ? '#' + report.relationships.game.data.id : '',
lastModerator: moderator,
description: report.attributes.reportDescription.substr(0, maxDescriptionLength) + (report.attributes.reportDescription.length > maxDescriptionLength ? '...' : ''),
notice: report.attributes.moderatorNotice,
status: report.attributes.reportStatus,
statusStyle
})
}

return cleanReports

Check warning on line 67 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L67

Added line #L67 was not covered by tests
}

module.exports = async (req, res) => {
let offendersNames = []

Check warning on line 71 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L71

Added line #L71 was not covered by tests
if (req.query.offenders !== undefined) {
offendersNames = req.query.offenders.split(' ')

Check warning on line 73 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L73

Added line #L73 was not covered by tests
}

let flash = null

Check warning on line 76 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L76

Added line #L76 was not covered by tests

if (req.originalUrl === '/report_submitted') {
flash = {

Check warning on line 79 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L79

Added line #L79 was not covered by tests
class: 'alert-success ',
messages: { errors: [{ msg: 'You have successfully submitted your report' }] },
type: 'Success!'
}
} else if (req.query.flash) {
const buff = Buffer.from(req.query.flash, 'base64')
const text = buff.toString('ascii')
flash = JSON.parse(text)

Check warning on line 87 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L85-L87

Added lines #L85 - L87 were not covered by tests
}

res.render('account/report', {

Check warning on line 90 in src/backend/routes/views/account/get/report.js

Codecov / codecov/patch

src/backend/routes/views/account/get/report.js#L90

Added line #L90 was not covered by tests
section: 'account',
formData: req.body || {},
game_id: req.query.game_id,
flash,
reports: await getReports(req.requestContainer.get('JavaApiClient')),
reportable_members: {},
offenders_names: offendersNames
})
}
35 changes: 35 additions & 0 deletions src/backend/routes/views/account/get/requestPasswordReset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const axios = require('axios')
const appConfig = require('../../../../config/app')

exports = module.exports = async function (req, res) {
const formData = req.body || {}

// funky issue: https://stackoverflow.com/questions/69169492/async-external-function-leaves-open-handles-jest-supertest-express
await new Promise(resolve => process.nextTick(resolve))

axios.post(appConfig.apiUrl + '/users/buildSteamPasswordResetUrl', {}, { maxRedirects: 0 }).then(response => {
if (response.status !== 200) {
throw new Error('java-api error')

Check warning on line 12 in src/backend/routes/views/account/get/requestPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/get/requestPasswordReset.js#L12

Added line #L12 was not covered by tests
}

res.render('account/requestPasswordReset', {

Check warning on line 15 in src/backend/routes/views/account/get/requestPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/get/requestPasswordReset.js#L15

Added line #L15 was not covered by tests
section: 'account',
flash: {},
steamReset: response.data.steamUrl,
formData,
recaptchaSiteKey: appConfig.recaptchaKey
})
}).catch(error => {
console.error(error.toString())
res.render('account/requestPasswordReset', {
section: 'account',
flash: {
class: 'alert-danger',
messages: 'issue resetting',
type: 'Error!'
},
formData,
recaptchaSiteKey: appConfig.recaptchaKey
})
})
}
32 changes: 32 additions & 0 deletions src/backend/routes/views/account/get/resync.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const flash = {}
const request = require('request')
const error = require('../post/error')

exports = module.exports = function (req, res) {
const locals = res.locals

Check warning on line 6 in src/backend/routes/views/account/get/resync.js

Codecov / codecov/patch

src/backend/routes/views/account/get/resync.js#L6

Added line #L6 was not covered by tests

// locals.section is used to set the currently selected
// item in the header navigation.
locals.section = 'account'

Check warning on line 10 in src/backend/routes/views/account/get/resync.js

Codecov / codecov/patch

src/backend/routes/views/account/get/resync.js#L10

Added line #L10 was not covered by tests

locals.formData = req.body || {}

const overallRes = res

Check warning on line 14 in src/backend/routes/views/account/get/resync.js

Codecov / codecov/patch

src/backend/routes/views/account/get/resync.js#L14

Added line #L14 was not covered by tests

request.post({

Check warning on line 16 in src/backend/routes/views/account/get/resync.js

Codecov / codecov/patch

src/backend/routes/views/account/get/resync.js#L16

Added line #L16 was not covered by tests
url: process.env.API_URL + '/users/resyncAccount',
headers: { Authorization: 'Bearer ' + req.services.userService.getUser()?.oAuthPassport.token }
}, function (err, res, body) {

Check warning on line 19 in src/backend/routes/views/account/get/resync.js

Codecov / codecov/patch

src/backend/routes/views/account/get/resync.js#L19

Added line #L19 was not covered by tests
if (err || res.statusCode !== 200) {
error.parseApiErrors(body, flash)

Check warning on line 21 in src/backend/routes/views/account/get/resync.js

Codecov / codecov/patch

src/backend/routes/views/account/get/resync.js#L21

Added line #L21 was not covered by tests
} else {
// Successfully account resync
flash.class = 'alert-success'
flash.messages = [{ msg: 'Your account was resynced successfully.' }]
flash.type = 'Success!'

Check warning on line 26 in src/backend/routes/views/account/get/resync.js

Codecov / codecov/patch

src/backend/routes/views/account/get/resync.js#L24-L26

Added lines #L24 - L26 were not covered by tests
}

overallRes.render('account/confirmResyncAccount', { flash })

Check warning on line 29 in src/backend/routes/views/account/get/resync.js

Codecov / codecov/patch

src/backend/routes/views/account/get/resync.js#L29

Added line #L29 was not covered by tests
}
)
}
53 changes: 53 additions & 0 deletions src/backend/routes/views/account/post/activate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const flash = {}
const request = require('request')
const error = require('./error')
const { check, validationResult } = require('express-validator')

exports = module.exports = function (req, res) {
const locals = res.locals
locals.username = req.query.username
locals.token = req.query.token

Check warning on line 9 in src/backend/routes/views/account/post/activate.js

Codecov / codecov/patch

src/backend/routes/views/account/post/activate.js#L7-L9

Added lines #L7 - L9 were not covered by tests

locals.formData = req.body || {}

// validate the input
check('password', 'Password is required').notEmpty()
check('password', 'Password must be six or more characters').isLength({ min: 6 })
check('password', 'Passwords don\'t match').equals(req.body.password_confirm)

Check warning on line 16 in src/backend/routes/views/account/post/activate.js

Codecov / codecov/patch

src/backend/routes/views/account/post/activate.js#L14-L16

Added lines #L14 - L16 were not covered by tests

// check the validation object for errors
const errors = validationResult(req)

Check warning on line 19 in src/backend/routes/views/account/post/activate.js

Codecov / codecov/patch

src/backend/routes/views/account/post/activate.js#L19

Added line #L19 was not covered by tests

// Must have client side errors to fix
if (!errors.isEmpty()) {
flash.class = 'alert-danger'
flash.messages = errors
flash.type = 'Error!'

Check warning on line 25 in src/backend/routes/views/account/post/activate.js

Codecov / codecov/patch

src/backend/routes/views/account/post/activate.js#L23-L25

Added lines #L23 - L25 were not covered by tests

res.render('account/activate', { flash })

Check warning on line 27 in src/backend/routes/views/account/post/activate.js

Codecov / codecov/patch

src/backend/routes/views/account/post/activate.js#L27

Added line #L27 was not covered by tests
} else {
const token = req.query.token
const password = req.body.password

Check warning on line 30 in src/backend/routes/views/account/post/activate.js

Codecov / codecov/patch

src/backend/routes/views/account/post/activate.js#L29-L30

Added lines #L29 - L30 were not covered by tests

const overallRes = res

Check warning on line 32 in src/backend/routes/views/account/post/activate.js

Codecov / codecov/patch

src/backend/routes/views/account/post/activate.js#L32

Added line #L32 was not covered by tests

// Run post to reset endpoint
request.post({

Check warning on line 35 in src/backend/routes/views/account/post/activate.js

Codecov / codecov/patch

src/backend/routes/views/account/post/activate.js#L35

Added line #L35 was not covered by tests
url: process.env.API_URL + '/users/activate',
form: { password, token }
}, function (err, res, body) {

Check warning on line 38 in src/backend/routes/views/account/post/activate.js

Codecov / codecov/patch

src/backend/routes/views/account/post/activate.js#L38

Added line #L38 was not covered by tests
if (err || res.statusCode !== 200) {
error.parseApiErrors(body, flash)
return overallRes.render('account/activate', { flash })

Check warning on line 41 in src/backend/routes/views/account/post/activate.js

Codecov / codecov/patch

src/backend/routes/views/account/post/activate.js#L40-L41

Added lines #L40 - L41 were not covered by tests
}

// Successfully reset password
flash.class = 'alert-success'
flash.messages = [{ msg: 'Your account was created successfully.' }]
flash.type = 'Success!'

Check warning on line 47 in src/backend/routes/views/account/post/activate.js

Codecov / codecov/patch

src/backend/routes/views/account/post/activate.js#L45-L47

Added lines #L45 - L47 were not covered by tests

overallRes.render('account/activate', { flash })

Check warning on line 49 in src/backend/routes/views/account/post/activate.js

Codecov / codecov/patch

src/backend/routes/views/account/post/activate.js#L49

Added line #L49 was not covered by tests
}
)
}
}
52 changes: 52 additions & 0 deletions src/backend/routes/views/account/post/changeEmail.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const flash = {}
const request = require('request')
const error = require('./error')
const { check, validationResult } = require('express-validator')

exports = module.exports = function (req, res) {
const locals = res.locals

Check warning on line 7 in src/backend/routes/views/account/post/changeEmail.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeEmail.js#L7

Added line #L7 was not covered by tests

locals.formData = req.body || {}

// validate the input
check('email', 'Email is required').notEmpty()
check('email', 'Email does not appear to be valid').isEmail()

Check warning on line 13 in src/backend/routes/views/account/post/changeEmail.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeEmail.js#L12-L13

Added lines #L12 - L13 were not covered by tests

// check the validation object for errors
const errors = validationResult(req)

Check warning on line 16 in src/backend/routes/views/account/post/changeEmail.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeEmail.js#L16

Added line #L16 was not covered by tests

// Must have client side errors to fix
if (!errors.isEmpty()) {
// failure
flash.class = 'alert-danger'
flash.messages = errors
flash.type = 'Error!'

Check warning on line 23 in src/backend/routes/views/account/post/changeEmail.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeEmail.js#L21-L23

Added lines #L21 - L23 were not covered by tests

res.render('account/changeEmail', { flash })

Check warning on line 25 in src/backend/routes/views/account/post/changeEmail.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeEmail.js#L25

Added line #L25 was not covered by tests
} else {
// pull the form variables off the request body

const email = req.body.email
const password = req.body.password

Check warning on line 30 in src/backend/routes/views/account/post/changeEmail.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeEmail.js#L29-L30

Added lines #L29 - L30 were not covered by tests

const overallRes = res

Check warning on line 32 in src/backend/routes/views/account/post/changeEmail.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeEmail.js#L32

Added line #L32 was not covered by tests

request.post({

Check warning on line 34 in src/backend/routes/views/account/post/changeEmail.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeEmail.js#L34

Added line #L34 was not covered by tests
url: `${process.env.API_URL}/users/changeEmail`,
headers: { Authorization: `Bearer ${req.services.userService.getUser()?.oAuthPassport.token}` },
form: { newEmail: email, currentPassword: password }
}, function (err, res, body) {

Check warning on line 38 in src/backend/routes/views/account/post/changeEmail.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeEmail.js#L38

Added line #L38 was not covered by tests
if (err || res.statusCode !== 200) {
error.parseApiErrors(body, flash)
return overallRes.render('account/changeEmail', { flash })

Check warning on line 41 in src/backend/routes/views/account/post/changeEmail.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeEmail.js#L40-L41

Added lines #L40 - L41 were not covered by tests
}

// Successfully changed email
flash.class = 'alert-success'
flash.messages = [{ msg: 'Your email was set successfully.' }]
flash.type = 'Success!'

Check warning on line 47 in src/backend/routes/views/account/post/changeEmail.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeEmail.js#L45-L47

Added lines #L45 - L47 were not covered by tests

overallRes.render('account/changeEmail', { flash })

Check warning on line 49 in src/backend/routes/views/account/post/changeEmail.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeEmail.js#L49

Added line #L49 was not covered by tests
})
}
}
58 changes: 58 additions & 0 deletions src/backend/routes/views/account/post/changePassword.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const flash = {}
const request = require('request')
const error = require('./error')
const { check, validationResult } = require('express-validator')

exports = module.exports = function (req, res) {
const locals = res.locals

Check warning on line 7 in src/backend/routes/views/account/post/changePassword.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changePassword.js#L7

Added line #L7 was not covered by tests

locals.formData = req.body || {}

// validate the input
check('old_password', 'Old Password is required').notEmpty()
check('old_password', 'Old Password must be six or more characters').isLength({ min: 6 })
check('password', 'New Password is required').notEmpty()
check('password', 'New Password must be six or more characters').isLength({ min: 6 })
check('password', 'New Passwords don\'t match').equals(req.body.password_confirm)
check('username', 'Username is required').notEmpty()
check('username', 'Username must be three or more characters').isLength({ min: 3 })

Check warning on line 18 in src/backend/routes/views/account/post/changePassword.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changePassword.js#L12-L18

Added lines #L12 - L18 were not covered by tests

// check the validation object for errors
const errors = validationResult(req)

Check warning on line 21 in src/backend/routes/views/account/post/changePassword.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changePassword.js#L21

Added line #L21 was not covered by tests

// Must have client side errors to fix
if (!errors.isEmpty()) {
// failure
flash.class = 'alert-danger'
flash.messages = errors
flash.type = 'Error!'

Check warning on line 28 in src/backend/routes/views/account/post/changePassword.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changePassword.js#L26-L28

Added lines #L26 - L28 were not covered by tests

res.render('account/changePassword', { flash })

Check warning on line 30 in src/backend/routes/views/account/post/changePassword.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changePassword.js#L30

Added line #L30 was not covered by tests
} else {
// Encrypt password before sending it off to endpoint
const newPassword = req.body.password
const oldPassword = req.body.old_password
const username = req.body.username

Check warning on line 35 in src/backend/routes/views/account/post/changePassword.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changePassword.js#L33-L35

Added lines #L33 - L35 were not covered by tests

const overallRes = res

Check warning on line 37 in src/backend/routes/views/account/post/changePassword.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changePassword.js#L37

Added line #L37 was not covered by tests

// Run post to reset endpoint
request.post({

Check warning on line 40 in src/backend/routes/views/account/post/changePassword.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changePassword.js#L40

Added line #L40 was not covered by tests
url: process.env.API_URL + '/users/changePassword',
headers: { Authorization: 'Bearer ' + req.services.userService.getUser()?.oAuthPassport.token },
form: { name: username, currentPassword: oldPassword, newPassword }
}, function (err, res, body) {

Check warning on line 44 in src/backend/routes/views/account/post/changePassword.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changePassword.js#L44

Added line #L44 was not covered by tests
if (err || res.statusCode !== 200) {
error.parseApiErrors(body, flash)
return overallRes.render('account/changePassword', { flash })

Check warning on line 47 in src/backend/routes/views/account/post/changePassword.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changePassword.js#L46-L47

Added lines #L46 - L47 were not covered by tests
}

// Successfully reset password
flash.class = 'alert-success'
flash.messages = [{ msg: 'Your password was changed successfully. Please use the new password to log in!' }]
flash.type = 'Success!'

Check warning on line 53 in src/backend/routes/views/account/post/changePassword.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changePassword.js#L51-L53

Added lines #L51 - L53 were not covered by tests

overallRes.render('account/changePassword', { flash })

Check warning on line 55 in src/backend/routes/views/account/post/changePassword.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changePassword.js#L55

Added line #L55 was not covered by tests
})
}
}
50 changes: 50 additions & 0 deletions src/backend/routes/views/account/post/changeUsername.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const flash = {}
const request = require('request')
const error = require('./error')
const { check, validationResult } = require('express-validator')

exports = module.exports = function (req, res) {
const locals = res.locals

Check warning on line 7 in src/backend/routes/views/account/post/changeUsername.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeUsername.js#L7

Added line #L7 was not covered by tests

locals.formData = req.body || {}

// validate the input
check('username', 'Username is required').notEmpty()
check('username', 'Username must be three or more characters').isLength({ min: 3 })

Check warning on line 13 in src/backend/routes/views/account/post/changeUsername.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeUsername.js#L12-L13

Added lines #L12 - L13 were not covered by tests

// check the validation object for errors
const errors = validationResult(req)

Check warning on line 16 in src/backend/routes/views/account/post/changeUsername.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeUsername.js#L16

Added line #L16 was not covered by tests

// Must have client side errors to fix
if (!errors.isEmpty()) {
// failure
flash.class = 'alert-danger'
flash.messages = errors
flash.type = 'Error!'

Check warning on line 23 in src/backend/routes/views/account/post/changeUsername.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeUsername.js#L21-L23

Added lines #L21 - L23 were not covered by tests

res.render('account/changeUsername', { flash })

Check warning on line 25 in src/backend/routes/views/account/post/changeUsername.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeUsername.js#L25

Added line #L25 was not covered by tests
} else {
// pull the form variables off the request body
const username = req.body.username
const overallRes = res

Check warning on line 29 in src/backend/routes/views/account/post/changeUsername.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeUsername.js#L28-L29

Added lines #L28 - L29 were not covered by tests

// Run post to reset endpoint
request.post({

Check warning on line 32 in src/backend/routes/views/account/post/changeUsername.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeUsername.js#L32

Added line #L32 was not covered by tests
url: process.env.API_URL + '/users/changeUsername',
headers: { Authorization: 'Bearer ' + req.services.userService.getUser()?.oAuthPassport.token },
form: { newUsername: username }
}, function (err, res, body) {

Check warning on line 36 in src/backend/routes/views/account/post/changeUsername.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeUsername.js#L36

Added line #L36 was not covered by tests
if (err || res.statusCode !== 200) {
error.parseApiErrors(body, flash)
return overallRes.render('account/changeUsername', { flash })

Check warning on line 39 in src/backend/routes/views/account/post/changeUsername.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeUsername.js#L38-L39

Added lines #L38 - L39 were not covered by tests
}

// Successfully changed username
flash.class = 'alert-success'
flash.messages = [{ msg: 'Your username was changed successfully. Please use the new username to log in!' }]
flash.type = 'Success!'

Check warning on line 45 in src/backend/routes/views/account/post/changeUsername.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeUsername.js#L43-L45

Added lines #L43 - L45 were not covered by tests

overallRes.render('account/changeUsername', { flash })

Check warning on line 47 in src/backend/routes/views/account/post/changeUsername.js

Codecov / codecov/patch

src/backend/routes/views/account/post/changeUsername.js#L47

Added line #L47 was not covered by tests
})
}
}
53 changes: 53 additions & 0 deletions src/backend/routes/views/account/post/confirmPasswordReset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const flash = {}
const request = require('request')
const error = require('./error')
const { check, validationResult } = require('express-validator')

exports = module.exports = function (req, res) {
const locals = res.locals
locals.username = req.query.username
locals.token = req.query.token

Check warning on line 9 in src/backend/routes/views/account/post/confirmPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/confirmPasswordReset.js#L7-L9

Added lines #L7 - L9 were not covered by tests

locals.formData = req.body || {}

// validate the input
check('password', 'Password is required').notEmpty()
check('password', 'Password must be six or more characters').isLength({ min: 6 })
check('password', 'Passwords don\'t match').equals(req.body.password_confirm)

Check warning on line 16 in src/backend/routes/views/account/post/confirmPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/confirmPasswordReset.js#L14-L16

Added lines #L14 - L16 were not covered by tests

// check the validation object for errors
const errors = validationResult(req)

Check warning on line 19 in src/backend/routes/views/account/post/confirmPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/confirmPasswordReset.js#L19

Added line #L19 was not covered by tests

// Must have client side errors to fix
if (!errors.isEmpty()) {
flash.class = 'alert-danger'
flash.messages = errors
flash.type = 'Error!'

Check warning on line 25 in src/backend/routes/views/account/post/confirmPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/confirmPasswordReset.js#L23-L25

Added lines #L23 - L25 were not covered by tests

res.render('account/confirmPasswordReset', { flash })

Check warning on line 27 in src/backend/routes/views/account/post/confirmPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/confirmPasswordReset.js#L27

Added line #L27 was not covered by tests
} else {
const token = req.query.token
const newPassword = req.body.password

Check warning on line 30 in src/backend/routes/views/account/post/confirmPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/confirmPasswordReset.js#L29-L30

Added lines #L29 - L30 were not covered by tests

const overallRes = res

Check warning on line 32 in src/backend/routes/views/account/post/confirmPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/confirmPasswordReset.js#L32

Added line #L32 was not covered by tests

// Run post to reset endpoint
request.post({

Check warning on line 35 in src/backend/routes/views/account/post/confirmPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/confirmPasswordReset.js#L35

Added line #L35 was not covered by tests
url: process.env.API_URL + '/users/performPasswordReset',
form: { newPassword, token }
}, function (err, res, body) {

Check warning on line 38 in src/backend/routes/views/account/post/confirmPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/confirmPasswordReset.js#L38

Added line #L38 was not covered by tests
if (err || res.statusCode !== 200) {
error.parseApiErrors(body, flash)
return overallRes.render('account/confirmPasswordReset', { flash })

Check warning on line 41 in src/backend/routes/views/account/post/confirmPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/confirmPasswordReset.js#L40-L41

Added lines #L40 - L41 were not covered by tests
}

// Successfully reset password
flash.class = 'alert-success'
flash.messages = [{ msg: 'Your password was changed successfully.' }]
flash.type = 'Success!'

Check warning on line 47 in src/backend/routes/views/account/post/confirmPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/confirmPasswordReset.js#L45-L47

Added lines #L45 - L47 were not covered by tests

overallRes.render('account/confirmPasswordReset', { flash })

Check warning on line 49 in src/backend/routes/views/account/post/confirmPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/confirmPasswordReset.js#L49

Added line #L49 was not covered by tests
}
)
}
}
21 changes: 21 additions & 0 deletions src/backend/routes/views/account/post/error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module.exports = {
parseApiErrors: function (body, flash) {
const errorMessages = []

Check warning on line 3 in src/backend/routes/views/account/post/error.js

Codecov / codecov/patch

src/backend/routes/views/account/post/error.js#L2-L3

Added lines #L2 - L3 were not covered by tests

try {
const response = JSON.parse(body)
response.errors.forEach(error => errorMessages.push({ msg: error.detail }))

Check warning on line 7 in src/backend/routes/views/account/post/error.js

Codecov / codecov/patch

src/backend/routes/views/account/post/error.js#L5-L7

Added lines #L5 - L7 were not covered by tests
} catch (e) {
errorMessages.push({ msg: 'An unknown error occurred. Please try again later or ask the support.' })
console.log('Error on parsing server response: ' + body)

Check warning on line 10 in src/backend/routes/views/account/post/error.js

Codecov / codecov/patch

src/backend/routes/views/account/post/error.js#L9-L10

Added lines #L9 - L10 were not covered by tests
}

if (errorMessages.length === 0) {
errorMessages.push({ msg: 'An unknown error occurred. Please try again later or ask the support.' })

Check warning on line 14 in src/backend/routes/views/account/post/error.js

Codecov / codecov/patch

src/backend/routes/views/account/post/error.js#L14

Added line #L14 was not covered by tests
}

flash.class = 'alert-danger'
flash.messages = errorMessages
flash.type = 'Error!'

Check warning on line 19 in src/backend/routes/views/account/post/error.js

Codecov / codecov/patch

src/backend/routes/views/account/post/error.js#L17-L19

Added lines #L17 - L19 were not covered by tests
}
}
69 changes: 69 additions & 0 deletions src/backend/routes/views/account/post/linkGog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
let flash = {}
const request = require('request')
const error = require('./error')
const { check, validationResult } = require('express-validator')

exports = module.exports = function (req, res) {
const locals = res.locals

Check warning on line 7 in src/backend/routes/views/account/post/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/post/linkGog.js#L7

Added line #L7 was not covered by tests

locals.formData = req.body || {}

// validate the input
check('gog_username', 'Username is required').notEmpty()
check('gog_username', 'Username must be at least 3 characters').isLength({ min: 3 })
check('gog_username', 'Username must be at most 100 characters').isLength({ max: 100 })

Check warning on line 14 in src/backend/routes/views/account/post/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/post/linkGog.js#L12-L14

Added lines #L12 - L14 were not covered by tests

// check the validation object for errors
const errors = validationResult(req)

Check warning on line 17 in src/backend/routes/views/account/post/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/post/linkGog.js#L17

Added line #L17 was not covered by tests

// Must have client side errors to fix
if (!errors.isEmpty()) {
// failure
flash.class = 'alert-danger'
flash.messages = errors
flash.type = 'Error!'

Check warning on line 24 in src/backend/routes/views/account/post/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/post/linkGog.js#L22-L24

Added lines #L22 - L24 were not covered by tests

res.render('account/linkGog', { flash })

Check warning on line 26 in src/backend/routes/views/account/post/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/post/linkGog.js#L26

Added line #L26 was not covered by tests
} else {
const gogUsername = req.body.gog_username // this is obtained from the form field in the mixin, not the pug file of this page!

Check warning on line 28 in src/backend/routes/views/account/post/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/post/linkGog.js#L28

Added line #L28 was not covered by tests

const overallRes = res

Check warning on line 30 in src/backend/routes/views/account/post/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/post/linkGog.js#L30

Added line #L30 was not covered by tests

request.post({

Check warning on line 32 in src/backend/routes/views/account/post/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/post/linkGog.js#L32

Added line #L32 was not covered by tests
url: process.env.API_URL + '/users/linkToGog',
headers: { Authorization: 'Bearer ' + req.services.userService.getUser()?.oAuthPassport.token },
form: { gogUsername }
}, function (err, res, body) {

Check warning on line 36 in src/backend/routes/views/account/post/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/post/linkGog.js#L36

Added line #L36 was not covered by tests
if (!err && res.statusCode === 200) {
flash.class = 'alert-success'
flash.messages = [{ msg: 'Your accounts were linked successfully.' }]
flash.type = 'Success!'

Check warning on line 40 in src/backend/routes/views/account/post/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/post/linkGog.js#L38-L40

Added lines #L38 - L40 were not covered by tests

locals.gogToken = '-'
overallRes.render('account/linkGog', { flash })

Check warning on line 43 in src/backend/routes/views/account/post/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/post/linkGog.js#L42-L43

Added lines #L42 - L43 were not covered by tests
} else {
error.parseApiErrors(body, flash)

Check warning on line 45 in src/backend/routes/views/account/post/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/post/linkGog.js#L45

Added line #L45 was not covered by tests

// We need the gog token on the error page as well,
// this code literally does the same as linkGog.js, but due to the architectural structure of this application
// it's not possible to extract it into a separate function while saving any code
request.get({

Check warning on line 50 in src/backend/routes/views/account/post/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/post/linkGog.js#L50

Added line #L50 was not covered by tests
url: process.env.API_URL + '/users/buildGogProfileToken',
headers: { Authorization: 'Bearer ' + req.services.userService.getUser()?.oAuthPassport.token },
form: {}
}, function (err, res, body) {
locals.gogToken = 'unable to obtain token'

Check warning on line 55 in src/backend/routes/views/account/post/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/post/linkGog.js#L54-L55

Added lines #L54 - L55 were not covered by tests
if (err || res.statusCode !== 200) {
flash = {}
error.parseApiErrors(body, flash)
return overallRes.render('account/linkGog', { flash })

Check warning on line 59 in src/backend/routes/views/account/post/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/post/linkGog.js#L57-L59

Added lines #L57 - L59 were not covered by tests
}

locals.gogToken = JSON.parse(body).gogToken

Check warning on line 62 in src/backend/routes/views/account/post/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/post/linkGog.js#L62

Added line #L62 was not covered by tests

return overallRes.render('account/linkGog', { flash })

Check warning on line 64 in src/backend/routes/views/account/post/linkGog.js

Codecov / codecov/patch

src/backend/routes/views/account/post/linkGog.js#L64

Added line #L64 was not covered by tests
})
}
})
}
}
77 changes: 77 additions & 0 deletions src/backend/routes/views/account/post/register.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
const flash = {}
const request = require('request')
const { check, validationResult } = require('express-validator')
require('dotenv').config()

exports = module.exports = function (req, res) {
const locals = res.locals

Check warning on line 7 in src/backend/routes/views/account/post/register.js

Codecov / codecov/patch

src/backend/routes/views/account/post/register.js#L7

Added line #L7 was not covered by tests

locals.formData = req.body || {}
// validate the input
check('username', 'Username is required').notEmpty()
check('username', 'Username must be three or more characters').isLength({ min: 3 })
check('email', 'Email is required').notEmpty()
check('email', 'Email does not appear to be valid').isEmail()

Check warning on line 14 in src/backend/routes/views/account/post/register.js

Codecov / codecov/patch

src/backend/routes/views/account/post/register.js#L11-L14

Added lines #L11 - L14 were not covered by tests

// check the validation object for errors
const errors = validationResult(req)

Check warning on line 17 in src/backend/routes/views/account/post/register.js

Codecov / codecov/patch

src/backend/routes/views/account/post/register.js#L17

Added line #L17 was not covered by tests

// Must have client side errors to fix
if (!errors.isEmpty()) {
// failure
flash.class = 'alert-danger'
flash.messages = errors
flash.type = 'Error!'

Check warning on line 24 in src/backend/routes/views/account/post/register.js

Codecov / codecov/patch

src/backend/routes/views/account/post/register.js#L22-L24

Added lines #L22 - L24 were not covered by tests

res.render('account/register', { flash })

Check warning on line 26 in src/backend/routes/views/account/post/register.js

Codecov / codecov/patch

src/backend/routes/views/account/post/register.js#L26

Added line #L26 was not covered by tests
} else {
// pull the form variables off the request body
const username = req.body.username
const email = req.body.email
const recaptchaResponse = req.body['g-recaptcha-response']

Check warning on line 31 in src/backend/routes/views/account/post/register.js

Codecov / codecov/patch

src/backend/routes/views/account/post/register.js#L29-L31

Added lines #L29 - L31 were not covered by tests

const overallRes = res

Check warning on line 33 in src/backend/routes/views/account/post/register.js

Codecov / codecov/patch

src/backend/routes/views/account/post/register.js#L33

Added line #L33 was not covered by tests

// Run post to register endpoint
request.post({

Check warning on line 36 in src/backend/routes/views/account/post/register.js

Codecov / codecov/patch

src/backend/routes/views/account/post/register.js#L36

Added line #L36 was not covered by tests
url: process.env.API_URL + '/users/register',
form: { username, email, recaptchaResponse }
}, function (err, res, body) {

Check warning on line 39 in src/backend/routes/views/account/post/register.js

Codecov / codecov/patch

src/backend/routes/views/account/post/register.js#L39

Added line #L39 was not covered by tests
let resp
const errorMessages = []

Check warning on line 41 in src/backend/routes/views/account/post/register.js

Codecov / codecov/patch

src/backend/routes/views/account/post/register.js#L41

Added line #L41 was not covered by tests

if (err || res.statusCode !== 200) {
try {
resp = JSON.parse(body)

Check warning on line 45 in src/backend/routes/views/account/post/register.js

Codecov / codecov/patch

src/backend/routes/views/account/post/register.js#L44-L45

Added lines #L44 - L45 were not covered by tests
} catch (e) {
errorMessages.push({ msg: 'Invalid registration sign up. Please try again later.' })
flash.class = 'alert-danger'
flash.messages = errorMessages
flash.type = 'Error!'

Check warning on line 50 in src/backend/routes/views/account/post/register.js

Codecov / codecov/patch

src/backend/routes/views/account/post/register.js#L47-L50

Added lines #L47 - L50 were not covered by tests

return overallRes.render('account/register', { flash, recaptchaSiteKey: process.env.RECAPTCHA_SITE_KEY })

Check warning on line 52 in src/backend/routes/views/account/post/register.js

Codecov / codecov/patch

src/backend/routes/views/account/post/register.js#L52

Added line #L52 was not covered by tests
}

// Failed registering user
for (let i = 0; i < resp.errors.length; i++) {
const error = resp.errors[i]

Check warning on line 57 in src/backend/routes/views/account/post/register.js

Codecov / codecov/patch

src/backend/routes/views/account/post/register.js#L56-L57

Added lines #L56 - L57 were not covered by tests

errorMessages.push({ msg: error.detail })

Check warning on line 59 in src/backend/routes/views/account/post/register.js

Codecov / codecov/patch

src/backend/routes/views/account/post/register.js#L59

Added line #L59 was not covered by tests
}

flash.class = 'alert-danger'
flash.messages = errorMessages
flash.type = 'Error!'

Check warning on line 64 in src/backend/routes/views/account/post/register.js

Codecov / codecov/patch

src/backend/routes/views/account/post/register.js#L62-L64

Added lines #L62 - L64 were not covered by tests

return overallRes.render('account/register', { flash, recaptchaSiteKey: process.env.RECAPTCHA_SITE_KEY })

Check warning on line 66 in src/backend/routes/views/account/post/register.js

Codecov / codecov/patch

src/backend/routes/views/account/post/register.js#L66

Added line #L66 was not covered by tests
}

// Successfully registered user
flash.class = 'alert-success'
flash.messages = [{ msg: 'Please check your email to verify your registration. Then you will be ready to log in!' }]
flash.type = 'Success!'

Check warning on line 72 in src/backend/routes/views/account/post/register.js

Codecov / codecov/patch

src/backend/routes/views/account/post/register.js#L70-L72

Added lines #L70 - L72 were not covered by tests

overallRes.render('account/register', { flash })

Check warning on line 74 in src/backend/routes/views/account/post/register.js

Codecov / codecov/patch

src/backend/routes/views/account/post/register.js#L74

Added line #L74 was not covered by tests
})
}
}
126 changes: 126 additions & 0 deletions src/backend/routes/views/account/post/report.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
const { validationResult, body, matchedData } = require('express-validator')
const getReportRoute = require('../get/report')

exports = module.exports = [
body('offender', 'Offender invalid').isString().isLength({ min: 2, max: 50 }).trim().escape(),
body('report_description', 'Please describe the incident').notEmpty().isLength({ min: 2, max: 2000 }).trim().escape(),
body('game_id', 'Please enter a valid game ID, or nothing. The # is not needed.').optional({ values: 'falsy' }).isDecimal(),
async function (req, res) {
const javaApiClient = req.requestContainer.get('JavaApiClient')
const errors = validationResult(req)

Check warning on line 10 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L8-L10

Added lines #L8 - L10 were not covered by tests

if (!errors.isEmpty()) {
req.query.flash = Buffer.from(JSON.stringify({

Check warning on line 13 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L13

Added line #L13 was not covered by tests
class: 'alert-danger',
messages: errors,
type: 'Error!'
})).toString('base64')

return getReportRoute(req, res)

Check warning on line 19 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L19

Added line #L19 was not covered by tests
}

const formData = matchedData(req)

Check warning on line 22 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L22

Added line #L22 was not covered by tests

let apiUsers
try {
const userFetch = await javaApiClient.get('/data/player?filter=login==' + encodeURIComponent(formData.offender) + '&fields[player]=login&page[size]=1')

Check warning on line 26 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L25-L26

Added lines #L25 - L26 were not covered by tests

if (userFetch.status !== 200) {
throw new Error('issues getting players')

Check warning on line 29 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L29

Added line #L29 was not covered by tests
}

apiUsers = JSON.parse(userFetch.data)

Check warning on line 32 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L32

Added line #L32 was not covered by tests
} catch (e) {
req.query.flash = Buffer.from(JSON.stringify({

Check warning on line 34 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L34

Added line #L34 was not covered by tests
class: 'alert-danger',
messages: { errors: [{ msg: 'Error while fetching offender' }] },
type: 'Error!'
})).toString('base64')

return getReportRoute(req, res)

Check warning on line 40 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L40

Added line #L40 was not covered by tests
}

// Mapping users to their IDs
let offenderId = null
apiUsers.data.forEach((user) => {

Check warning on line 45 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L44-L45

Added lines #L44 - L45 were not covered by tests
if (user.attributes.login.toUpperCase() === formData.offender.toUpperCase()) {
offenderId = user.id

Check warning on line 47 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L47

Added line #L47 was not covered by tests
}
})

if (!offenderId) {
req.query.flash = Buffer.from(JSON.stringify({

Check warning on line 52 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L52

Added line #L52 was not covered by tests
class: 'alert-danger',
messages: { errors: [{ msg: 'The following user could not be found : ' + formData.offender }] },
type: 'Error!'
})).toString('base64')

return getReportRoute(req, res)

Check warning on line 58 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L58

Added line #L58 was not covered by tests
}

// Checking the game exists
if (formData.game_id != null) {
try {
const response = await javaApiClient.get('/data/game?filter=id==' + encodeURIComponent(formData.game_id))

Check warning on line 64 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L63-L64

Added lines #L63 - L64 were not covered by tests
if (response.status !== 200) {
throw new Error('issues getting game')

Check warning on line 66 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L66

Added line #L66 was not covered by tests
}
} catch (e) {
req.query.flash = Buffer.from(JSON.stringify({

Check warning on line 69 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L69

Added line #L69 was not covered by tests
class: 'alert-danger',
messages: { errors: [{ msg: 'The game could not be found. Please check the game ID you provided.' }] },
type: 'Error!'
})).toString('base64')

return getReportRoute(req, res)

Check warning on line 75 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L75

Added line #L75 was not covered by tests
}
}

const relationShips = {

Check warning on line 79 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L79

Added line #L79 was not covered by tests
reportedUsers: {
data: [{
type: 'player',
id: '' + offenderId
}]
}
}

if (formData.game_id != null) {
relationShips.game = { data: { type: 'game', id: '' + formData.game_id } }

Check warning on line 89 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L89

Added line #L89 was not covered by tests
}

const report =
{

Check warning on line 93 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L93

Added line #L93 was not covered by tests
data: [
{
type: 'moderationReport',
attributes: {
gameIncidentTimecode: (formData.game_timecode ? formData.game_timecode : null),
reportDescription: formData.report_description
},
relationships: relationShips
}
]
}

const resp = await javaApiClient.post('/data/moderationReport', JSON.stringify(report), {

Check warning on line 106 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L106

Added line #L106 was not covered by tests
headers: {
'Content-Type': 'application/vnd.api+json',
Accept: 'application/vnd.api+json'
}
})

if (resp.status !== 201) {
const apiError = JSON.parse(resp.data)
req.query.flash = Buffer.from(JSON.stringify({

Check warning on line 115 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L114-L115

Added lines #L114 - L115 were not covered by tests
class: 'alert-danger',
messages: { errors: [{ msg: 'Error while submitting the report form' }, { msg: apiError.errors?.[0]?.detail || 'unknown api error' }] },
type: 'Error!'
})).toString('base64')

return getReportRoute(req, res)

Check warning on line 121 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L121

Added line #L121 was not covered by tests
}

res.redirect('../report_submitted')

Check warning on line 124 in src/backend/routes/views/account/post/report.js

Codecov / codecov/patch

src/backend/routes/views/account/post/report.js#L124

Added line #L124 was not covered by tests
}
]
54 changes: 54 additions & 0 deletions src/backend/routes/views/account/post/requestPasswordReset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const flash = {}
const request = require('request')
const error = require('./error')
const { check, validationResult } = require('express-validator')

exports = module.exports = function (req, res) {
const locals = res.locals

Check warning on line 7 in src/backend/routes/views/account/post/requestPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/requestPasswordReset.js#L7

Added line #L7 was not covered by tests

locals.formData = req.body || {}

// validate the input
check('usernameOrEmail', 'Username or email is required').notEmpty()

Check warning on line 12 in src/backend/routes/views/account/post/requestPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/requestPasswordReset.js#L12

Added line #L12 was not covered by tests

// check the validation object for errors
const errors = validationResult(req)

Check warning on line 15 in src/backend/routes/views/account/post/requestPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/requestPasswordReset.js#L15

Added line #L15 was not covered by tests

// Must have client side errors to fix
if (!errors.isEmpty()) {
flash.class = 'alert-danger'
flash.messages = errors
flash.type = 'Error!'

Check warning on line 21 in src/backend/routes/views/account/post/requestPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/requestPasswordReset.js#L19-L21

Added lines #L19 - L21 were not covered by tests

res.render('account/requestPasswordReset', { flash })

Check warning on line 23 in src/backend/routes/views/account/post/requestPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/requestPasswordReset.js#L23

Added line #L23 was not covered by tests
} else {
const identifier = req.body.usernameOrEmail
const recaptchaResponse = req.body['g-recaptcha-response']

Check warning on line 26 in src/backend/routes/views/account/post/requestPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/requestPasswordReset.js#L25-L26

Added lines #L25 - L26 were not covered by tests

const overallRes = res

Check warning on line 28 in src/backend/routes/views/account/post/requestPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/requestPasswordReset.js#L28

Added line #L28 was not covered by tests

// Run post to reset endpoint
request.post({

Check warning on line 31 in src/backend/routes/views/account/post/requestPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/requestPasswordReset.js#L31

Added line #L31 was not covered by tests
url: process.env.API_URL + '/users/requestPasswordReset',
form: { identifier, recaptchaResponse }
}, function (err, res, body) {

Check warning on line 34 in src/backend/routes/views/account/post/requestPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/requestPasswordReset.js#L34

Added line #L34 was not covered by tests
if (err || res.statusCode !== 200) {
error.parseApiErrors(body, flash)
return overallRes.render('account/requestPasswordReset', {

Check warning on line 37 in src/backend/routes/views/account/post/requestPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/requestPasswordReset.js#L36-L37

Added lines #L36 - L37 were not covered by tests
flash,
recaptchaSiteKey: process.env.RECAPTCHA_SITE_KEY
})
}

// Successfully reset password
flash.class = 'alert-success'
flash.messages = [{ msg: 'Your password is in the process of being reset, please reset your password by clicking on the link provided in an email.' }]
flash.type = 'Success!'

Check warning on line 46 in src/backend/routes/views/account/post/requestPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/requestPasswordReset.js#L44-L46

Added lines #L44 - L46 were not covered by tests

overallRes.render('account/requestPasswordReset', {

Check warning on line 48 in src/backend/routes/views/account/post/requestPasswordReset.js

Codecov / codecov/patch

src/backend/routes/views/account/post/requestPasswordReset.js#L48

Added line #L48 was not covered by tests
flash,
recaptchaSiteKey: process.env.RECAPTCHA_SITE_KEY
})
})
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
const express = require('../../ExpressApp')
const router = express.Router();
const router = express.Router()
const middlewares = require('../middleware')


router.get('/linkGog', middlewares.isAuthenticated(), require('./account/get/linkGog'))
router.post('/linkGog', middlewares.isAuthenticated(), require('./account/post/linkGog'))

@@ -24,10 +23,9 @@ router.post('/password/confirmReset', require('./account/post/confirmPasswordRes
router.get('/requestPasswordReset', require('./account/get/requestPasswordReset'))
router.post('/requestPasswordReset', require('./account/post/requestPasswordReset'))

//still used in other applications (user-service, game-client etc.)
// still used in other applications (user-service, game-client etc.)
router.get('/password/reset', (req, res) => res.redirect('/account/requestPasswordReset'))


router.get('/register', require('./account/get/register'))
router.post('/register', require('./account/post/register'))

8 changes: 4 additions & 4 deletions routes/views/auth.js → src/backend/routes/views/auth.js
Original file line number Diff line number Diff line change
@@ -3,16 +3,16 @@
const express = require('../../ExpressApp')
const router = express.Router()

router.get('/login', passport.authenticate(appConfig.oauth.strategy));
router.get('/login', passport.authenticate(appConfig.oauth.strategy))

router.get(
'/' + appConfig.oauth.callback,
(req, res, next)=>{
(req, res, next) => {

Check warning on line 10 in src/backend/routes/views/auth.js

Codecov / codecov/patch

src/backend/routes/views/auth.js#L10

Added line #L10 was not covered by tests
res.locals.returnTo = req.session.returnTo

return next()
},
passport.authenticate(appConfig.oauth.strategy, {failureRedirect: '/login', failureFlash: true}),
passport.authenticate(appConfig.oauth.strategy, { failureRedirect: '/login', failureFlash: true }),
(req, res) => {
res.redirect(res.locals.returnTo || '/')
}
19 changes: 19 additions & 0 deletions src/backend/routes/views/checkUsername.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const request = require('request')

exports = module.exports = function (req, res) {
const name = req.query.username

Check warning on line 4 in src/backend/routes/views/checkUsername.js

Codecov / codecov/patch

src/backend/routes/views/checkUsername.js#L4

Added line #L4 was not covered by tests

request(process.env.API_URL + '/data/player?filter=login==' + encodeURI(name), function (error, response, body) {

Check warning on line 6 in src/backend/routes/views/checkUsername.js

Codecov / codecov/patch

src/backend/routes/views/checkUsername.js#L6

Added line #L6 was not covered by tests
if (error) {
console.error(error)
return res.status(500).send(error)

Check warning on line 9 in src/backend/routes/views/checkUsername.js

Codecov / codecov/patch

src/backend/routes/views/checkUsername.js#L8-L9

Added lines #L8 - L9 were not covered by tests
}

try {
const userNameFree = JSON.parse(body).data.length === 0

Check warning on line 13 in src/backend/routes/views/checkUsername.js

Codecov / codecov/patch

src/backend/routes/views/checkUsername.js#L12-L13

Added lines #L12 - L13 were not covered by tests
return res.status(userNameFree ? 200 : 400).send(userNameFree)
} catch (e) {
return res.status(500).send(e)

Check warning on line 16 in src/backend/routes/views/checkUsername.js

Codecov / codecov/patch

src/backend/routes/views/checkUsername.js#L16

Added line #L16 was not covered by tests
}
})
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const express = require('../../ExpressApp')
const router = express.Router();
const router = express.Router()

// This will be replaced soon, therefor I did not spend time on it
router.get('*', (req, res) => res.status(503).render('errors/503-known-issue'));
router.get('*', (req, res) => res.status(503).render('errors/503-known-issue'))

module.exports = router
92 changes: 92 additions & 0 deletions src/backend/routes/views/clans/get/accept_invite.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
const request = require('request')

exports = module.exports = function (req, res) {
const locals = res.locals

// locals.section is used to set the currently selected
// item in the header navigation.
locals.section = 'clan'
const flash = {}

if (!req.query.i) {
flash.type = 'Error!'
flash.class = 'alert-danger'
flash.messages = [{ msg: 'The invitation link is wrong or truncated. Key informations are missing.' }]

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return res.redirect('/clans?flash=' + data)
}

const invitationId = req.query.i

if (!req.app.locals.clanInvitations[invitationId]) {
flash.type = 'Error!'
flash.class = 'alert-danger'
flash.messages = [{ msg: 'The invitation link is wrong or truncated. Invite code missing from website clan map.' }]

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return res.redirect('/clans?flash=' + data)
}

const invite = req.app.locals.clanInvitations[invitationId]
const clanId = invite.clan

if (req.user.data.attributes.clan != null) {
// User is already in a clan!
return res.redirect(`/clans/${req.user.data.attributes.clan.tag}?member=true`)
}

const queryUrl = process.env.API_URL +
'/data/clan/' + clanId +
'?include=memberships.player' +
'&fields[clan]=createTime,description,name,tag,updateTime,websiteUrl,founder,leader' +
'&fields[player]=login,updateTime'

request.get(
{
url: queryUrl
},
function (err, childRes, body) {
const clan = JSON.parse(body)

if (err || !clan.data) {
flash.type = 'Error!'
flash.class = 'alert-danger'
flash.messages = [{ msg: 'The clan you want to join is invalid or does no longer exist' }]

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return res.redirect('./?flash=' + data)
}

locals.clanName = clan.data.attributes.name
locals.clanLeaderName = '<unknown>'

for (const k in clan.included) {
let player = null
switch (clan.included[k].type) {
case 'player':
player = clan.included[k]

// Getting the leader name
if (player.id === clan.data.relationships.leader.data.id) {
locals.clanLeaderName = player.attributes.login
}

break
}
}

const token = invite.token
locals.acceptURL = `/clans/join?clan_id=${clanId}&token=${token}`

// Render the view
res.render('clans/accept_invite')
}
)
}
53 changes: 53 additions & 0 deletions src/backend/routes/views/clans/get/create.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
exports = module.exports = function (req, res) {
const locals = res.locals

// locals.section is used to set the currently selected
// item in the header navigation.
locals.section = 'clan'

const request = require('request')

request.get(
{
url: process.env.API_URL + '/clans/me',
headers: {
Authorization: 'Bearer ' + req.user.data.attributes.token
}
},
function (err, childRes, body) {
if (err) {
return res.redirect('/clans/manage')
}

const clanInfo = JSON.parse(body)
if (clanInfo.clan != null) {
res.redirect('/clans/manage')
return
}

locals.formData = req.body || {}
locals.clan_name = req.query.clan_name || ''
locals.clan_tag = req.query.clan_tag || ''
locals.clan_description = req.query.clan_description || ''
locals.clan_create_time = (new Date()).toUTCString()

let flash = null

if (req.query.flash) {
const buff = Buffer.from(req.query.flash, 'base64')
const text = buff.toString('ascii')

try {
flash = JSON.parse(text)
} catch (e) {
console.error('Parsing error while trying to decode a flash error: ' + text)
console.error(e)
flash = [{ msg: 'Unknown error' }]
}
}

// Render the view
res.render('clans/create', { flash })
}
)
}
129 changes: 129 additions & 0 deletions src/backend/routes/views/clans/get/manage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
const request = require('request')

exports = module.exports = function (req, res) {
const locals = res.locals

// locals.section is used to set the currently selected
// item in the header navigation.
locals.section = 'clan'

let flash = null

let clanMembershipId = null
try {
clanMembershipId = req.user.data.attributes.clan.membershipId
} catch {
// The user doesnt belong to a clan
res.redirect('/clans')
return
}

// In case the user has just generated an invite link
if (req.query.invitation_id) {
flash = {}
flash.class = 'alert-invite'

flash.messages = [
{
msg:
`<p><a id='inviteLink' onclick='return false' href='${process.env.HOST}/clans/accept_invite?i=${req.query.invitation_id}'> Right click on me and copy the invitation link</a></p>Note: It only works for the user you typed.`
}
]
flash.type = ''
}

request.get(
{
url:
process.env.API_URL +
'/data/clanMembership/' + clanMembershipId + '/clan' +
'?include=memberships.player' +
'&fields[clan]=createTime,description,name,tag,updateTime,websiteUrl,founder,leader' +
'&fields[player]=login,updateTime' +
'&fields[clanMembership]=createTime,player',
headers: {
Authorization: 'Bearer ' + req.user.data.attributes.token
}
},
function (err, childRes, body) {
const clan = JSON.parse(body)

if (err || !clan.data) {
flash = {}
flash.class = 'alert-danger'
flash.messages = [{ msg: 'Unknown error while retrieving your clan information' }]
flash.type = 'Error!'

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return res.redirect('/clans?flash=' + data)
}

if (clan.data.relationships.leader.data.id !== req.user.data.id) {
// Not the leader! Shouldn't be able to manage stuff
res.redirect(`/clans/${req.user.data.attributes.clan.tag}?member=true`)
return
}

locals.clan_name = clan.data.attributes.name
locals.clan_tag = clan.data.attributes.tag
locals.clan_description = clan.data.attributes.description
locals.clan_create_time = clan.data.attributes.createTime
locals.me = req.user.data.id
locals.clan_id = clan.data.id
locals.clan_link = process.env.HOST + '/clans/see?id=' + clan.data.id

const members = {}
let player = null
let membership = null
let member = null

for (const k in clan.included) {
switch (clan.included[k].type) {
case 'player':
player = clan.included[k]
if (!members[player.id]) members[player.id] = {}
members[player.id].id = player.id
members[player.id].name = player.attributes.login

if (clan.data.relationships.founder.data.id === player.id) {
locals.founder_name = player.attributes.login
}
break

case 'clanMembership':
membership = clan.included[k]
member = membership.relationships.player.data
if (!members[member.id]) members[member.id] = {}
members[member.id].id = member.id
members[member.id].membershipId = membership.id
members[member.id].joinedAt = membership.attributes.createTime
break
}
}

locals.clan_members = members

if (req.originalUrl === '/clan_created') {
flash = {}
flash.class = 'alert-success'
flash.messages = [{ msg: 'You have successfully created your clan' }]
flash.type = 'Success!'
} else if (req.query.flash) {
const buff = Buffer.from(req.query.flash, 'base64')
const text = buff.toString('ascii')
try {
flash = JSON.parse(text)
} catch (e) {
console.error('Parsing error while trying to decode a flash error: ' + text)
console.error(e)
flash = [{ msg: 'Unknown error' }]
}
}

// Render the view
res.render('clans/manage', { flash })
}
)
}
96 changes: 96 additions & 0 deletions src/backend/routes/views/clans/post/create.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
const flash = {}
const request = require('request')
const { check, validationResult } = require('express-validator')

exports = module.exports = async function (req, res) {
const locals = res.locals

locals.formData = req.body || {}

const overallRes = res

// validate the input
check('clan_tag', 'Please indicate the clan tag - No special characters and 3 characters maximum').notEmpty().isLength({ max: 3 })
check('clan_description', 'Please add a description for your clan').notEmpty().isLength({ max: 1000 })
check('clan_name', "Please indicate your clan's name").notEmpty().isLength({ max: 40 })

// check the validation object for errors
const errors = validationResult(req)

// Must have client side errors to fix
if (!errors.isEmpty()) {
flash.class = 'alert-danger'
flash.messages = errors
flash.type = 'Error!'

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return overallRes.redirect('create?flash=' + data)
} else {
const clanName = req.body.clan_name
const clanTag = req.body.clan_tag
const clanDescription = req.body.clan_description

const queryUrl =
process.env.API_URL +
'/clans/create' +
'?name=' + encodeURIComponent(clanName) +
'&tag=' + encodeURIComponent(clanTag) +
'&description=' + encodeURIComponent(clanDescription)

// Run post to endpoint
request.post({
url: queryUrl,
body: '',
headers: {
Authorization: 'Bearer ' + req.user.data.attributes.token
}
}, function (err, res, body) {
const errorMessages = []

if (err || res.statusCode !== 200) {
let msg = 'Error while creating the clan'
try {
msg += ': ' + JSON.stringify(JSON.parse(res.body).errors[0].detail)
} catch {
}
errorMessages.push({ msg })
flash.class = 'alert-danger'
flash.messages = errorMessages
flash.type = 'Error!'

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return overallRes.redirect('create?flash=' + data + '&clan_name=' + clanName + '&clan_tag=' + clanTag + '&clan_description=' + clanDescription + '')
}

// Refreshing user
request.get({
url: process.env.API_URL + '/me',
headers: {
Authorization: 'Bearer ' + req.user.data.attributes.token
}
},
function (err, res, body) {
if (err) {
console.error('There was an error updating a session after a clan creation')

return
}
try {
const user = JSON.parse(body)
user.data.attributes.token = req.user.data.attributes.token
user.data.id = user.data.attributes.userId
req.logIn(user, function (err) {
if (err) console.error(err)
return overallRes.redirect('/clans/manage')
})
} catch {
console.error('There was an error updating a session after a clan creation')
}
})
})
}
}
96 changes: 96 additions & 0 deletions src/backend/routes/views/clans/post/destroy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
let flash = {}
const request = require('request')
const { check, validationResult } = require('express-validator')

exports = module.exports = async function (req, res) {
const locals = res.locals

locals.formData = req.body || {}

const overallRes = res

// validate the input
check('clan_id', 'Internal error while processing your query: invalid clan ID').notEmpty()

// check the validation object for errors
const errors = validationResult(req)

// Must have client side errors to fix
if (!errors.isEmpty()) {
flash.class = 'alert-danger'
flash.messages = errors
flash.type = 'Error!'

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return overallRes.redirect('manage?flash=' + data)
} else {
// Building update query
const queryUrl =
process.env.API_URL +
'/data/clan/' + req.body.clan_id

// Run post to endpoint
request.delete({
url: queryUrl,
body: '',
headers: {
Authorization: 'Bearer ' + req.user.data.attributes.token
}
}, function (err, res, body) {
const errorMessages = []

if (err || res.statusCode !== 204) {
let msg = 'Error while destroying the clan'
try {
msg += ': ' + JSON.stringify(JSON.parse(res.body).errors[0].detail)
} catch {}
errorMessages.push({ msg })
flash.class = 'alert-danger'
flash.messages = errorMessages
flash.type = 'Error!'

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return overallRes.redirect('manage?flash=' + data)
}

flash = {}
flash.class = 'alert-success'
flash.messages = [{ msg: 'The clan was successfully destroyed' }]
flash.type = 'Success!'

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

// Refreshing user
request.get({
url: process.env.API_URL + '/me',
headers: {
Authorization: 'Bearer ' + req.user.data.attributes.token
}
},

function (err, res, body) {
if (err) {
console.error('There was an error updating a session after a clan destruction')

return
}
try {
const user = JSON.parse(body)
user.data.id = user.data.attributes.userId
user.data.attributes.token = req.user.data.attributes.token
req.logIn(user, function (err) {
if (err) console.error(err)
return overallRes.redirect('/clans?flash=' + data)
})
} catch {
console.error('There was an error updating a session after a clan destruction')
}
})
})
}
}
143 changes: 143 additions & 0 deletions src/backend/routes/views/clans/post/invite.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
const flash = {}
const request = require('request')
const { check, validationResult } = require('express-validator')

function promiseRequest (url) {
return new Promise(function (resolve, reject) {
request(url, function (error, res, body) {
if (!error && res.statusCode < 300) {
resolve(body)
} else {
console.error('Call to ' + url + ' failed: ' + error)
reject(error)
}
})
})
}

function setLongTimeout (func, delayMs) {
const maxDelay = 214748364 - 1 // JS Limit for 32 bit integers

if (delayMs > maxDelay) {
const remainingDelay = delayMs - maxDelay

// we cut it in smaller, edible chunks
setTimeout(() => {
setLongTimeout(func, remainingDelay)
}, maxDelay)
} else {
setTimeout(func, delayMs)
}
}

exports = module.exports = async function (req, res) {
const locals = res.locals

locals.formData = req.body || {}

const overallRes = res

// validate the input
check('invited_player', 'Please indicate the player name').notEmpty()

// check the validation object for errors
const errors = validationResult(req)

// Must have client side errors to fix
if (!errors.isEmpty()) {
flash.class = 'alert-danger'
flash.messages = errors
flash.type = 'Error!'

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return overallRes.redirect('manage?flash=' + data)
} else {
const clanId = req.body.clan_id
const userName = req.body.invited_player

// Let's check first that the player exists
const fetchRoute = process.env.API_URL + '/data/player?filter=login=="' + userName + '"&fields[player]='

let playerData = null
let playerId = null
try {
const httpData = await promiseRequest(fetchRoute)
playerData = JSON.parse(httpData).data
playerId = playerData[0].id
} catch (e) {
flash.class = 'alert-danger'
flash.messages = [{ msg: 'The player ' + userName + " doesn't seem to exist" + e }]
flash.type = 'Error!'

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return overallRes.redirect('manage?flash=' + data)
}

const queryUrl =
process.env.API_URL +
'/clans/generateInvitationLink' +
'?clanId=' + encodeURIComponent(clanId) +
'&playerId=' + encodeURIComponent(playerId)

// Run post to endpoint
request.get({
url: queryUrl,
body: '',
headers: {
Authorization: 'Bearer ' + req.user.data.attributes.token
}
}, function (err, res, body) {
if (err || res.statusCode !== 200) {
const errorMessages = []
let msg = 'Error while generating the invite link'
try {
msg += ': ' + JSON.stringify(JSON.parse(res.body).errors[0].detail)
} catch {
}

errorMessages.push({ msg })
flash.class = 'alert-danger'
flash.messages = errorMessages
flash.type = 'Error!'

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')
return overallRes.redirect('manage?flash=' + data)
} else {
try {
const token = JSON.parse(res.body).jwtToken

const id = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5).toUpperCase()

req.app.locals.clanInvitations[id] = {
token,
clan: clanId
}

// We use timeout here because if we delete the invite link whenver the page is GET,
// then discord and other messaging applications will destroy the link accidentally
// when pre-fetching the page. So we will delete it later. Regardless if the website is restarted all the links will be
// killed instantly, which is fine. They are short lived by design.
const lifespan = process.env.CLAN_INVITES_LIFESPAN_DAYS * 24 * 3600 * 1000
setLongTimeout(() => {
delete req.app.locals.clanInvitations[id]
console.log(`Killed invitation with id ${id} after having waited ${lifespan} seconds (${process.env.CLAN_INVITES_LIFESPAN_DAYS} days)`)
}, lifespan)

return overallRes.redirect('manage?invitation_id=' + id)
} catch (e) {
flash.class = 'alert-danger'
flash.messages = [{ msg: 'Unkown error while generating the invite link: ' + e }]
flash.type = 'Error!'
const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')
return overallRes.redirect('manage?flash=' + data)
}
}
})
}
}
93 changes: 93 additions & 0 deletions src/backend/routes/views/clans/post/join.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
const request = require('request')

exports = module.exports = function (req, res) {
const locals = res.locals

// locals.section is used to set the currently selected
// item in the header navigation.
locals.section = 'clan'

const flash = {}
const overallRes = res

if (!req.query.token || !req.query.clan_id) {
flash.type = 'Error!'
flash.class = 'alert-danger'
flash.messages = [{ msg: 'The invitation link is invalid!' }]

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return res.redirect('/clans?flash=' + data + '')
}

const token = req.query.token

request.post(
{
url: process.env.API_URL + '/clans/joinClan?token=' + token,
headers: {
Authorization: 'Bearer ' + req.user.data.attributes.token
}
},
function (err, childRes, body) {
if (err) {
console.error('There was an error at join')

return
}

let flashData
if (childRes.statusCode === 200 || childRes.statusCode === 201) {
flash.class = 'alert-success'
flash.messages = [
{ msg: 'Welcome to your new clan!' }
]
flash.type = 'Success!'
const buff = Buffer.from(JSON.stringify(flash))
flashData = buff.toString('base64')

// Refreshing user
return request.get({
url: process.env.API_URL + '/me',
headers: {
Authorization: 'Bearer ' + req.user.data.attributes.token
}
},

function (err, res, body) {
if (err) {
console.error('There was an error updating a session after an user left a clan')

return
}
try {
const user = JSON.parse(body)
user.data.id = user.data.attributes.userId
user.data.attributes.token = req.user.data.attributes.token
req.logIn(user, function (err) {
if (err) console.error(err)
return overallRes.redirect(`${user.data.attributes.clan.tag}?member=true&flash=${flashData}`)
})
} catch {
console.error('There was an error updating a session after an user left a clan')
}
})
} else {
flash.type = 'Error!'
flash.class = 'alert-danger'
let msg = 'The invitation is invalid or has expired, or you are already part of a clan'
try {
msg += ': ' + JSON.stringify(JSON.parse(childRes.body).errors[0].detail)
} catch {}

flash.messages = [{ msg }]

const buff = Buffer.from(JSON.stringify(flash))
flashData = buff.toString('base64')

return overallRes.redirect('/clans?flash=' + flashData + '')
}
}
)
}
76 changes: 76 additions & 0 deletions src/backend/routes/views/clans/post/kick.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
let flash = {}
const request = require('request')
const { check, validationResult } = require('express-validator')

exports = module.exports = async function (req, res) {
const locals = res.locals

locals.formData = req.body || {}

const overallRes = res

// validate the input
check('clan_id', 'Internal error while processing your query: invalid clan ID').notEmpty()
check('membership_id', 'Internal error while processing your query: invalid member ID').notEmpty()

// check the validation object for errors
let errors = validationResult(req)

// Should not happen normally, but you never know
if (req.body.membership_id === req.user.clan.membershipId) errors = [{ msg: 'You cannot kick yourself' }]

// Must have client side errors to fix
if (!errors.isEmpty()) {
flash.class = 'alert-danger'
flash.messages = errors
flash.type = 'Error!'

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return overallRes.redirect('manage?flash=' + data)
} else {
// Building update query
const membershipId = req.body.membership_id
const queryUrl =
process.env.API_URL +
'/data/clanMembership/' + membershipId

// Run post to endpoint
request.delete({
url: queryUrl,
body: '',
headers: {
Authorization: 'Bearer ' + req.user.data.attributes.token
}
}, function (err, res, body) {
const errorMessages = []

if (err || res.statusCode !== 204) {
let msg = 'Error while removing the member'
try {
msg += ': ' + JSON.stringify(JSON.parse(res.body).errors[0].detail)
} catch {}
errorMessages.push({ msg })
flash.class = 'alert-danger'
flash.messages = errorMessages
flash.type = 'Error!'

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return overallRes.redirect('manage?flash=' + data)
}

flash = {}
flash.class = 'alert-success'
flash.messages = [{ msg: 'The member was kicked' }]
flash.type = 'Success!'

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return overallRes.redirect('manage?flash=' + data)
})
}
}
93 changes: 93 additions & 0 deletions src/backend/routes/views/clans/post/leave.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
let flash = {}
const request = require('request')
const { check, validationResult } = require('express-validator')

exports = module.exports = async function (req, res) {
const locals = res.locals

locals.formData = req.body || {}

const overallRes = res

// validate the input
check('clan_id', 'Internal error while processing your query: invalid clan ID').notEmpty()
check('membership_id', 'Internal error while processing your query: invalid member ID').notEmpty()

// check the validation object for errors
const errors = validationResult(req)

// Must have client side errors to fix
if (!errors.isEmpty()) {
flash.class = 'alert-danger'
flash.messages = errors
flash.type = 'Error!'

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return overallRes.redirect('/clans?flash=' + data)
} else {
// Building update query
// Run post to endpoint
request.delete({
url: `${process.env.API_URL}/data/clanMembership/${req.user.clan.membershipId}`,
headers: {
Authorization: 'Bearer ' + req.user.data.attributes.token
}
}, function (err, res, body) {
const errorMessages = []

if (err || res.statusCode !== 204) {
let msg = 'Error while leaving the clan'
try {
msg += ': ' + JSON.stringify(JSON.parse(res.body).errors[0].detail)
} catch {
errorMessages.push({ msg })
flash.class = 'alert-danger'
flash.messages = errorMessages
flash.type = 'Error!'
}

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return overallRes.redirect('/clans?flash=' + data)
}

flash = {}
flash.class = 'alert-success'
flash.messages = [{ msg: 'You left the clan' }]
flash.type = 'Success!'

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

// Refreshing user
request.get({
url: process.env.API_URL + '/me',
headers: {
Authorization: 'Bearer ' + req.user.data.attributes.token
}
},

function (err, res, body) {
if (err) {
console.error('There was an error updating a session after an user left a clan')

return
}
try {
const user = JSON.parse(body)
user.data.id = user.data.attributes.userId
user.data.attributes.token = req.user.data.attributes.token
req.logIn(user, function (err) {
if (err) console.error(err)
return overallRes.redirect('/clans?flash=' + data)
})
} catch {
console.error('There was an error updating a session after an user left a clan')
}
})
})
}
}
153 changes: 153 additions & 0 deletions src/backend/routes/views/clans/post/transfer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
const flash = {}
const request = require('request')
const { check, validationResult } = require('express-validator')

function promiseRequest (url) {
return new Promise(function (resolve, reject) {
request(url, function (error, res, body) {
if (!error && res.statusCode < 300) {
resolve(body)
} else {
reject(error || `Unexpected status code ${res.statusCode}`)
}
})
})
}

exports = module.exports = async function (req, res) {
const locals = res.locals

locals.formData = req.body || {}

const overallRes = res

// validate the input
check('transfer_to', 'Please indicate the recipient name').notEmpty()
check('clan_id', 'Internal error while processing your query: invalid clan ID').notEmpty()

// check the validation object for errors
const errors = validationResult(req)

// Must have client side errors to fix
if (!errors.isEmpty()) {
flash.class = 'alert-danger'
flash.messages = errors
flash.type = 'Error!'

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return overallRes.redirect('manage?flash=' + data)
} else {
const clanId = req.body.clan_id
const userName = req.body.transfer_to

// Let's check first that the player exists AND is part of this clan
const fetchRoute = process.env.API_URL + '/data/clan/' + clanId + '?include=memberships.player&fields[player]=login'

let playerId = null

try {
if (userName === req.user.data.attributes.userName) throw new Error('You cannot transfer your own clan to yourself')

const httpData = await promiseRequest(fetchRoute)
const clanData = JSON.parse(httpData)

const members = {}

for (const k in clanData.included) {
const record = clanData.included[k]
if (record.type !== 'player') continue
members[record.attributes.login] = record.id
}

if (!members[userName]) throw new Error('User does not exist or is not part of the clan')
playerId = members[userName]
} catch (e) {
flash.class = 'alert-danger'
flash.messages = [{ msg: 'There was an error during the transfer to ' + userName + ': ' + e }]
flash.type = 'Error!'

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return overallRes.redirect('manage?flash=' + data)
}

// Building update query
const queryUrl =
process.env.API_URL +
'/data/clan/' + clanId

const newClanObject =
{
data: {
type: 'clan',
id: clanId,
relationships: {
leader: {
data: {
id: playerId,
type: 'player'
}
}
}
}
}

// Run post to endpoint
request.patch({
url: queryUrl,
body: JSON.stringify(newClanObject),
headers: {
Authorization: 'Bearer ' + req.user.data.attributes.token,
'Content-Type': 'application/vnd.api+json'
}
}, function (err, res, body) {
if (err || res.statusCode !== 204) {
const errorMessages = []
let msg = 'Error during the ownership transfer'
try {
msg += ': ' + JSON.stringify(JSON.parse(res.body).errors[0].detail)
} catch {}

errorMessages.push({ msg })
flash.class = 'alert-danger'
flash.messages = errorMessages
flash.type = 'Error!'

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return overallRes.redirect('manage?flash=' + data)
} else {
// Refreshing user
request.get({
url: process.env.API_URL + '/me',
headers: {
Authorization: 'Bearer ' + req.user.data.attributes.token
}
},

function (err, res, body) {
if (err) {
console.error('There was an error updating a session after a clan transfer:', err)

return
}
try {
const user = JSON.parse(body)
user.data.id = user.data.attributes.userId
user.data.attributes.token = req.user.data.attributes.token
req.logIn(user, function (err) {
if (err) console.error(err)
return overallRes.redirect('see?id=' + clanId)
})
} catch {
console.error('There was an error updating a session after a clan transfer')
}
})
}
})
}
}
145 changes: 145 additions & 0 deletions src/backend/routes/views/clans/post/update.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
let flash = {}
const request = require('request')
const { check, validationResult } = require('express-validator')

function promiseRequest (url) {
return new Promise(function (resolve, reject) {
request(url, function (error, res, body) {
if (!error && res.statusCode < 300) {
resolve(body)
} else {
reject(error)
}
})
})
}

exports = module.exports = async function (req, res) {
const locals = res.locals

locals.formData = req.body || {}

const overallRes = res

// validate the input
check('clan_tag', 'Please indicate the clan tag - No special characters and 3 characters maximum').notEmpty().isLength({ max: 3 })
check('clan_description', 'Please add a description for your clan').notEmpty().isLength({ max: 1000 })
check('clan_name', "Please indicate your clan's name").notEmpty().isLength({ max: 64 })
check('clan_id', 'Internal error while processing your query: invalid clan ID').notEmpty()

// check the validation object for errors
const errors = validationResult(req)

// Must have client side errors to fix
if (!errors.isEmpty()) {
flash.class = 'alert-danger'
flash.messages = errors
flash.type = 'Error!'

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return overallRes.redirect('manage?flash=' + data)
} else {
const newName = req.body.clan_name
const newTag = req.body.clan_tag
const oldName = req.body.original_clan_name
const oldTag = req.body.original_clan_tag
const clanDescription = req.body.clan_description

// Is the name taken ?
try {
let msg = null

flash.class = 'alert-danger'
flash.type = 'Error!'

if (oldName !== newName) {
const fetchRoute = process.env.API_URL + '/data/clan?filter=name=="' + encodeURIComponent(newName) + '"'
const data = await promiseRequest(fetchRoute)
const exists = JSON.parse(data).data.length > 0

if (exists) msg = 'This name is already taken: ' + encodeURIComponent(newName)
}
if (oldTag !== newTag) {
const fetchRoute = process.env.API_URL + '/data/clan?filter=tag=="' + encodeURIComponent(newTag) + '"'
const data = await promiseRequest(fetchRoute)
const exists = JSON.parse(data).data.length > 0

if (exists) msg = 'This tag is already taken: ' + encodeURIComponent(newTag)
}

if (msg) {
flash.messages = [{ msg }]
const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')
return overallRes.redirect('manage?flash=' + data)
}
} catch (e) {
flash.class = 'alert-danger'
flash.messages = [{ msg: 'Error while updating the clan ' + e }]
flash.type = 'Error!'

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return overallRes.redirect('manage?flash=' + data)
}

// Building update query
const queryUrl =
process.env.API_URL +
'/data/clan/' + req.body.clan_id

const newClanObject = {
data: {
type: 'clan',
id: req.body.clan_id,
attributes: {
description: clanDescription,
name: newName,
tag: newTag
}
}
}

// Run post to endpoint
request.patch({
url: queryUrl,
body: JSON.stringify(newClanObject),
headers: {
Authorization: 'Bearer ' + req.user.data.attributes.token,
'Content-Type': 'application/vnd.api+json',
Accept: 'application/vnd.api+json'
}
}, function (err, res) {
const errorMessages = []

if (err || res.statusCode !== 204) {
let msg = 'Error while updating the clan'
try {
msg += ': ' + JSON.stringify(JSON.parse(res.body).errors[0].detail)
} catch {}
errorMessages.push({ msg })
flash.class = 'alert-danger'
flash.messages = errorMessages
flash.type = 'Error!'

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return overallRes.redirect('manage?flash=' + data)
}

flash = {}
flash.class = 'alert-success'
flash.messages = [{ msg: 'You have successfully updated your clan' }]
flash.type = 'Success!'

const buff = Buffer.from(JSON.stringify(flash))
const data = buff.toString('base64')

return overallRes.redirect('manage?flash=' + data)
})
}
}
46 changes: 46 additions & 0 deletions src/backend/routes/views/dataRouter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const express = require('../../ExpressApp')
const router = express.Router()
const { AcquireTimeoutError } = require('../../services/MutexService')

const getData = async (req, res, name, data) => {
try {
return res.json(data)

Check warning on line 7 in src/backend/routes/views/dataRouter.js

Codecov / codecov/patch

src/backend/routes/views/dataRouter.js#L6-L7

Added lines #L6 - L7 were not covered by tests
} catch (e) {
if (e instanceof AcquireTimeoutError) {
return res.status(503).json({ error: 'timeout reached' })

Check warning on line 10 in src/backend/routes/views/dataRouter.js

Codecov / codecov/patch

src/backend/routes/views/dataRouter.js#L10

Added line #L10 was not covered by tests
}

console.error('[error] dataRouter::get:' + name + '.json failed with "' + e.toString() + '"')

Check warning on line 13 in src/backend/routes/views/dataRouter.js

Codecov / codecov/patch

src/backend/routes/views/dataRouter.js#L13

Added line #L13 was not covered by tests

if (!res.headersSent) {
return res.status(500).json({ error: 'unexpected error' })

Check warning on line 16 in src/backend/routes/views/dataRouter.js

Codecov / codecov/patch

src/backend/routes/views/dataRouter.js#L16

Added line #L16 was not covered by tests
}
throw e

Check warning on line 18 in src/backend/routes/views/dataRouter.js

Codecov / codecov/patch

src/backend/routes/views/dataRouter.js#L18

Added line #L18 was not covered by tests
}
}
router.get('/newshub.json', async (req, res) => {
getData(req, res, 'newshub', await req.appContainer.get('WordpressService').getNewshub())

Check warning on line 22 in src/backend/routes/views/dataRouter.js

Codecov / codecov/patch

src/backend/routes/views/dataRouter.js#L22

Added line #L22 was not covered by tests
})

router.get('/tournament-news.json', async (req, res) => {
getData(req, res, 'tournament-news', await req.appContainer.get('WordpressService').getTournamentNews())

Check warning on line 26 in src/backend/routes/views/dataRouter.js

Codecov / codecov/patch

src/backend/routes/views/dataRouter.js#L26

Added line #L26 was not covered by tests
})
router.get('/faf-teams.json', async (req, res) => {
getData(req, res, 'faf-teams', await req.appContainer.get('WordpressService').getFafTeams())

Check warning on line 29 in src/backend/routes/views/dataRouter.js

Codecov / codecov/patch

src/backend/routes/views/dataRouter.js#L29

Added line #L29 was not covered by tests
})

router.get('/content-creators.json', async (req, res) => {
getData(req, res, 'content-creators', await req.appContainer.get('WordpressService').getContentCreators())

Check warning on line 33 in src/backend/routes/views/dataRouter.js

Codecov / codecov/patch

src/backend/routes/views/dataRouter.js#L33

Added line #L33 was not covered by tests
})

router.get('/recent-players.json', async (req, res) => {
const rawData = await req.appContainer.get('LeaderboardService').getLeaderboard(1)
const data = rawData.map((item) => ({

Check warning on line 38 in src/backend/routes/views/dataRouter.js

Codecov / codecov/patch

src/backend/routes/views/dataRouter.js#L37-L38

Added lines #L37 - L38 were not covered by tests
id: item.playerId,
name: item.label
}))

getData(req, res, 'content-creators', data)

Check warning on line 43 in src/backend/routes/views/dataRouter.js

Codecov / codecov/patch

src/backend/routes/views/dataRouter.js#L43

Added line #L43 was not covered by tests
})

module.exports = router
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const express = require('../../ExpressApp')
const router = express.Router();
const router = express.Router()

router.get('/', (req, res) => res.render('index'))
router.get('/newshub', (req, res) => res.render('newshub'))
@@ -20,6 +20,6 @@ router.get('/account_activated', (req, res) => res.redirect('/account/register')
router.get('/password_resetted', (req, res) => res.redirect('/account/requestPasswordReset'))

// this is prob. outdated, but don't know
router.get('/report_submitted', require('./account/get/report'))
router.get('/report_submitted', require('./account/get/report'))

module.exports = router
48 changes: 48 additions & 0 deletions src/backend/routes/views/leaderboardRouter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const express = require('../../ExpressApp')
const router = express.Router()
const { AcquireTimeoutError } = require('../../services/MutexService')

const getLeaderboardId = (leaderboardName) => {
const mapping = {
global: 1,
'1v1': 2,
'2v2': 3,
'4v4': 4
}

if (leaderboardName in mapping) {
return mapping[leaderboardName]
}

return null
}

router.get('/', (req, res) => {
return res.render('leaderboards')
})

router.get('/:leaderboard.json', async (req, res) => {
try {
const leaderboardId = getLeaderboardId(req.params.leaderboard ?? null)

if (leaderboardId === null) {
return res.status(404).json({ error: 'Leaderboard "' + req.params.leaderboard + '" does not exist' })
}

return res.json(await req.appContainer.get('LeaderboardService').getLeaderboard(leaderboardId))
} catch (e) {
if (e instanceof AcquireTimeoutError) {
return res.status(503).json({ error: 'timeout reached' })

Check warning on line 35 in src/backend/routes/views/leaderboardRouter.js

Codecov / codecov/patch

src/backend/routes/views/leaderboardRouter.js#L35

Added line #L35 was not covered by tests
}

console.error('[error] leaderboardRouter::get:leaderboard.json failed with "' + e.toString() + '"', e.stack)

Check warning on line 38 in src/backend/routes/views/leaderboardRouter.js

Codecov / codecov/patch

src/backend/routes/views/leaderboardRouter.js#L38

Added line #L38 was not covered by tests

if (!res.headersSent) {
return res.status(500).json({ error: 'unexpected error' })

Check warning on line 41 in src/backend/routes/views/leaderboardRouter.js

Codecov / codecov/patch

src/backend/routes/views/leaderboardRouter.js#L41

Added line #L41 was not covered by tests
}

throw e

Check warning on line 44 in src/backend/routes/views/leaderboardRouter.js

Codecov / codecov/patch

src/backend/routes/views/leaderboardRouter.js#L44

Added line #L44 was not covered by tests
}
})

module.exports = router
49 changes: 49 additions & 0 deletions src/backend/routes/views/news.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const express = require('../../ExpressApp')
const router = express.Router()

function getNewsArticleBySlug (articles, slug) {
const [newsArticle] = articles.filter((entry) => {
return entry.slug === slug
}) ?? []

return newsArticle ?? null
}

function getNewsArticleByDeprecatedSlug (articles, slug) {
const [newsArticle] = articles.filter((entry) => {
return entry.bcSlug === slug
}) ?? []

return newsArticle ?? null
}

router.get('/', async (req, res) => {
res.render('news', { news: await req.appContainer.get('WordpressService').getNews() })
})

router.get('/:slug', async (req, res) => {
const newsArticles = await req.appContainer.get('WordpressService').getNews()

const newsArticle = getNewsArticleBySlug(newsArticles, req.params.slug)

if (newsArticle === null) {
const newsArticleByOldSlug = getNewsArticleByDeprecatedSlug(newsArticles, req.params.slug)

if (newsArticleByOldSlug) {
// old slug style, here for backward compatibility
res.redirect(301, newsArticleByOldSlug.slug)

return
}

res.redirect(req.baseUrl)

Check warning on line 39 in src/backend/routes/views/news.js

Codecov / codecov/patch

src/backend/routes/views/news.js#L39

Added line #L39 was not covered by tests

return

Check warning on line 41 in src/backend/routes/views/news.js

Codecov / codecov/patch

src/backend/routes/views/news.js#L41

Added line #L41 was not covered by tests
}

res.render('newsArticle', {
newsArticle
})
})

module.exports = router
23 changes: 23 additions & 0 deletions src/backend/routes/views/staticMarkdownRouter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const express = require('../../ExpressApp')
const showdown = require('showdown')
const fs = require('fs')
const router = express.Router()

function markdown (template) {
return (req, res) => {
res.render('markdown', {
content: new showdown.Converter().makeHtml(fs.readFileSync(template, 'utf-8'))
})
}
}

router.get('/privacy', markdown('src/backend/templates/views/markdown/privacy.md'))
router.get('/privacy-fr', markdown('src/backend/templates/views/markdown/privacy-fr.md'))
router.get('/privacy-ru', markdown('src/backend/templates/views/markdown/privacy-ru.md'))
router.get('/tos', markdown('src/backend/templates/views/markdown/tos.md'))
router.get('/tos-fr', markdown('src/backend/templates/views/markdown/tos-fr.md'))
router.get('/tos-ru', markdown('src/backend/templates/views/markdown/tos-ru.md'))
router.get('/rules', markdown('src/backend/templates/views/markdown/rules.md'))
router.get('/cg', markdown('src/backend/templates/views/markdown/cg.md'))

module.exports = router
45 changes: 45 additions & 0 deletions src/backend/security/bootPassport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const passport = require('passport')
const OidcStrategy = require('passport-openidconnect')
const refresh = require('passport-oauth2-refresh')
const { JavaApiClientFactory } = require('../services/JavaApiClientFactory')
const { UserRepository } = require('../services/UserRepository')
const { UserService } = require('../services/UserService')

module.exports.bootPassport = (expressApp, appConfig) => {
expressApp.use(passport.initialize())
expressApp.use(passport.session())

passport.serializeUser((user, done) => done(null, user))
passport.deserializeUser((user, done) => done(null, user))

const authStrategy = new OidcStrategy({
passReqToCallback: true,
issuer: appConfig.oauth.url + '/',
tokenURL: appConfig.oauth.url + '/oauth2/token',
authorizationURL: appConfig.oauth.publicUrl + '/oauth2/auth',
userInfoURL: appConfig.oauth.url + '/userinfo?schema=openid',
clientID: appConfig.oauth.clientId,
clientSecret: appConfig.oauth.clientSecret,
callbackURL: `${appConfig.host}/${appConfig.oauth.callback}`,
scope: ['openid', 'offline', 'public_profile', 'write_account_data']
}, async function (req, iss, sub, profile, jwtClaims, token, refreshToken, params, verified) {
const oAuthPassport = {

Check warning on line 26 in src/backend/security/bootPassport.js

Codecov / codecov/patch

src/backend/security/bootPassport.js#L25-L26

Added lines #L25 - L26 were not covered by tests
token,
refreshToken
}

const apiClient = JavaApiClientFactory.createInstance(new UserService(), appConfig.apiUrl, oAuthPassport)
const userRepository = new UserRepository(apiClient)

Check warning on line 32 in src/backend/security/bootPassport.js

Codecov / codecov/patch

src/backend/security/bootPassport.js#L31-L32

Added lines #L31 - L32 were not covered by tests

userRepository.fetchUser(oAuthPassport).then(user => {
verified(null, user)
}).catch(e => {
console.error('[Error] oAuth verify failed with "' + e.toString() + '"')
verified(null, null)

Check warning on line 38 in src/backend/security/bootPassport.js

Codecov / codecov/patch

src/backend/security/bootPassport.js#L34-L38

Added lines #L34 - L38 were not covered by tests
})
}
)

passport.use(appConfig.oauth.strategy, authStrategy)
refresh.use(appConfig.oauth.strategy, authStrategy)
}
File renamed without changes.
4 changes: 2 additions & 2 deletions lib/CacheService.js → src/backend/services/CacheService.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const NodeCache = require("node-cache");
const NodeCache = require('node-cache')
const cacheService = new NodeCache(
{
stdTTL: 300, // use 5 min for all caches if not changed with ttl
checkperiod: 600 // cleanup memory every 10 min
}
)

module.exports = cacheService
module.exports.CacheService = cacheService
72 changes: 72 additions & 0 deletions src/backend/services/JavaApiClientFactory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
const { Axios } = require('axios')
const refresh = require('passport-oauth2-refresh')
const { AuthFailed } = require('./ApiErrors')

const getRefreshToken = (strategy, oAuthPassport) => {
return new Promise((resolve, reject) => {
refresh.requestNewAccessToken(strategy, oAuthPassport.refreshToken, function (err, accessToken, refreshToken) {
if (err || !accessToken || !refreshToken) {
return reject(new AuthFailed('Failed to refresh token' + err))
}

return resolve([accessToken, refreshToken])
})
})
}

class JavaApiClientFactory {
static createInstance (userService, javaApiBaseURL, oAuthPassport, strategy) {
if (typeof oAuthPassport !== 'object') {
throw new Error('oAuthPassport not an object')
}

if (typeof oAuthPassport.refreshToken !== 'string') {
throw new Error('oAuthPassport.refreshToken not a string')
}

if (typeof oAuthPassport.token !== 'string') {
throw new Error('oAuthPassport.token not a string')
}

let tokenRefreshRunning = null
const client = new Axios({
baseURL: javaApiBaseURL
})

client.interceptors.request.use(
async config => {
config.headers.Authorization = `Bearer ${oAuthPassport.token}`

return config
})

client.interceptors.response.use((res) => {
if (!res.config._refreshTokenRequest && res.config && res.status === 401) {
res.config._refreshTokenRequest = true

if (!tokenRefreshRunning) {
tokenRefreshRunning = getRefreshToken(strategy, oAuthPassport)
}

return tokenRefreshRunning.then(([token, refreshToken]) => {
oAuthPassport.token = token
oAuthPassport.refreshToken = refreshToken

userService.updatePassport(oAuthPassport)

return client.request(res.config)
})
}

if (res.status === 401) {
throw new AuthFailed('Token no longer valid and refresh did not help')
}

return res
})

return client
}
}

module.exports.JavaApiClientFactory = JavaApiClientFactory
51 changes: 51 additions & 0 deletions src/backend/services/JavaApiM2MClient.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const { Axios } = require('axios')
const { ClientCredentials } = require('simple-oauth2')
const { AuthFailed } = require('./ApiErrors')

class JavaApiM2MClient {
static createInstance (clientId, clientSecret, host, javaApiBaseURL) {
let passport = null
const axios = new Axios({
baseURL: javaApiBaseURL
})

axios.interceptors.request.use(async (config) => {
if (!passport || passport.expired()) {
passport = await JavaApiM2MClient.getToken(clientId, clientSecret, host)

Check warning on line 14 in src/backend/services/JavaApiM2MClient.js

Codecov / codecov/patch

src/backend/services/JavaApiM2MClient.js#L14

Added line #L14 was not covered by tests
}
config.headers.Authorization = `Bearer ${passport.token.access_token}`

Check warning on line 16 in src/backend/services/JavaApiM2MClient.js

Codecov / codecov/patch

src/backend/services/JavaApiM2MClient.js#L16

Added line #L16 was not covered by tests

return config

Check warning on line 18 in src/backend/services/JavaApiM2MClient.js

Codecov / codecov/patch

src/backend/services/JavaApiM2MClient.js#L18

Added line #L18 was not covered by tests
})

return axios
}

static getToken (clientId, clientSecret, host) {
const tokenClient = new ClientCredentials({

Check warning on line 25 in src/backend/services/JavaApiM2MClient.js

Codecov / codecov/patch

src/backend/services/JavaApiM2MClient.js#L24-L25

Added lines #L24 - L25 were not covered by tests
client: {
id: clientId,
secret: clientSecret

},
auth: {
tokenHost: host,
tokenPath: '/oauth2/token',
revokePath: '/oauth2/revoke'
},
options: {
authorizationMethod: 'body'
}
})

try {
return tokenClient.getToken({

Check warning on line 42 in src/backend/services/JavaApiM2MClient.js

Codecov / codecov/patch

src/backend/services/JavaApiM2MClient.js#L41-L42

Added lines #L41 - L42 were not covered by tests
scope: ''
})
} catch (error) {
throw new AuthFailed(error.toString())

Check warning on line 46 in src/backend/services/JavaApiM2MClient.js

Codecov / codecov/patch

src/backend/services/JavaApiM2MClient.js#L46

Added line #L46 was not covered by tests
}
}
}

module.exports.JavaApiM2MClient = JavaApiM2MClient
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
class LeaderboardRepository {
constructor(javaApiClient, monthsInThePast = 12) {
constructor (javaApiClient, monthsInThePast = 12) {
this.javaApiClient = javaApiClient
this.monthsInThePast = monthsInThePast
}

getUpdateTimeForApiEntries() {
const date = new Date();
date.setMonth(date.getMonth() - this.monthsInThePast);
getUpdateTimeForApiEntries () {
const date = new Date()
date.setMonth(date.getMonth() - this.monthsInThePast)

return date.toISOString()
}

async fetchLeaderboard(id) {
async fetchLeaderboard (id) {
const updateTime = this.getUpdateTimeForApiEntries()

let response = await this.javaApiClient.get(`/data/leaderboardRating?include=player&sort=-rating&filter=leaderboard.id==${id};updateTime=ge=${updateTime}&page[size]=9999`);
const response = await this.javaApiClient.get(`/data/leaderboardRating?include=player&sort=-rating&filter=leaderboard.id==${id};updateTime=ge=${updateTime}&page[size]=9999`)

if (response.status !== 200) {
throw new Error('LeaderboardRepository::fetchLeaderboard failed with response status "' + response.status + '"')
@@ -23,12 +23,12 @@ class LeaderboardRepository {
return this.mapResponse(JSON.parse(response.data))
}

mapResponse(data) {
mapResponse (data) {
if (typeof data !== 'object' || data === null) {
throw new Error('LeaderboardRepository::mapResponse malformed response, not an object')
}

if (!data.hasOwnProperty('data')) {
if (!Object.prototype.hasOwnProperty.call(data, 'data')) {
throw new Error('LeaderboardRepository::mapResponse malformed response, expected "data"')
}

@@ -38,29 +38,29 @@ class LeaderboardRepository {
return []
}

if (!data.hasOwnProperty('included')) {
if (!Object.prototype.hasOwnProperty.call(data, 'included')) {
throw new Error('LeaderboardRepository::mapResponse malformed response, expected "included"')
}

let leaderboardData = []
const leaderboardData = []

data.data.forEach((item, index) => {
try {
leaderboardData.push({
playerId: item.id,
rating: item.attributes.rating,
totalgames: item.attributes.totalGames,
wonGames: item.attributes.wonGames,
date: item.attributes.updateTime,
label: data.included[index]?.attributes.login || 'unknown user',
label: data.included[index]?.attributes.login || 'unknown user'
})
} catch (e) {
console.error('LeaderboardRepository::mapResponse failed on item with "' + e.toString() + '"')
}

})

return leaderboardData
}
}

module.exports = LeaderboardRepository
module.exports.LeaderboardRepository = LeaderboardRepository
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
const {MutexService} = require("./MutexService");
const leaderboardTTL = 60 * 60 // 1 hours ttl as a relaxation for https://github.com/FAForever/website/issues/482
const { MutexService } = require('./MutexService')
const leaderboardTTL = 60 * 60 // 1 hours ttl as a relaxation for https://github.com/FAForever/website/issues/482

class LeaderboardService {
constructor(cacheService, leaderboardRepository, lockTimeout = 3000) {
constructor (cacheService, leaderboardRepository, lockTimeout = 3000) {
this.lockTimeout = lockTimeout
this.cacheService = cacheService
this.mutexService = new MutexService()
this.leaderboardRepository = leaderboardRepository
}

async getLeaderboard(id) {

async getLeaderboard (id, ignoreCache = false) {
if (typeof (id) !== 'number') {
throw new Error('LeaderboardService:getLeaderboard id must be a number')
}

const cacheKey = 'leaderboard-' + id

if (this.cacheService.has(cacheKey)) {
if (this.cacheService.has(cacheKey) && ignoreCache === false) {
return this.cacheService.get(cacheKey)
}

@@ -29,11 +28,11 @@ class LeaderboardService {

await this.mutexService.acquire(async () => {
const result = await this.leaderboardRepository.fetchLeaderboard(id)
this.cacheService.set(cacheKey, result, leaderboardTTL);
this.cacheService.set(cacheKey, result, leaderboardTTL)
})

return this.getLeaderboard(id)
}
}

module.exports = LeaderboardService
module.exports.LeaderboardService = LeaderboardService
36 changes: 18 additions & 18 deletions lib/MutexService.js → src/backend/services/MutexService.js
Original file line number Diff line number Diff line change
@@ -2,13 +2,13 @@ class AcquireTimeoutError extends Error {
}

class MutexService {
constructor() {
this.queue = [];
this.locked = false;
constructor () {
this.queue = []
this.locked = false
}

async acquire(callback, timeLimitMS = 500) {
let timeoutHandle;
async acquire (callback, timeLimitMS = 500) {
let timeoutHandle
const lockHandler = {}

const timeoutPromise = new Promise((resolve, reject) => {
@@ -18,23 +18,23 @@ class MutexService {
timeoutHandle = setTimeout(
() => reject(new AcquireTimeoutError('MutexService timeout reached')),
timeLimitMS
);
});
)
})

const asyncPromise = new Promise((resolve, reject) => {
if (this.locked) {
lockHandler.resolve = resolve
lockHandler.reject = reject

this.queue.push(lockHandler);
this.queue.push(lockHandler)
} else {
this.locked = true;
resolve();
this.locked = true
resolve()
}
});
})

await Promise.race([asyncPromise, timeoutPromise]).then(async () => {
clearTimeout(timeoutHandle);
clearTimeout(timeoutHandle)
try {
if (callback[Symbol.toStringTag] === 'AsyncFunction') {
await callback()
@@ -46,22 +46,22 @@ class MutexService {
this.release()
}
}).catch(e => {
let index = this.queue.indexOf(lockHandler);
const index = this.queue.indexOf(lockHandler)

if (index !== -1) {
this.queue.splice(index, 1);
this.queue.splice(index, 1)
}

throw e
})
}

release() {
release () {
if (this.queue.length > 0) {
const queueItem = this.queue.shift();
queueItem.resolve();
const queueItem = this.queue.shift()
queueItem.resolve()
} else {
this.locked = false;
this.locked = false
}
}
}
10 changes: 5 additions & 5 deletions lib/Scheduler.js → src/backend/services/Scheduler.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
const {EventEmitter} = require("events")
const { EventEmitter } = require('events')

module.exports = class Scheduler extends EventEmitter {
constructor(eventName, action, ms) {
constructor (eventName, action, ms) {

Check warning on line 4 in src/backend/services/Scheduler.js

Codecov / codecov/patch

src/backend/services/Scheduler.js#L4

Added line #L4 was not covered by tests
super()
this.eventName = eventName
this.action = action
this.handle = undefined
this.interval = ms
this.addListener(this.eventName, this.action);
this.addListener(this.eventName, this.action)

Check warning on line 10 in src/backend/services/Scheduler.js

Codecov / codecov/patch

src/backend/services/Scheduler.js#L10

Added line #L10 was not covered by tests
}

start() {
start () {

Check warning on line 13 in src/backend/services/Scheduler.js

Codecov / codecov/patch

src/backend/services/Scheduler.js#L13

Added line #L13 was not covered by tests
if (!this.handle) {
this.handle = setInterval(() => this.emit(this.eventName), this.interval);
this.handle = setInterval(() => this.emit(this.eventName), this.interval)

Check warning on line 15 in src/backend/services/Scheduler.js

Codecov / codecov/patch

src/backend/services/Scheduler.js#L15

Added line #L15 was not covered by tests
}
}
}
Original file line number Diff line number Diff line change
@@ -29,4 +29,4 @@ class UserRepository {
}
}

module.exports = UserRepository
module.exports.UserRepository = UserRepository
26 changes: 26 additions & 0 deletions src/backend/services/UserService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
class UserService {
constructor () {
this.user = null
this.session = null
}

setUserFromRequest (request) {
this.user = request.user
this.session = request.session.passport
}

isAuthenticated () {
return !!this.user

Check warning on line 13 in src/backend/services/UserService.js

Codecov / codecov/patch

src/backend/services/UserService.js#L12-L13

Added lines #L12 - L13 were not covered by tests
}

getUser () {
return this.user
}

updatePassport (oAuthPassport) {
this.user.oAuthPassport = oAuthPassport
this.session.user.oAuthPassport = oAuthPassport
}
}

module.exports.UserService = UserService
Original file line number Diff line number Diff line change
@@ -1,86 +1,87 @@
const {convert} = require("url-slug");
const { convert } = require('url-slug')

class WordpressRepository {
constructor(wordpressClient) {
constructor (wordpressClient) {
this.wordpressClient = wordpressClient
}
async fetchNews() {
let response = await this.wordpressClient.get('/wp-json/wp/v2/posts/?per_page=100&_embed&_fields=_links.author,_links.wp:featuredmedia,_embedded,title,content.rendered,date,categories&categories=587')

async fetchNews () {
const response = await this.wordpressClient.get('/wp-json/wp/v2/posts/?per_page=100&_embed&_fields=_links.author,_links.wp:featuredmedia,_embedded,title,content.rendered,date,categories&categories=587')

Check warning on line 9 in src/backend/services/WordpressRepository.js

Codecov / codecov/patch

src/backend/services/WordpressRepository.js#L8-L9

Added lines #L8 - L9 were not covered by tests

if (response.status !== 200) {
throw new Error('WordpressRepository::fetchNews failed with response status "' + response.status + '"')
}

const rawNewsData = JSON.parse(response.data)

if (typeof rawNewsData !== 'object' || rawNewsData === null) {
throw new Error('WordpressRepository::mapNewsResponse malformed response, not an object')
}

return rawNewsData.map(item => ({
return rawNewsData.map(item => ({

Check warning on line 21 in src/backend/services/WordpressRepository.js

Codecov / codecov/patch

src/backend/services/WordpressRepository.js#L21

Added line #L21 was not covered by tests
slug: convert(item.title.rendered),
bcSlug: item.title.rendered.replace(/ /g, '-'),
date: item.date,
title: item.title.rendered,
content: item.content.rendered,
author: item._embedded.author[0].name,
media: item._embedded['wp:featuredmedia'][0].source_url,
media: item._embedded['wp:featuredmedia'][0].source_url
}))
}

async fetchTournamentNews() {
let response = await this.wordpressClient.get('/wp-json/wp/v2/posts/?per_page=10&_embed&_fields=content.rendered,categories&categories=638')
async fetchTournamentNews () {
const response = await this.wordpressClient.get('/wp-json/wp/v2/posts/?per_page=10&_embed&_fields=content.rendered,categories&categories=638')

Check warning on line 33 in src/backend/services/WordpressRepository.js

Codecov / codecov/patch

src/backend/services/WordpressRepository.js#L32-L33

Added lines #L32 - L33 were not covered by tests

if (response.status !== 200) {
throw new Error('WordpressRepository::fetchTournamentNews failed with response status "' + response.status + '"')
}

let dataObjectToArray = JSON.parse(response.data);

let sortedData = dataObjectToArray.map(item => ({
const dataObjectToArray = JSON.parse(response.data)

Check warning on line 39 in src/backend/services/WordpressRepository.js

Codecov / codecov/patch

src/backend/services/WordpressRepository.js#L39

Added line #L39 was not covered by tests

const sortedData = dataObjectToArray.map(item => ({

Check warning on line 41 in src/backend/services/WordpressRepository.js

Codecov / codecov/patch

src/backend/services/WordpressRepository.js#L41

Added line #L41 was not covered by tests
content: item.content.rendered,
category: item.categories
}));
}))

return sortedData.filter(article => article.category[1] !== 284)
}

async fetchContentCreators() {
async fetchContentCreators () {

Check warning on line 49 in src/backend/services/WordpressRepository.js

Codecov / codecov/patch

src/backend/services/WordpressRepository.js#L49

Added line #L49 was not covered by tests
const response = await this.wordpressClient.get('/wp-json/wp/v2/posts/?per_page=100&_embed&_fields=content.rendered,categories&categories=639')

if (response.status !== 200) {
throw new Error('WordpressRepository::fetchContentCreators failed with response status "' + response.status + '"')
}

const items = JSON.parse(response.data);
const items = JSON.parse(response.data)

Check warning on line 56 in src/backend/services/WordpressRepository.js

Codecov / codecov/patch

src/backend/services/WordpressRepository.js#L56

Added line #L56 was not covered by tests

return items.map(item => ({
content: item.content.rendered,
content: item.content.rendered
}))
}

async fetchFafTeams() {
async fetchFafTeams () {

Check warning on line 63 in src/backend/services/WordpressRepository.js

Codecov / codecov/patch

src/backend/services/WordpressRepository.js#L63

Added line #L63 was not covered by tests
const response = await this.wordpressClient.get('/wp-json/wp/v2/posts/?per_page=100&_embed&_fields=content.rendered,categories&categories=636')

if (response.status !== 200) {
throw new Error('WordpressRepository::fetchFafTeams failed with response status "' + response.status + '"')
}
const items = JSON.parse(response.data);

const items = JSON.parse(response.data)

Check warning on line 70 in src/backend/services/WordpressRepository.js

Codecov / codecov/patch

src/backend/services/WordpressRepository.js#L70

Added line #L70 was not covered by tests

return items.map(item => ({
content: item.content.rendered,
content: item.content.rendered
}))
}

async fetchNewshub() {
async fetchNewshub () {

Check warning on line 77 in src/backend/services/WordpressRepository.js

Codecov / codecov/patch

src/backend/services/WordpressRepository.js#L77

Added line #L77 was not covered by tests
const response = await this.wordpressClient.get('/wp-json/wp/v2/posts/?per_page=10&_embed&_fields=_links.author,_links.wp:featuredmedia,_embedded,title,newshub_externalLinkUrl,newshub_sortIndex,content.rendered,date,categories&categories=283')

if (response.status !== 200) {
throw new Error('WordpressRepository::fetchNewshub failed with response status "' + response.status + '"')
}
const items = JSON.parse(response.data);

const items = JSON.parse(response.data)

Check warning on line 84 in src/backend/services/WordpressRepository.js

Codecov / codecov/patch

src/backend/services/WordpressRepository.js#L84

Added line #L84 was not covered by tests
const sortedData = items.map(item => ({
category: item.categories,
sortIndex: item.newshub_sortIndex,
@@ -89,15 +90,15 @@
title: item.title.rendered,
content: item.content.rendered,
author: item._embedded.author[0].name,
media: item._embedded['wp:featuredmedia'][0].source_url,
}));
sortedData.sort((articleA, articleB) => articleB.sortIndex - articleA.sortIndex);
media: item._embedded['wp:featuredmedia'][0].source_url
}))

sortedData.sort((articleA, articleB) => articleB.sortIndex - articleA.sortIndex)

Check warning on line 96 in src/backend/services/WordpressRepository.js

Codecov / codecov/patch

src/backend/services/WordpressRepository.js#L96

Added line #L96 was not covered by tests

return sortedData.filter((article) => {
return article.category[1] !== 284;
return article.category[1] !== 284

Check warning on line 99 in src/backend/services/WordpressRepository.js

Codecov / codecov/patch

src/backend/services/WordpressRepository.js#L99

Added line #L99 was not covered by tests
})
}
}

module.exports = WordpressRepository
module.exports.WordpressRepository = WordpressRepository
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
const {MutexService} = require("./MutexService");
const { MutexService } = require('./MutexService')
const wordpressTTL = 60 * 60

class WordpressService {
constructor(cacheService, wordpressRepository, lockTimeout = 3000) {
constructor (cacheService, wordpressRepository, lockTimeout = 3000) {
this.lockTimeout = lockTimeout
this.cacheService = cacheService
this.mutexServices = {
news: new MutexService(),
tournament: new MutexService(),
creators: new MutexService(),
teams: new MutexService(),
newshub: new MutexService(),
news: new MutexService(),
tournament: new MutexService(),
creators: new MutexService(),
teams: new MutexService(),
newshub: new MutexService()
}
this.wordpressRepository = wordpressRepository
}
getCacheKey(name) {

getCacheKey (name) {

Check warning on line 18 in src/backend/services/WordpressService.js

Codecov / codecov/patch

src/backend/services/WordpressService.js#L18

Added line #L18 was not covered by tests
return 'WordpressService_' + name
}

async getNews(ignoreCache = false) {
async getNews (ignoreCache = false) {

Check warning on line 22 in src/backend/services/WordpressService.js

Codecov / codecov/patch

src/backend/services/WordpressService.js#L22

Added line #L22 was not covered by tests
const cacheKey = this.getCacheKey('news')

if (this.cacheService.has(cacheKey) && ignoreCache === false) {
@@ -31,16 +31,16 @@
}, this.lockTimeout)
return this.getNews()
}

await this.mutexServices.news.acquire(async () => {
const result = await this.wordpressRepository.fetchNews()
this.cacheService.set(cacheKey, result, wordpressTTL);
this.cacheService.set(cacheKey, result, wordpressTTL)

Check warning on line 37 in src/backend/services/WordpressService.js

Codecov / codecov/patch

src/backend/services/WordpressService.js#L37

Added line #L37 was not covered by tests
})

return this.getNews()
}

async getTournamentNews(ignoreCache = false) {
async getTournamentNews (ignoreCache = false) {

Check warning on line 43 in src/backend/services/WordpressService.js

Codecov / codecov/patch

src/backend/services/WordpressService.js#L43

Added line #L43 was not covered by tests
const cacheKey = this.getCacheKey('tournament-news')

if (this.cacheService.has(cacheKey) && ignoreCache === false) {
@@ -55,13 +55,13 @@

await this.mutexServices.tournament.acquire(async () => {
const result = await this.wordpressRepository.fetchTournamentNews()
this.cacheService.set(cacheKey, result, wordpressTTL);
this.cacheService.set(cacheKey, result, wordpressTTL)

Check warning on line 58 in src/backend/services/WordpressService.js

Codecov / codecov/patch

src/backend/services/WordpressService.js#L58

Added line #L58 was not covered by tests
})

return this.getTournamentNews()
}

async getContentCreators(ignoreCache = false) {
async getContentCreators (ignoreCache = false) {

Check warning on line 64 in src/backend/services/WordpressService.js

Codecov / codecov/patch

src/backend/services/WordpressService.js#L64

Added line #L64 was not covered by tests
const cacheKey = this.getCacheKey('content-creators')

if (this.cacheService.has(cacheKey) && ignoreCache === false) {
@@ -76,13 +76,13 @@

await this.mutexServices.creators.acquire(async () => {
const result = await this.wordpressRepository.fetchContentCreators()
this.cacheService.set(cacheKey, result, wordpressTTL);
this.cacheService.set(cacheKey, result, wordpressTTL)

Check warning on line 79 in src/backend/services/WordpressService.js

Codecov / codecov/patch

src/backend/services/WordpressService.js#L79

Added line #L79 was not covered by tests
})

return this.getContentCreators()
}

async getFafTeams(ignoreCache = false) {
async getFafTeams (ignoreCache = false) {

Check warning on line 85 in src/backend/services/WordpressService.js

Codecov / codecov/patch

src/backend/services/WordpressService.js#L85

Added line #L85 was not covered by tests
const cacheKey = this.getCacheKey('faf-teams')

if (this.cacheService.has(cacheKey) && ignoreCache === false) {
@@ -97,13 +97,13 @@

await this.mutexServices.teams.acquire(async () => {
const result = await this.wordpressRepository.fetchFafTeams()
this.cacheService.set(cacheKey, result, wordpressTTL);
this.cacheService.set(cacheKey, result, wordpressTTL)

Check warning on line 100 in src/backend/services/WordpressService.js

Codecov / codecov/patch

src/backend/services/WordpressService.js#L100

Added line #L100 was not covered by tests
})

return this.getFafTeams()
}

async getNewshub(ignoreCache = false) {
async getNewshub (ignoreCache = false) {

Check warning on line 106 in src/backend/services/WordpressService.js

Codecov / codecov/patch

src/backend/services/WordpressService.js#L106

Added line #L106 was not covered by tests
const cacheKey = this.getCacheKey('newshub')

if (this.cacheService.has(cacheKey) && ignoreCache === false) {
@@ -118,11 +118,11 @@

await this.mutexServices.newshub.acquire(async () => {
const result = await this.wordpressRepository.fetchNewshub()
this.cacheService.set(cacheKey, result, wordpressTTL);
this.cacheService.set(cacheKey, result, wordpressTTL)

Check warning on line 121 in src/backend/services/WordpressService.js

Codecov / codecov/patch

src/backend/services/WordpressService.js#L121

Added line #L121 was not covered by tests
})

return this.getNewshub()
}
}

module.exports = WordpressService
module.exports.WordpressService = WordpressService
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
const WordpressService = require("./WordpressService")
const WordpressRepository = require("./WordpressRepository")
const {Axios} = require("axios")
const WordpressService = require('./WordpressService')
const WordpressRepository = require('./WordpressRepository')
const { Axios } = require('axios')
const cacheService = require('./CacheService')

module.exports = (wordpressBaseURL) => {
const config = {
baseURL: wordpressBaseURL
};
}
const wordpressClient = new Axios(config)

return new WordpressService(cacheService, new WordpressRepository(wordpressClient))
File renamed without changes.
7 changes: 7 additions & 0 deletions src/backend/templates/mixins/flash-error.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
mixin flash-error(validationErrors)
if validationErrors
div.alert(class=validationErrors.class)
ul.validationErrors-errors
each error in validationErrors.messages.errors
if error.msg
li <strong>#{validationErrors.type}</strong> !{error.msg}
Original file line number Diff line number Diff line change
@@ -4,5 +4,5 @@ mixin flash-messages(messages)
ul.flash-errors
if flash.messages
if flash.messages[0]
if flash.messages[0].msg
if flash.messages[0].msg
li <strong>#{flash.type}</strong> !{flash.messages[0].msg}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
extends ../../layouts/default
include ../../mixins/flash-messages
include ../../mixins/flash-error
include ../../mixins/form/account
block css
link(href="/styles/awesomplete.css?version=" + Date.now(), rel="stylesheet")
block bannerMixin
block content
.containerCenter.text-center
@@ -11,25 +13,25 @@ block content
p Here you can report players who have broken the community rules in some way. We encourage users to report misconducting players to keep Forged Alliance Forever a healthy community. All reports will be processed by our moderation team.
p Examples of reportable behaviour:
ul
li
li
a(href='/rules') Breaking any of the rules
li Teamkilling
li Griefing (e.g. reclaiming friendly units or structures)
li Insulting and bad behaviour
li Exploits
p Bugs in the game should be
a(href='https://forum.faforever.com/category/8/game-issues-and-gameplay-questions') submitted to our tech support forum
| or preferably as an

p Bugs in the game should be
a(href='https://forum.faforever.com/category/8/game-issues-and-gameplay-questions') submitted to our tech support forum
| or preferably as an
a(href='https://github.com/FAForever/fa') issue on our github page.
hr
br
br
.row
.col-md-offset-3.col-md-6
+flash-messages(flash)
form(method='post',action="/account/report",data-toggle="validator").accountForm
+flash-error(flash)

form(method='post', action="/account/report", data-toggle="validator").accountForm
.column6
div.form-group
p Reporter:
@@ -38,12 +40,12 @@ block content

div.form-group
p Offender username:
input(type='text', required='required', class='offender_name', id='offender_0', name='offender_0', placeholder='FAF username(s)').form-control
input(type='text', required='required', class='offender_name', id='offender', name='offender', placeholder='FAF username(s)').form-control

span(aria-hidden='true').glyphicon.form-control-feedback
.help-block Make sure to spell the name correctly and to respect the casing.



.column6
div.form-group
@@ -52,7 +54,7 @@ block content
span(aria-hidden='true').glyphicon.form-control-feedback
.help-block
p Please enter the replay ID of the game where the incident happened


div.form-group
p Timestamp:
@@ -66,16 +68,16 @@ block content
textarea(rows='8', name='report_description', required='required', placeholder='Please provide a short but thorough description of the incident you are reporting. If there are no records available of the incident (e.g. not something that happened in #aeolus or in-game), please provide us a screenshot of it. You can use any image hosting site, e.g. http://imgur.com/.').form-control
span(aria-hidden='true').glyphicon.form-control-feedback
br
.column12
.column12
.form-actions
button(type='submit') Submit Report

h3.column12
br
br
p Current reports


.centerFormPlease
table
thead
@@ -99,14 +101,15 @@ block content
td #{report.lastModerator}
td #{report.notice}
td(style=report.statusStyle) #{report.status}





block js
script(type='text/javascript').
window.reportable_members = '!{JSON.stringify(reportable_members)}';
window.offenders_names = '!{JSON.stringify(offenders_names)}';

if (window.history.replaceState) {
window.history.replaceState(null, null, window.location.href);
}

script(src=webpackAssetJS('report'))
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
5 changes: 3 additions & 2 deletions src/frontend/js/entrypoint/leaderboards.js
Original file line number Diff line number Diff line change
@@ -17,9 +17,10 @@ async function leaderboardOneJSON (leaderboardFile) {
// Check which category is active
const response = await fetch(`leaderboards/${leaderboardFile}.json`)

if (response.status === 400) {
window.location.href = '/leaderboards'
if (response.status !== 200) {
throw new Error('issues getting leaderboard')
}

currentLeaderboard = leaderboardFile
const data = await response.json()
return await data
58 changes: 18 additions & 40 deletions src/frontend/js/entrypoint/report.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,26 @@
import $ from 'jquery'
import Awesomplete from 'awesomplete'
import axios from 'axios'

const memberList = JSON.parse(window.reportable_members)
const searchBar = $('#offender_0')
addAwesompleteListener(searchBar)
async function getPlayers () {
const response = await axios.get('/data/recent-players.json')
if (response.status !== 200) {
throw new Error('issues getting data')
}

return response.data
}

$('#add_offender').click(function () {
addOffender()
getPlayers().then((memberList) => {
addAwesompleteListener(document.getElementById('offender'), memberList)
})

function addAwesompleteListener (element) {
// Show label but insert value into the input:
/* eslint-disable no-new */
new Awesomplete(element[0], {
list: memberList
})
element[0].addEventListener('awesomplete-select', function (e) {})
element[0].addEventListener('awesomplete-selectcomplete', function (e) {
const text = e.text
element.val(text)
function addAwesompleteListener (element, memberList) {
const list = memberList.map((player) => {
return player.name
})
}

function addOffender () {
const numberOfOffenders = $('.offender_name').length
for (let i = 0; i <= numberOfOffenders; i++) {
if (!$('#offender_' + i).length) {
const element = $('#offender_' + (i - 1)).clone(false)
element.insertAfter($('#offender_' + (i - 1)))
element.attr('id', 'offender_' + i)
element.attr('name', 'offender_' + i)
element.val('')
addAwesompleteListener(element)
return element
}
}
}

const offenders = JSON.parse(window.offenders_names)
for (const k in offenders) {
const offender = offenders[k]
if (k === 0) {
searchBar.val(offender)
continue
}
addOffender().val(offender)
/* eslint-disable no-new */
new Awesomplete(element, {
list
})
}
31 changes: 19 additions & 12 deletions tests/JavaApiClient.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
const { JavaApiClientFactory } = require('../lib/JavaApiClientFactory')
const appConfig = require('../config/app')
const { JavaApiClientFactory } = require('../src/backend/services/JavaApiClientFactory')
const appConfig = require('../src/backend/config/app')
const refresh = require('passport-oauth2-refresh')
const OidcStrategy = require('passport-openidconnect')
const nock = require('nock')
const { AuthFailed } = require('../lib/ApiErrors')
const { AuthFailed } = require('../src/backend/services/ApiErrors')
const { UserService } = require('../src/backend/services/UserService')

beforeEach(() => {
refresh.use(appConfig.oauth.strategy, new OidcStrategy({
@@ -21,22 +22,24 @@ afterEach(() => {
jest.restoreAllMocks()
})
test('empty passport', () => {
expect(() => JavaApiClientFactory('http://api-localhost')).toThrowError('oAuthPassport not an object')
expect(() => JavaApiClientFactory.createInstance(new UserService(), 'http://api-localhost')).toThrowError('oAuthPassport not an object')
})

test('empty token', () => {
expect(() => JavaApiClientFactory('http://api-localhost', { refreshToken: '123' })).toThrowError('oAuthPassport.token not a string')
expect(() => JavaApiClientFactory.createInstance(new UserService(), 'http://api-localhost', { refreshToken: '123' })).toThrowError('oAuthPassport.token not a string')
})

test('empty refresh-token', () => {
expect(() => JavaApiClientFactory('http://api-localhost', { token: '123' })).toThrowError('oAuthPassport.refreshToken not a string')
expect(() => JavaApiClientFactory.createInstance(new UserService(), 'http://api-localhost', { token: '123' })).toThrowError('oAuthPassport.refreshToken not a string')
})

test('multiple calls with stale token will trigger refresh only once', async () => {
const client = JavaApiClientFactory('http://api-localhost', {
const userService = new UserService()
userService.setUserFromRequest({ user: {}, session: { passport: { user: {} } } })
const client = JavaApiClientFactory.createInstance(userService, 'http://api-localhost', {
token: '123',
refreshToken: '456'
})
}, appConfig.oauth.strategy)

const refreshSpy = jest.spyOn(refresh, 'requestNewAccessToken')
const apiScope = nock('http://api-localhost')
@@ -69,10 +72,12 @@ test('multiple calls with stale token will trigger refresh only once', async ()
})

test('refresh will throw on error', async () => {
const client = JavaApiClientFactory('http://api-localhost', {
const userService = new UserService()
userService.setUserFromRequest({ user: {}, session: { passport: { user: {} } } })
const client = JavaApiClientFactory.createInstance(userService, 'http://api-localhost', {
token: '123',
refreshToken: '456'
})
}, appConfig.oauth.strategy)

const refreshSpy = jest.spyOn(refresh, 'requestNewAccessToken')
const apiScope = nock('http://api-localhost')
@@ -100,10 +105,12 @@ test('refresh will throw on error', async () => {
})

test('refresh will not loop to death', async () => {
const client = JavaApiClientFactory('http://api-localhost', {
const userService = new UserService()
userService.setUserFromRequest({ user: {}, session: { passport: { user: {} } } })
const client = JavaApiClientFactory.createInstance(userService, 'http://api-localhost', {
token: '123',
refreshToken: '456'
})
}, appConfig.oauth.strategy)

const apiScope = nock('http://api-localhost')
.get('/example')
4 changes: 2 additions & 2 deletions tests/LeaderboardService.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const LeaderboardService = require('../lib/LeaderboardService')
const LeaderboardRepository = require('../lib/LeaderboardRepository')
const { LeaderboardService } = require('../src/backend/services/LeaderboardService')
const { LeaderboardRepository } = require('../src/backend/services/LeaderboardRepository')
const NodeCache = require('node-cache')
const { Axios } = require('axios')

2 changes: 1 addition & 1 deletion tests/MutexService.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { AcquireTimeoutError, MutexService } = require('../lib/MutexService')
const { AcquireTimeoutError, MutexService } = require('../src/backend/services/MutexService')
test('release will unlock the queue', async () => {
const mutexService = new MutexService()
expect(mutexService.locked).toBe(false)
16 changes: 7 additions & 9 deletions tests/integration/IsAuthenticatedMiddleware.test.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
const Express = require('../../ExpressApp')
const middlewares = require('../../routes/middleware')
const middlewares = require('../../src/backend/routes/middleware')
const supertestSession = require('supertest-session')
const fafApp = require('../../fafApp')
const { AppKernel } = require('../../src/backend/AppKernel')

let testApp = null
let testSession = null

beforeEach(() => {
const app = new Express()
fafApp.setup(app)
testSession = supertestSession(app)
testApp = app
beforeEach(async () => {
const kernel = new AppKernel()
await kernel.boot()
testSession = supertestSession(kernel.expressApp)
testApp = kernel.expressApp
})

describe('Authenticate Middleware', function () {
15 changes: 6 additions & 9 deletions tests/integration/NewsRouter.test.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
const Express = require('../../ExpressApp')
const supertestSession = require('supertest-session')
const fafApp = require('../../fafApp')
const { AppKernel } = require('../../src/backend/AppKernel')

let testSession = null
beforeEach(() => {
const app = new Express()
fafApp.setup(app)
fafApp.loadRouters(app)

testSession = supertestSession(app)
beforeEach(async () => {
const kernel = new AppKernel()
await kernel.boot()
kernel.loadControllers()
testSession = supertestSession(kernel.expressApp)
})

describe('News Routes', function () {
test('responds to /', async () => {
const res = await testSession.get('/news')
13 changes: 6 additions & 7 deletions tests/integration/accountRouter.test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
const Express = require('../../ExpressApp')
const supertestSession = require('supertest-session')
const fafApp = require('../../fafApp')
const { AppKernel } = require('../../src/backend/AppKernel')

let testSession = null
beforeEach(() => {
const app = new Express()
fafApp.setup(app)
fafApp.loadRouters(app)
testSession = supertestSession(app)
beforeEach(async () => {
const kernel = new AppKernel()
await kernel.boot()
kernel.loadControllers()
testSession = supertestSession(kernel.expressApp)
})

describe('Account Routes', function () {
2 changes: 1 addition & 1 deletion tests/integration/asyncErrorHandler.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const Express = require('../../ExpressApp')
const Express = require('../../src/backend/ExpressApp')
const supertestSession = require('supertest-session')

let testApp = null
11 changes: 5 additions & 6 deletions tests/integration/clanRouter.test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
const Express = require('../../ExpressApp')
const supertestSession = require('supertest-session')
const fafApp = require('../../fafApp')
const { AppKernel } = require('../../src/backend/AppKernel')

let testSession = null
beforeEach(async () => {
const app = new Express()
fafApp.setup(app)
fafApp.loadRouters(app)
testSession = supertestSession(app)
const kernel = new AppKernel()
await kernel.boot()
kernel.loadControllers()
testSession = supertestSession(kernel.expressApp)
})

describe('Clan Routes', function () {
14 changes: 6 additions & 8 deletions tests/integration/defaultRouter.test.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
const Express = require('../../ExpressApp')
const supertestSession = require('supertest-session')
const fafApp = require('../../fafApp')
const { AppKernel } = require('../../src/backend/AppKernel')

let testSession = null
beforeEach(() => {
const app = new Express()
fafApp.setup(app)
fafApp.loadRouters(app)
testSession = supertestSession(app)
beforeEach(async () => {
const kernel = new AppKernel()
await kernel.boot()
kernel.loadControllers()
testSession = supertestSession(kernel.expressApp)
})

describe('Default Routes', function () {
const arr = [
'',
23 changes: 11 additions & 12 deletions tests/integration/leaderboardRouter.test.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
const Express = require('../../ExpressApp')
const supertestSession = require('supertest-session')
const fafApp = require('../../fafApp')
const passportMock = require('../helpers/PassportMock')
const { AppKernel } = require('../../src/backend/AppKernel')

let testSession = null
beforeEach(() => {
const app = new Express()
fafApp.setup(app)
passportMock(app, { passAuthentication: true })
fafApp.loadRouters(app)
testSession = supertestSession(app)
beforeEach(async () => {
const kernel = new AppKernel()
await kernel.boot()
passportMock(kernel.expressApp, { passAuthentication: true })
kernel.loadControllers()
testSession = supertestSession(kernel.expressApp)
})

describe('Leaderboard Routes', function () {
test('authentication required for main page', async () => {
test('no authentication required for main page', async () => {
let response = await testSession.get('/leaderboards')
expect(response.status).toBe(302)
expect(response.status).toBe(200)

await testSession.get('/mock-login')

response = await testSession.get('/leaderboards')
expect(response.status).toBe(200)
})

test('authentication required for datasets', async () => {
test('no authentication required for datasets', async () => {
const response = await testSession.get('/leaderboards/1v1.json')
expect(response.status).toBe(401)
expect(response.status).toBe(200)
})

test('fails with 404 on unknown leaderboard', async () => {
14 changes: 6 additions & 8 deletions tests/integration/markdownRouter.test.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
const Express = require('../../ExpressApp')
const supertestSession = require('supertest-session')
const fafApp = require('../../fafApp')
const { AppKernel } = require('../../src/backend/AppKernel')

let testSession = null
beforeEach(() => {
const app = new Express()
fafApp.setup(app)
fafApp.loadRouters(app)
testSession = supertestSession(app)
beforeEach(async () => {
const kernel = new AppKernel()
await kernel.boot()
kernel.loadControllers()
testSession = supertestSession(kernel.expressApp)
})

describe('Privacy And TOS Routes', function () {
const arr = [
'/privacy',
60 changes: 0 additions & 60 deletions tests/integration/servicesMiddleware.test.js

This file was deleted.

19 changes: 11 additions & 8 deletions tests/setup.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
const fs = require('fs')
const wordpressService = require('../lib/WordpressService')
const leaderboardService = require('../lib/LeaderboardService')
const { WordpressService } = require('../src/backend/services/WordpressService')
const { LeaderboardService } = require('../src/backend/services/LeaderboardService')
const nock = require('nock')
nock.disableNetConnect()
nock.enableNetConnect('127.0.0.1')
beforeEach(() => {
const newsFile = JSON.parse(fs.readFileSync('tests/integration/testData/news.json', { encoding: 'utf8', flag: 'r' }))
jest.spyOn(wordpressService.prototype, 'getNews').mockResolvedValue(newsFile)
jest.spyOn(WordpressService.prototype, 'getNews').mockResolvedValue(newsFile)

const tnFile = JSON.parse(fs.readFileSync('tests/integration/testData/tournament-news.json', { encoding: 'utf8', flag: 'r' }))
jest.spyOn(wordpressService.prototype, 'getTournamentNews').mockResolvedValue(tnFile)
jest.spyOn(WordpressService.prototype, 'getTournamentNews').mockResolvedValue(tnFile)

const ccFile = JSON.parse(fs.readFileSync('tests/integration/testData/content-creators.json', { encoding: 'utf8', flag: 'r' }))
jest.spyOn(wordpressService.prototype, 'getContentCreators').mockResolvedValue(ccFile)
jest.spyOn(WordpressService.prototype, 'getContentCreators').mockResolvedValue(ccFile)

const ftFile = JSON.parse(fs.readFileSync('tests/integration/testData/faf-teams.json', { encoding: 'utf8', flag: 'r' }))
jest.spyOn(wordpressService.prototype, 'getFafTeams').mockResolvedValue(ftFile)
jest.spyOn(WordpressService.prototype, 'getFafTeams').mockResolvedValue(ftFile)

const nhFile = JSON.parse(fs.readFileSync('tests/integration/testData/newshub.json', { encoding: 'utf8', flag: 'r' }))
jest.spyOn(wordpressService.prototype, 'getNewshub').mockResolvedValue(nhFile)
jest.spyOn(WordpressService.prototype, 'getNewshub').mockResolvedValue(nhFile)

jest.spyOn(leaderboardService.prototype, 'getLeaderboard').mockImplementation((id) => {
jest.spyOn(LeaderboardService.prototype, 'getLeaderboard').mockImplementation((id) => {
const mapping = {
1: 'global',
2: '1v1',
40 changes: 20 additions & 20 deletions webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
const path = require("path");
const { WebpackManifestPlugin } = require('webpack-manifest-plugin')
const path = require('path')

module.exports = {
mode: "production",
mode: 'production',
entry: {
"clans": ["./src/frontend/js/entrypoint/clans.js"],
"content-creators": ["./src/frontend/js/entrypoint/content-creators.js"],
"donation": ["./src/frontend/js/entrypoint/donation.js"],
"faf-teams": ["./src/frontend/js/entrypoint/faf-teams.js"],
"getClans": ["./src/frontend/js/entrypoint/getClans.js"],
"leaderboards": ["./src/frontend/js/entrypoint/leaderboards.js"],
"navigation": ["./src/frontend/js/entrypoint/navigation.js"],
"newshub": ["./src/frontend/js/entrypoint/newshub.js"],
"play": ["./src/frontend/js/entrypoint/play.js"],
"report": ["./src/frontend/js/entrypoint/report.js"],
clans: ['./src/frontend/js/entrypoint/clans.js'],
'content-creators': ['./src/frontend/js/entrypoint/content-creators.js'],
donation: ['./src/frontend/js/entrypoint/donation.js'],
'faf-teams': ['./src/frontend/js/entrypoint/faf-teams.js'],
getClans: ['./src/frontend/js/entrypoint/getClans.js'],
leaderboards: ['./src/frontend/js/entrypoint/leaderboards.js'],
navigation: ['./src/frontend/js/entrypoint/navigation.js'],
newshub: ['./src/frontend/js/entrypoint/newshub.js'],
play: ['./src/frontend/js/entrypoint/play.js'],
report: ['./src/frontend/js/entrypoint/report.js']
},
output: {
filename: "[name].[contenthash].js",
path: path.resolve(__dirname, "dist/js"),
publicPath: "/dist/js",
clean: true,
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist/js'),
publicPath: '/dist/js',
clean: true
},
plugins: [
new WebpackManifestPlugin({ useEntryKeys: true }),
],
};
new WebpackManifestPlugin({ useEntryKeys: true })
]
}
260 changes: 248 additions & 12 deletions yarn.lock

Large diffs are not rendered by default.