Skip to content

Commit

Permalink
EventEmitter, init WordpressService, cleanup members/*.json
Browse files Browse the repository at this point in the history
  • Loading branch information
fcaps committed Nov 22, 2023
1 parent 7324782 commit eb73c7b
Show file tree
Hide file tree
Showing 29 changed files with 385 additions and 268 deletions.
1 change: 1 addition & 0 deletions config/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const appConfig = {
callback: process.env.CALLBACK || 'callback',
},
apiUrl: process.env.API_URL || 'https://api.faforever.com',
wordpressUrl: process.env.WP_URL || 'https://direct.faforever.com',
extractorInterval: process.env.EXTRACTOR_INTERVAL || 5,
playerCountInterval: process.env.PLAYER_COUNT_INTERVAL || 15
}
Expand Down
3 changes: 3 additions & 0 deletions express.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ const newsRouter = require('./routes/views/news');
const staticMarkdownRouter = require('./routes/views/staticMarkdownRouter');
const leaderboardRouter = require('./routes/views/leaderboardRouter');
const authRouter = require('./routes/views/auth');
const dataRouter = require('./routes/views/dataRouter');

app.locals.clanInvitations = {};

//Execute Middleware
app.use(middleware.injectServices);
app.use(middleware.initLocals);
app.use(middleware.clientChecks);

Expand Down Expand Up @@ -74,6 +76,7 @@ app.use('/', authRouter)
app.use('/', staticMarkdownRouter)
app.use('/news', newsRouter)
app.use('/leaderboards', leaderboardRouter)
app.use('/data', dataRouter)

// --- UNPROTECTED ROUTES ---
const appGetRouteArray = [
Expand Down
1 change: 0 additions & 1 deletion grunt/nodemon.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ module.exports = {
'node_modules/**',
'grunt/**',
'Gruntfile.js',
'public/js/app/members/**'
]
}
}
Expand Down
5 changes: 5 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const config = {
setupFilesAfterEnv: ['./tests/setup.js'],
};

module.exports = config;
9 changes: 9 additions & 0 deletions lib/CacheService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
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
5 changes: 3 additions & 2 deletions lib/LeaderboardService.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
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, mutexService, leaderboardRepository, lockTimeout = 3000) {
constructor(cacheService, leaderboardRepository, lockTimeout = 3000) {
this.lockTimeout = lockTimeout
this.cacheService = cacheService
this.mutexService = mutexService
this.mutexService = new MutexService()
this.leaderboardRepository = leaderboardRepository
}

Expand Down
13 changes: 2 additions & 11 deletions lib/LeaderboardServiceFactory.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
const LeaderboardService = require("./LeaderboardService");
const LeaderboardRepository = require("./LeaderboardRepository");
const {MutexService} = require("./MutexService");
const NodeCache = require("node-cache");
const {Axios} = require("axios");

const leaderboardMutex = new MutexService()
const cacheService = new NodeCache(
{
stdTTL: 300, // use 5 min for all caches if not changed with ttl
checkperiod: 600 // cleanup memory every 10 min
}
);
const cacheService = require('./CacheService')

module.exports = (javaApiBaseURL, token) => {
const config = {
Expand All @@ -19,5 +10,5 @@ module.exports = (javaApiBaseURL, token) => {
};
const javaApiClient = new Axios(config)

return new LeaderboardService(cacheService, leaderboardMutex, new LeaderboardRepository(javaApiClient))
return new LeaderboardService(cacheService, new LeaderboardRepository(javaApiClient))
}
18 changes: 18 additions & 0 deletions lib/Scheduler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const {EventEmitter} = require("events")

module.exports = class Scheduler extends EventEmitter {
constructor(eventName, action, ms) {
super()
this.eventName = eventName
this.action = action
this.handle = undefined
this.interval = ms
this.addListener(this.eventName, this.action);
}

start() {
if (!this.handle) {
this.handle = setInterval(() => this.emit(this.eventName), this.interval);
}
}
}
103 changes: 103 additions & 0 deletions lib/WordpressRepository.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
const {convert} = require("url-slug");

class WordpressRepository {
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')

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 => ({
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,
}))
}

async fetchTournamentNews() {
let response = await this.wordpressClient.get('/wp-json/wp/v2/posts/?per_page=10&_embed&_fields=content.rendered,categories&categories=638')

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 => ({
content: item.content.rendered,
category: item.categories
}));

return sortedData.filter(article => article.category[1] !== 284)
}

async fetchContentCreators() {
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);

return items.map(item => ({
content: item.content.rendered,
}))
}

async fetchFafTeams() {
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);

return items.map(item => ({
content: item.content.rendered,
}))
}

async fetchNewshub() {
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 sortedData = items.map(item => ({
category: item.categories,
sortIndex: item.newshub_sortIndex,
link: item.newshub_externalLinkUrl,
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,
}));

sortedData.sort((articleA, articleB) => articleB.sortIndex - articleA.sortIndex);

return sortedData.filter((article) => {
return article.category[1] !== 284;
})
}
}

module.exports = WordpressRepository
128 changes: 128 additions & 0 deletions lib/WordpressService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
const {MutexService} = require("./MutexService");
const wordpressTTL = 60 * 60

class WordpressService {
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(),
}
this.wordpressRepository = wordpressRepository
}

getCacheKey(name) {
return 'WordpressService_' + name
}

async getNews(ignoreCache = false) {
const cacheKey = this.getCacheKey('news')

if (this.cacheService.has(cacheKey) && ignoreCache === false) {
return this.cacheService.get(cacheKey)
}

if (this.mutexServices.news.locked) {
await this.mutexServices.news.acquire(() => {
}, this.lockTimeout)
return this.getNews()
}

await this.mutexServices.news.acquire(async () => {
const result = await this.wordpressRepository.fetchNews()
this.cacheService.set(cacheKey, result, wordpressTTL);
})

return this.getNews()
}

async getTournamentNews(ignoreCache = false) {
const cacheKey = this.getCacheKey('tournament-news')

if (this.cacheService.has(cacheKey) && ignoreCache === false) {
return this.cacheService.get(cacheKey)
}

if (this.mutexServices.tournament.locked) {
await this.mutexService.acquire(() => {
}, this.lockTimeout)
return this.getTournamentNews()
}

await this.mutexServices.tournament.acquire(async () => {
const result = await this.wordpressRepository.fetchTournamentNews()
this.cacheService.set(cacheKey, result, wordpressTTL);
})

return this.getTournamentNews()
}

async getContentCreators(ignoreCache = false) {
const cacheKey = this.getCacheKey('content-creators')

if (this.cacheService.has(cacheKey) && ignoreCache === false) {
return this.cacheService.get(cacheKey)
}

if (this.mutexServices.creators.locked) {
await this.mutexServices.creators.acquire(() => {
}, this.lockTimeout)
return this.getContentCreators()
}

await this.mutexServices.creators.acquire(async () => {
const result = await this.wordpressRepository.fetchContentCreators()
this.cacheService.set(cacheKey, result, wordpressTTL);
})

return this.getContentCreators()
}

async getFafTeams(ignoreCache = false) {
const cacheKey = this.getCacheKey('faf-teams')

if (this.cacheService.has(cacheKey) && ignoreCache === false) {
return this.cacheService.get(cacheKey)
}

if (this.mutexServices.teams.locked) {
await this.mutexService.acquire(() => {
}, this.lockTimeout)
return this.getFafTeams()
}

await this.mutexServices.teams.acquire(async () => {
const result = await this.wordpressRepository.fetchFafTeams()
this.cacheService.set(cacheKey, result, wordpressTTL);
})

return this.getFafTeams()
}

async getNewshub(ignoreCache = false) {
const cacheKey = this.getCacheKey('newshub')

if (this.cacheService.has(cacheKey) && ignoreCache === false) {
return this.cacheService.get(cacheKey)
}

if (this.mutexServices.newshub.locked) {
await this.mutexService.acquire(() => {
}, this.lockTimeout)
return this.getNewshub()
}

await this.mutexServices.newshub.acquire(async () => {
const result = await this.wordpressRepository.fetchNewshub()
this.cacheService.set(cacheKey, result, wordpressTTL);
})

return this.getNewshub()
}
}

module.exports = WordpressService
13 changes: 13 additions & 0 deletions lib/WordpressServiceFactory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
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))
}
2 changes: 1 addition & 1 deletion public/js/app/content-creators.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
async function getWordpress() {
const response = await fetch(`./js/app/members/content-creators.json`);
const response = await fetch('/data/content-creators.json');
const data = await response.json();
let insertWordpress = document.getElementById('contentCreatorWordpress');
insertWordpress.insertAdjacentHTML('beforeend', `${data[0].content}`);
Expand Down
2 changes: 1 addition & 1 deletion public/js/app/faf-teams.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
let teamSelection = document.querySelectorAll('.teamSelection');
let teamContainer = document.querySelectorAll('.teamContainer');
async function getWordpress() {
const response = await fetch(`js/app/members/faf-teams.json`);
const response = await fetch('/data/faf-teams.json');
const data = await response.json();
let insertWordpress = document.getElementById('insertWordpress');
insertWordpress.insertAdjacentHTML('beforeend', `${data[0].content}`);
Expand Down
1 change: 0 additions & 1 deletion public/js/app/members/.gitignore

This file was deleted.

6 changes: 2 additions & 4 deletions public/js/app/newshub.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@

async function getNewshub() {
const response = await fetch(`js/app/members/newshub.json`);
const response = await fetch('/data/newshub.json');
const data = await response.json();
return await data;
}
async function getTournament() {
const response = await fetch(`js/app/members/tournament-news.json`);
const response = await fetch('/data/tournament-news.json');
const data = await response.json();
return await data;
}
Expand Down Expand Up @@ -72,8 +72,6 @@ let arrowRight = document.getElementById('clientArrowRigth');
let arrowLeft = document.getElementById('clientArrowLeft');
let newsPosition = 0;
let newsLimit = 0;
let newsMove = clientContainer[0].offsetWidth;
console.log(newsMove);
let spawnStyle = getComputedStyle(clientSpawn).columnGap;
let columnGap = spawnStyle.slice(0, 2);

Expand Down
Loading

0 comments on commit eb73c7b

Please sign in to comment.