mirror of
https://github.com/vkaelin/LeagueStats.git
synced 2026-03-25 12:57:28 +00:00
chore: delete old server folder and rename new one
This commit is contained in:
parent
324d4d539f
commit
75894b3025
119 changed files with 3386 additions and 12189 deletions
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.json]
|
||||
insert_final_newline = ignore
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
PORT=3333
|
||||
HOST=0.0.0.0
|
||||
NODE_ENV=development
|
||||
APP_KEY=
|
||||
|
||||
MONGODB_URL=mongodb://localhost:27017
|
||||
MONGODB_DATABASE=leaguestats
|
||||
|
||||
REDIS_CONNECTION=local
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=
|
||||
|
||||
API_KEY=RGAPI-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
7
server-new/.gitignore
vendored
7
server-new/.gitignore
vendored
|
|
@ -1,7 +0,0 @@
|
|||
node_modules
|
||||
build
|
||||
coverage
|
||||
.vscode
|
||||
.DS_STORE
|
||||
.env
|
||||
tmp
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Ace Commands
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This file is the entry point for running ace commands. For typescript
|
||||
| projects, the ace commands will fallback to the compiled code and
|
||||
| hence this file has to be executable by node directly.
|
||||
|
|
||||
*/
|
||||
|
||||
require('reflect-metadata')
|
||||
require('source-map-support').install({ handleUncaughtExceptions: false })
|
||||
|
||||
const { Ignitor } = require('@adonisjs/core/build/src/Ignitor')
|
||||
new Ignitor(__dirname)
|
||||
.ace()
|
||||
.handle(process.argv.slice(2))
|
||||
.catch(console.error)
|
||||
5381
server-new/package-lock.json
generated
5381
server-new/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,35 +0,0 @@
|
|||
{
|
||||
"name": "leaguestats-api",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "node ace build",
|
||||
"dev": "npm run start",
|
||||
"start": "node ace serve --watch",
|
||||
"lint": "eslint . --ext=.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@adonisjs/assembler": "^2.1.5",
|
||||
"adonis-preset-ts": "^1.0.4",
|
||||
"eslint": "^7.10.0",
|
||||
"eslint-plugin-adonis": "^1.0.15",
|
||||
"pino-pretty": "^4.2.1",
|
||||
"typescript": "^4.0.3",
|
||||
"youch": "^2.1.0",
|
||||
"youch-terminal": "^1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@adonisjs/ace": "^6.9.4",
|
||||
"@adonisjs/core": "^5.0.0-preview-rc-1.12",
|
||||
"@adonisjs/fold": "^6.4.1",
|
||||
"@adonisjs/redis": "^4.1.2",
|
||||
"@fightmegg/riot-rate-limiter": "0.0.11",
|
||||
"@zakodium/adonis-mongodb": "^0.3.0",
|
||||
"got": "^11.7.0",
|
||||
"proxy-addr": "^2.0.6",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"request": "^2.88.2",
|
||||
"riot-ratelimiter": "^0.1.5",
|
||||
"source-map-support": "^0.5.19"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,14 @@
|
|||
# editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.json]
|
||||
insert_final_newline = ignore
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
|
|
|||
|
|
@ -1,26 +1,14 @@
|
|||
HOST=127.0.0.1
|
||||
PORT=5000
|
||||
PORT=3333
|
||||
HOST=0.0.0.0
|
||||
NODE_ENV=development
|
||||
|
||||
APP_NAME=LeagueStats
|
||||
APP_URL=http://${HOST}:${PORT}
|
||||
|
||||
CACHE_VIEWS=false
|
||||
|
||||
APP_KEY=
|
||||
|
||||
DB_CONNECTION=mongodb
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=27017
|
||||
DB_USER=root
|
||||
DB_PASSWORD=
|
||||
DB_DATABASE=leaguestats
|
||||
MONGODB_URL=mongodb://localhost:27017
|
||||
MONGODB_DATABASE=leaguestats
|
||||
|
||||
REDIS_CONNECTION=local
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=
|
||||
|
||||
HASH_DRIVER=bcrypt
|
||||
|
||||
API_KEY=RGAPI-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
|
|
|
|||
14
server/.gitignore
vendored
14
server/.gitignore
vendored
|
|
@ -1,11 +1,7 @@
|
|||
# Node modules
|
||||
node_modules
|
||||
|
||||
# Adonis directory for storing tmp files
|
||||
tmp
|
||||
|
||||
# Environment variables, never commit this file
|
||||
build
|
||||
coverage
|
||||
.vscode
|
||||
.DS_STORE
|
||||
.env
|
||||
|
||||
# The development sqlite file
|
||||
database/development.sqlite
|
||||
tmp
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
# LeagueStats Backend
|
||||
|
||||
This is the boilerplate for creating an API server in AdonisJs, it comes pre-configured with.
|
||||
|
||||
1. Bodyparser
|
||||
2. Authentication
|
||||
3. CORS
|
||||
4. Lucid ORM
|
||||
5. Migrations and seeds
|
||||
|
||||
## Setup
|
||||
|
||||
Use the adonis command to install the blueprint
|
||||
|
||||
```bash
|
||||
adonis new yardstick --api-only
|
||||
```
|
||||
|
||||
or manually clone the repo and then run `npm install`.
|
||||
|
||||
|
||||
### Migrations
|
||||
|
||||
Run the following command to run startup migrations.
|
||||
|
||||
```js
|
||||
adonis migration:run
|
||||
```
|
||||
20
server/ace
20
server/ace
|
|
@ -1,21 +1,19 @@
|
|||
'use strict'
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Ace Commands
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The ace file is just a regular Javascript file but with no extension. You
|
||||
| can call `node ace` followed by the command name and it just works.
|
||||
|
|
||||
| Also you can use `adonis` followed by the command name, since the adonis
|
||||
| global proxies all the ace commands.
|
||||
| This file is the entry point for running ace commands. For typescript
|
||||
| projects, the ace commands will fallback to the compiled code and
|
||||
| hence this file has to be executable by node directly.
|
||||
|
|
||||
*/
|
||||
|
||||
const { Ignitor } = require('@adonisjs/ignitor')
|
||||
require('reflect-metadata')
|
||||
require('source-map-support').install({ handleUncaughtExceptions: false })
|
||||
|
||||
new Ignitor(require('@adonisjs/fold'))
|
||||
.appRoot(__dirname)
|
||||
.fireAce()
|
||||
const { Ignitor } = require('@adonisjs/core/build/src/Ignitor')
|
||||
new Ignitor(__dirname)
|
||||
.ace()
|
||||
.handle(process.argv.slice(2))
|
||||
.catch(console.error)
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const { Command } = require('@adonisjs/ace')
|
||||
const Database = use('Database')
|
||||
const Match = use('App/Models/Match')
|
||||
|
||||
class DeleteMatch extends Command {
|
||||
static get signature() {
|
||||
return `
|
||||
delete:match
|
||||
{ field : Field to check }
|
||||
{ value?=null : Value of the field, if true: delete match }
|
||||
`
|
||||
}
|
||||
|
||||
static get description() {
|
||||
return 'Delete matches from db with a condition'
|
||||
}
|
||||
|
||||
async handle(args, options) {
|
||||
console.time('DeleteMatches')
|
||||
const nbMatchesBefore = await Match.count()
|
||||
const matches = await Match.all()
|
||||
for (const match of matches.toJSON()) {
|
||||
await Match.where('_id', match._id).where(args.field, args.value).delete()
|
||||
}
|
||||
const nbMatchesAfter = await Match.count()
|
||||
Database.close()
|
||||
console.timeEnd('DeleteMatches')
|
||||
this.success(`${this.icon('success')} Delete Matches completed: ${nbMatchesBefore - nbMatchesAfter} matche(s) deleted`)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DeleteMatch
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const { Command } = require('@adonisjs/ace')
|
||||
const DetailedMatchTransformer = use('App/Transformers/DetailedMatchTransformer')
|
||||
const Database = use('Database')
|
||||
const Jax = use('App/Services/Jax')
|
||||
const DetailedMatch = use('App/Models/DetailedMatch')
|
||||
const Queue = use('Bee/Queue')
|
||||
|
||||
class EditDetailedMatch extends Command {
|
||||
static get signature() {
|
||||
return `
|
||||
edit:detailed:match
|
||||
{ concurrent?=10 : Number of concurrent jobs }
|
||||
`
|
||||
}
|
||||
|
||||
static get description() {
|
||||
return 'Edit DetailedMatches in the db with the new Transformer version'
|
||||
}
|
||||
|
||||
/**
|
||||
* Create edit-detailed-matches Queue
|
||||
*/
|
||||
createQueue(concurrent) {
|
||||
Queue.get('edit-detailed-matches').process(concurrent, async (job) => {
|
||||
// Get stats from Riot API
|
||||
const matchRiot = await Jax.Match.get(job.data.gameId, job.data.region)
|
||||
// Transform raw matches data
|
||||
const transformedMatch = await DetailedMatchTransformer.transform(matchRiot)
|
||||
|
||||
// Update match in DB
|
||||
await DetailedMatch.where('_id', job.data._id).update(transformedMatch)
|
||||
|
||||
return
|
||||
})
|
||||
|
||||
/**
|
||||
* Job (edit detailed match) finished with success
|
||||
*/
|
||||
Queue.get('edit-detailed-matches').on('succeeded', (job, result) => {
|
||||
console.log(`Job ${job.id} succeeded`)
|
||||
})
|
||||
}
|
||||
|
||||
async handle(args, options) {
|
||||
console.time('EditDetailedMatches')
|
||||
|
||||
this.createQueue(args.concurrent)
|
||||
|
||||
// All detailed matches from the db
|
||||
const matches = await DetailedMatch.all()
|
||||
const matchesArray = matches.toJSON()
|
||||
|
||||
// Create jobs
|
||||
const jobs = []
|
||||
for (const match of matchesArray) {
|
||||
const matchInfos = {
|
||||
_id: match._id,
|
||||
gameId: match.gameId,
|
||||
region: match.region,
|
||||
}
|
||||
const job = await Queue
|
||||
.get('edit-detailed-matches')
|
||||
.createJob(matchInfos)
|
||||
.save()
|
||||
jobs.push(job)
|
||||
}
|
||||
|
||||
// Wait that all jobs are done
|
||||
const finalResult = await new Promise((resolve, reject) => {
|
||||
const lastJob = jobs[jobs.length - 1]
|
||||
lastJob.on('succeeded', result => {
|
||||
resolve(`FINAL RESULT for job ${lastJob.id}`)
|
||||
})
|
||||
})
|
||||
|
||||
Database.close()
|
||||
console.timeEnd('EditDetailedMatches')
|
||||
this.success(`${this.icon('success')} Edit ${matchesArray.length} DetailedMatches completed`)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EditDetailedMatch
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const { Command } = require('@adonisjs/ace')
|
||||
const BasicMatchTransformer = use('App/Transformers/BasicMatchTransformer')
|
||||
const Database = use('Database')
|
||||
const Jax = use('App/Services/Jax')
|
||||
const Match = use('App/Models/Match')
|
||||
const Queue = use('Bee/Queue')
|
||||
|
||||
class EditMatch extends Command {
|
||||
static get signature() {
|
||||
return `
|
||||
edit:match
|
||||
{ concurrent?=10 : Number of concurrent jobs },
|
||||
{ puuid? : Only update matches for a specific summoner },
|
||||
`
|
||||
}
|
||||
|
||||
static get description() {
|
||||
return 'Edit matches in the db with the new Transformer version'
|
||||
}
|
||||
|
||||
/**
|
||||
* Create edit-matches Queue
|
||||
*/
|
||||
createQueue(concurrent) {
|
||||
Queue.get('edit-matches').process(concurrent, async (job) => {
|
||||
// Get stats from Riot API
|
||||
const matchRiot = await Jax.Match.get(job.data.gameId, job.data.region)
|
||||
const account = {
|
||||
accountId: (matchRiot.participantIdentities.find(s => s.player.summonerName === job.data.name)).player.currentAccountId,
|
||||
puuid: job.data.summoner_puuid
|
||||
}
|
||||
|
||||
// Transform raw matches data
|
||||
const transformedMatch = await BasicMatchTransformer.transform(matchRiot, { account })
|
||||
|
||||
// Update match in DB
|
||||
await Match.where('_id', job.data._id).update(transformedMatch)
|
||||
|
||||
return
|
||||
})
|
||||
|
||||
/**
|
||||
* Job (edit match) finished with success
|
||||
*/
|
||||
Queue.get('edit-matches').on('succeeded', (job, result) => {
|
||||
console.log(`Job ${job.id} succeeded`)
|
||||
})
|
||||
}
|
||||
|
||||
async handle(args, options) {
|
||||
console.time('EditMatches')
|
||||
|
||||
this.info(`Concurrent: ${args.concurrent}`)
|
||||
this.info(`PUUID: ${args.puuid}`)
|
||||
this.createQueue(args.concurrent)
|
||||
|
||||
// All matches from the db
|
||||
const matches = args.puuid ? await Match.query().where({ summoner_puuid: args.puuid }).fetch() : await Match.all()
|
||||
const matchesArray = matches.toJSON()
|
||||
console.log(`${matchesArray.length} matches to edit.`)
|
||||
|
||||
// Create jobs
|
||||
const jobs = []
|
||||
for (const match of matchesArray) {
|
||||
const matchInfos = {
|
||||
_id: match._id,
|
||||
gameId: match.gameId,
|
||||
name: match.name,
|
||||
region: match.region,
|
||||
summoner_puuid: match.summoner_puuid
|
||||
}
|
||||
const job = await Queue
|
||||
.get('edit-matches')
|
||||
.createJob(matchInfos)
|
||||
.save()
|
||||
jobs.push(job)
|
||||
}
|
||||
|
||||
// Wait that all jobs are done
|
||||
const finalResult = await new Promise((resolve, reject) => {
|
||||
const lastJob = jobs[jobs.length - 1]
|
||||
lastJob.on('succeeded', result => {
|
||||
resolve(`FINAL RESULT for job ${lastJob.id}`)
|
||||
})
|
||||
})
|
||||
|
||||
Database.close()
|
||||
console.timeEnd('EditMatches')
|
||||
this.success(`${this.icon('success')} Edit ${matchesArray.length} Matches completed`)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EditMatch
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const { Command } = require('@adonisjs/ace')
|
||||
const Database = use('Database')
|
||||
const Queue = use('Bee/Queue')
|
||||
const Summoner = use('App/Models/Summoner')
|
||||
const { getSeasonNumber } = use('App/helpers')
|
||||
|
||||
class EditMatchList extends Command {
|
||||
static get signature() {
|
||||
return 'edit:match:list'
|
||||
}
|
||||
|
||||
static get description() {
|
||||
return 'Edit all matchlist of players to add season of the matches'
|
||||
}
|
||||
|
||||
/**
|
||||
* Create edit-matchList Queue
|
||||
*/
|
||||
createQueue(concurrent) {
|
||||
Queue.get('edit-matchList').process(concurrent, async (job) => {
|
||||
// Update matchlist with season
|
||||
job.data.matchList = job.data.matchList.map(m => {
|
||||
m.seasonMatch = getSeasonNumber(m.timestamp)
|
||||
return m
|
||||
})
|
||||
|
||||
// Update Summoner in DB
|
||||
await Summoner.where('puuid', job.data.puuid).update(job.data)
|
||||
|
||||
return
|
||||
})
|
||||
|
||||
/**
|
||||
* Job (edit matchList) finished with success
|
||||
*/
|
||||
Queue.get('edit-matchList').on('succeeded', (job, result) => {
|
||||
console.log(`Job ${job.id} succeeded`)
|
||||
})
|
||||
}
|
||||
|
||||
async handle(args, options) {
|
||||
console.time('EditMatchList')
|
||||
this.info('Start EditMatchList Command')
|
||||
|
||||
this.createQueue(10)
|
||||
|
||||
// All sumoners from the db
|
||||
const summoners = await Summoner.all()
|
||||
const summonersArray = summoners.toJSON()
|
||||
|
||||
// Create jobs
|
||||
const jobs = []
|
||||
for (const summoner of summonersArray) {
|
||||
const job = await Queue
|
||||
.get('edit-matchList')
|
||||
.createJob(summoner)
|
||||
.save()
|
||||
jobs.push(job)
|
||||
}
|
||||
|
||||
// Wait that all jobs are done
|
||||
await new Promise((resolve, reject) => {
|
||||
const lastJob = jobs[jobs.length - 1]
|
||||
lastJob.on('succeeded', result => {
|
||||
resolve(`FINAL RESULT for job ${lastJob.id}`)
|
||||
})
|
||||
})
|
||||
|
||||
Database.close()
|
||||
console.timeEnd('EditMatchList')
|
||||
this.success(`${this.icon('success')} Edit ${summonersArray.length} Summoners completed`)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EditMatchList
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const Jax = use('App/Services/Jax')
|
||||
const DetailedMatch = use('App/Models/DetailedMatch')
|
||||
const DetailedMatchTransformer = use('App/Transformers/DetailedMatchTransformer')
|
||||
const MatchService = use('App/Services/MatchService')
|
||||
const StatsService = use('App/Services/StatsService')
|
||||
const SummonerService = use('App/Services/SummonerService')
|
||||
const Summoner = use('App/Models/Summoner')
|
||||
|
||||
class MatchController {
|
||||
/**
|
||||
* Get the soloQ rank of all the players of the team
|
||||
* @param summoner all the data of the summoner
|
||||
* @param region of the match
|
||||
*/
|
||||
async _getPlayerRank(summoner, region) {
|
||||
const account = await SummonerService.getAccount(summoner.name, region)
|
||||
if (account) {
|
||||
const ranked = await SummonerService.getRanked(account, region)
|
||||
summoner.rank = ranked.soloQ ? (({ tier, shortName }) => ({ tier, shortName }))(ranked.soloQ) : null
|
||||
} else {
|
||||
summoner.rank = null
|
||||
}
|
||||
|
||||
return summoner
|
||||
}
|
||||
|
||||
/**
|
||||
* POST - Return data from matches searched by gameIds
|
||||
*/
|
||||
async index({ request, response }) {
|
||||
console.log('More Matches Request')
|
||||
const account = request.input('account')
|
||||
const gameIds = request.input('gameIds')
|
||||
|
||||
const summonerDB = await Summoner.where({ puuid: account.puuid }).first()
|
||||
const matches = await MatchService.getMatches(account, gameIds, summonerDB)
|
||||
|
||||
await summonerDB.save()
|
||||
|
||||
const stats = await StatsService.getSummonerStats(account)
|
||||
|
||||
return response.json({
|
||||
matches,
|
||||
stats,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* POST - Return details data for one specific match
|
||||
*/
|
||||
async show({ request, response }) {
|
||||
console.time('MatchDetails')
|
||||
console.log('Match details request')
|
||||
const gameId = request.input('gameId')
|
||||
const region = request.input('region')
|
||||
console.log(gameId, region)
|
||||
|
||||
let matchDetails = {}
|
||||
const alreadySaved = await DetailedMatch.where({ gameId, region }).first()
|
||||
if (alreadySaved) {
|
||||
console.log('MATCH DETAILS ALREADY SAVED')
|
||||
matchDetails = alreadySaved
|
||||
} else {
|
||||
matchDetails = await Jax.Match.get(gameId, region)
|
||||
matchDetails = await DetailedMatchTransformer.transform(matchDetails)
|
||||
await DetailedMatch.create(matchDetails)
|
||||
}
|
||||
|
||||
console.timeEnd('MatchDetails')
|
||||
|
||||
return response.json({
|
||||
matchDetails
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* POST - Return ranks of players for a specific game
|
||||
*/
|
||||
async showRanks({ request, response }) {
|
||||
console.time('Ranks')
|
||||
const gameId = request.input('gameId')
|
||||
const region = request.input('region')
|
||||
|
||||
let matchDetails = await DetailedMatch.where({ gameId, region }).first()
|
||||
if (!matchDetails) {
|
||||
return response.json(null)
|
||||
}
|
||||
|
||||
const requestsBlue = matchDetails.blueTeam.players.map(p => this._getPlayerRank(p, region))
|
||||
matchDetails.blueTeam.players = await Promise.all(requestsBlue)
|
||||
|
||||
const requestsRed = matchDetails.redTeam.players.map(p => this._getPlayerRank(p, region))
|
||||
matchDetails.redTeam.players = await Promise.all(requestsRed)
|
||||
|
||||
matchDetails.save()
|
||||
console.timeEnd('Ranks')
|
||||
|
||||
return response.json({
|
||||
blueTeam: matchDetails.blueTeam.players,
|
||||
redTeam: matchDetails.redTeam.players,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MatchController
|
||||
|
|
@ -1,159 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const Jax = use('App/Services/Jax')
|
||||
const LiveMatchTransformer = use('App/Transformers/LiveMatchTransformer')
|
||||
const MatchRepository = make('App/Repositories/MatchRepository')
|
||||
const MatchService = use('App/Services/MatchService')
|
||||
const SummonerService = use('App/Services/SummonerService')
|
||||
const StatsService = use('App/Services/StatsService')
|
||||
const Summoner = use('App/Models/Summoner')
|
||||
|
||||
class SummonerController {
|
||||
async _getSeasons(puuid) {
|
||||
let seasons = await MatchRepository.seasons(puuid)
|
||||
return seasons.length ? seasons.map(s => s._id) : [10]
|
||||
}
|
||||
/**
|
||||
* POST: get basic summoner data
|
||||
*/
|
||||
async basic({ request, response }) {
|
||||
console.time('all')
|
||||
const summoner = request.input('summoner')
|
||||
const region = request.input('region')
|
||||
console.log(summoner, region)
|
||||
|
||||
const regexSummonerName = new RegExp('^[0-9\\p{L} _\\.]+$', 'u')
|
||||
if (!regexSummonerName.exec(summoner)) {
|
||||
return response.json(null)
|
||||
}
|
||||
|
||||
const finalJSON = {}
|
||||
Jax.regionName = region
|
||||
|
||||
try {
|
||||
const account = await SummonerService.getAccount(summoner, region)
|
||||
// Check if the summoner is found
|
||||
if (!account) return response.json(null)
|
||||
account.region = region
|
||||
finalJSON.account = account
|
||||
|
||||
// Summoner in DB
|
||||
const summonerDB = await Summoner.findOrCreate(
|
||||
{ puuid: account.puuid },
|
||||
{ puuid: account.puuid }
|
||||
)
|
||||
|
||||
// Summoner names
|
||||
finalJSON.account.names = SummonerService.getAllSummonerNames(account, summonerDB)
|
||||
|
||||
// MATCH LIST
|
||||
await MatchService.updateMatchList(account, summonerDB)
|
||||
finalJSON.matchList = summonerDB.matchList
|
||||
|
||||
// All seasons the summoner has played
|
||||
finalJSON.seasons = await this._getSeasons(account.puuid)
|
||||
|
||||
// CURRENT GAME
|
||||
const currentGame = await Jax.Spectator.summonerID(account.id, region)
|
||||
finalJSON.playing = !!currentGame
|
||||
finalJSON.current = currentGame
|
||||
|
||||
// RANKED STATS
|
||||
finalJSON.ranked = await SummonerService.getRanked(account, region)
|
||||
|
||||
// SAVE IN DB
|
||||
await summonerDB.save()
|
||||
} catch (error) {
|
||||
console.log('username not found')
|
||||
console.log(error)
|
||||
return response.json(null)
|
||||
}
|
||||
|
||||
console.timeEnd('all')
|
||||
return response.json(finalJSON)
|
||||
}
|
||||
|
||||
/**
|
||||
* POST: get overview view summoner data
|
||||
*/
|
||||
async overview({ request, response }) {
|
||||
console.time('overview')
|
||||
const account = request.input('account')
|
||||
const season = request.input('season')
|
||||
const finalJSON = {}
|
||||
|
||||
// Summoner in DB
|
||||
const summonerDB = await Summoner.findOrCreate(
|
||||
{ puuid: account.puuid },
|
||||
{ puuid: account.puuid }
|
||||
)
|
||||
|
||||
// MATCHES BASIC
|
||||
const gameIds = summonerDB.matchList.slice(0)
|
||||
.filter(m => {
|
||||
return season ? m.seasonMatch === season : true
|
||||
})
|
||||
.slice(0, 10)
|
||||
.map(({ gameId }) => gameId)
|
||||
finalJSON.matchesDetails = await MatchService.getMatches(account, gameIds, summonerDB)
|
||||
|
||||
// STATS
|
||||
console.time('STATS')
|
||||
finalJSON.stats = await StatsService.getSummonerStats(account, season)
|
||||
console.timeEnd('STATS')
|
||||
|
||||
// SAVE IN DB
|
||||
await summonerDB.save()
|
||||
|
||||
console.timeEnd('overview')
|
||||
return response.json(finalJSON)
|
||||
}
|
||||
|
||||
/**
|
||||
* POST: get champions view summoner data
|
||||
*/
|
||||
async champions({ request, response }) {
|
||||
const puuid = request.input('puuid')
|
||||
const queue = request.input('queue')
|
||||
const season = request.input('season')
|
||||
console.time('championsRequest')
|
||||
const championStats = await MatchRepository.championCompleteStats(puuid, queue, season)
|
||||
console.timeEnd('championsRequest')
|
||||
return response.json(championStats)
|
||||
}
|
||||
|
||||
/**
|
||||
* POST: get records view summoner data
|
||||
*/
|
||||
async records({ request, response }) {
|
||||
const puuid = request.input('puuid')
|
||||
const season = request.input('season')
|
||||
console.time('recordsRequest')
|
||||
const records = await MatchRepository.records(puuid, season)
|
||||
console.timeEnd('recordsRequest')
|
||||
return response.json(records[0])
|
||||
}
|
||||
|
||||
/**
|
||||
* POST - Return live match details
|
||||
*/
|
||||
async liveMatchDetails({ request, response }) {
|
||||
console.time('liveMatchDetails')
|
||||
const account = request.input('account')
|
||||
const region = request.input('region')
|
||||
|
||||
// CURRENT GAME
|
||||
let currentGame = await Jax.Spectator.summonerID(account.id, region)
|
||||
|
||||
if (!currentGame) {
|
||||
return response.json(null)
|
||||
}
|
||||
|
||||
currentGame = await LiveMatchTransformer.transform(currentGame, { region })
|
||||
console.timeEnd('liveMatchDetails')
|
||||
|
||||
return response.json(currentGame)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SummonerController
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
class ConvertEmptyStringsToNull {
|
||||
async handle ({ request }, next) {
|
||||
if (Object.keys(request.body).length) {
|
||||
request.body = Object.assign(
|
||||
...Object.keys(request.body).map(key => ({
|
||||
[key]: request.body[key] !== '' ? request.body[key] : null
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
||||
await next()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ConvertEmptyStringsToNull
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
/** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */
|
||||
const Model = use('Model')
|
||||
|
||||
class DetailedMatch extends Model {
|
||||
}
|
||||
|
||||
module.exports = DetailedMatch
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
/** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */
|
||||
const Model = use('Model')
|
||||
|
||||
class Match extends Model {
|
||||
}
|
||||
|
||||
module.exports = Match
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
/** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */
|
||||
const Model = use('Model')
|
||||
|
||||
class Summoner extends Model {
|
||||
matches() {
|
||||
return this.hasMany('App/Models/Match', 'puuid', 'summoner_puuid')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Summoner
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
class NoTimestamp {
|
||||
register (Model) {
|
||||
Object.defineProperties(Model, {
|
||||
createdAtColumn: {
|
||||
get: () => null,
|
||||
},
|
||||
updatedAtColumn: {
|
||||
get: () => null,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = NoTimestamp
|
||||
|
|
@ -1,322 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
class MatchRepository {
|
||||
static get inject() {
|
||||
return ['App/Models/Match']
|
||||
}
|
||||
|
||||
constructor(Match) {
|
||||
this.Match = Match
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic matchParams used in a lot of requests
|
||||
* @param puuid of the summoner
|
||||
*/
|
||||
_matchParams(puuid) {
|
||||
return {
|
||||
summoner_puuid: puuid,
|
||||
result: { $not: { $eq: 'Remake' } },
|
||||
gamemode: { $nin: [800, 810, 820, 830, 840, 850] },
|
||||
season: this.season ? this.season : { $exists: true }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the aggregate mongo query
|
||||
* @param {Number} puuid
|
||||
* @param {Object} matchParams
|
||||
* @param {Array} intermediateSteps
|
||||
* @param {*} groupId
|
||||
* @param {Object} groupParams
|
||||
* @param {Array} finalSteps
|
||||
*/
|
||||
_aggregate(puuid, matchParams, intermediateSteps, groupId, groupParams, finalSteps) {
|
||||
return this.Match.query().aggregate([
|
||||
{
|
||||
$match: {
|
||||
...this._matchParams(puuid),
|
||||
...matchParams
|
||||
}
|
||||
},
|
||||
...intermediateSteps,
|
||||
{
|
||||
$group: {
|
||||
_id: groupId,
|
||||
count: { $sum: 1 },
|
||||
wins: {
|
||||
$sum: {
|
||||
$cond: [{ $eq: ['$result', 'Win'] }, 1, 0]
|
||||
}
|
||||
},
|
||||
losses: {
|
||||
$sum: {
|
||||
$cond: [{ $eq: ['$result', 'Fail'] }, 1, 0]
|
||||
}
|
||||
},
|
||||
...groupParams
|
||||
},
|
||||
},
|
||||
...finalSteps
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Summoner's statistics for the N most played champions
|
||||
* @param puuid of the summoner
|
||||
* @param limit number of champions to fetch
|
||||
*/
|
||||
championStats(puuid, limit = 5) {
|
||||
const groupParams = {
|
||||
champion: { $first: '$champion' },
|
||||
kills: { $sum: '$stats.kills' },
|
||||
deaths: { $sum: '$stats.deaths' },
|
||||
assists: { $sum: '$stats.assists' },
|
||||
}
|
||||
const finalSteps = [
|
||||
{ $sort: { 'count': -1, 'champion.name': 1 } },
|
||||
{ $limit: limit }
|
||||
]
|
||||
return this._aggregate(puuid, {}, [], '$champion.id', groupParams, finalSteps)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Summoner's statistics for all played champion classes
|
||||
* @param puuid of the summoner
|
||||
*/
|
||||
championClassStats(puuid) {
|
||||
const groupId = { '$arrayElemAt': ['$champion.roles', 0] }
|
||||
return this._aggregate(puuid, {}, [], groupId, {}, [])
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Summoner's complete statistics for the all played champs
|
||||
* @param puuid of the summoner
|
||||
* @param queue of the matches to fetch, if null get all matches
|
||||
* @param season of the matches to fetch, if null get all seasons
|
||||
*/
|
||||
championCompleteStats(puuid, queue, season) {
|
||||
const matchParams = {}
|
||||
if (queue) {
|
||||
matchParams.gamemode = { $eq: Number(queue) }
|
||||
}
|
||||
this.season = season
|
||||
|
||||
const groupParams = {
|
||||
time: { $sum: '$time' },
|
||||
gameLength: { $avg: '$time' },
|
||||
date: { $max: '$date' },
|
||||
champion: { $first: '$champion' },
|
||||
kills: { $sum: '$stats.kills' },
|
||||
deaths: { $sum: '$stats.deaths' },
|
||||
assists: { $sum: '$stats.assists' },
|
||||
minions: { $avg: '$stats.minions' },
|
||||
gold: { $avg: '$stats.gold' },
|
||||
dmgChamp: { $avg: '$stats.dmgChamp' },
|
||||
dmgTaken: { $avg: '$stats.dmgTaken' },
|
||||
kp: { $avg: '$stats.kp' },
|
||||
}
|
||||
const finalSteps = [
|
||||
{ $sort: { 'count': -1, 'champion.name': 1 } }
|
||||
]
|
||||
return this._aggregate(puuid, matchParams, [], '$champion.id', groupParams, finalSteps)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Summoner's statistics for all played modes
|
||||
* @param puuid of the summoner
|
||||
*/
|
||||
gamemodeStats(puuid) {
|
||||
return this._aggregate(puuid, {}, [], '$gamemode', {}, [])
|
||||
}
|
||||
|
||||
/**
|
||||
* Get global Summoner's statistics
|
||||
* @param puuid of the summoner
|
||||
*/
|
||||
globalStats(puuid) {
|
||||
const groupParams = {
|
||||
time: { $sum: '$time' },
|
||||
kills: { $sum: '$stats.kills' },
|
||||
deaths: { $sum: '$stats.deaths' },
|
||||
assists: { $sum: '$stats.assists' },
|
||||
minions: { $sum: '$stats.minions' },
|
||||
vision: { $sum: '$stats.vision' },
|
||||
kp: { $avg: '$stats.kp' },
|
||||
}
|
||||
return this._aggregate(puuid, {}, [], null, groupParams, [])
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Summoner's all records
|
||||
* @param puuid of the summoner
|
||||
* @param season of the matches to fetch, if null get all seasons
|
||||
*/
|
||||
records(puuid, season) {
|
||||
this.season = season
|
||||
|
||||
return this.Match.query().aggregate([
|
||||
{
|
||||
$match: {
|
||||
...this._matchParams(puuid),
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: null,
|
||||
maxKills: { $max: '$stats.kills' },
|
||||
maxDeaths: { $max: '$stats.deaths' },
|
||||
maxAssists: { $max: '$stats.assists' },
|
||||
maxGold: { $max: '$stats.gold' },
|
||||
maxTime: { $max: '$time' },
|
||||
maxMinions: { $max: '$stats.minions' },
|
||||
maxKda: { $max: '$stats.realKda' },
|
||||
maxDmgTaken: { $max: '$stats.dmgTaken' },
|
||||
maxDmgChamp: { $max: '$stats.dmgChamp' },
|
||||
maxDmgObj: { $max: '$stats.dmgObj' },
|
||||
maxKp: { $max: '$stats.kp' },
|
||||
maxVision: { $max: '$stats.vision' },
|
||||
maxCriticalStrike: { $max: '$stats.criticalStrike' },
|
||||
maxLiving: { $max: '$stats.longestLiving' },
|
||||
maxHeal: { $max: '$stats.heal' },
|
||||
maxTowers: { $max: '$stats.towers' },
|
||||
maxKillingSpree: { $max: '$stats.killingSpree' },
|
||||
maxDouble: { $max: '$stats.doubleKills' },
|
||||
maxTriple: { $max: '$stats.tripleKills' },
|
||||
maxQuadra: { $max: '$stats.quadraKills' },
|
||||
maxPenta: { $max: '$stats.pentaKills' },
|
||||
docs: {
|
||||
'$push': {
|
||||
'champion': '$champion',
|
||||
'gameId': '$gameId',
|
||||
'kills': '$stats.kills',
|
||||
'deaths': '$stats.deaths',
|
||||
'assists': '$stats.assists',
|
||||
'gold': '$stats.gold',
|
||||
'time': '$time',
|
||||
'minions': '$stats.minions',
|
||||
'kda': '$stats.realKda',
|
||||
'dmgTaken': '$stats.dmgTaken',
|
||||
'dmgChamp': '$stats.dmgChamp',
|
||||
'dmgObj': '$stats.dmgObj',
|
||||
'kp': '$stats.kp',
|
||||
'vision': '$stats.vision',
|
||||
'criticalStrike': '$stats.criticalStrike',
|
||||
'longestLiving': '$stats.longestLiving',
|
||||
'heal': '$stats.heal',
|
||||
'towers': '$stats.towers',
|
||||
'killingSpree': '$stats.killingSpree',
|
||||
'doubleKills': '$stats.doubleKills',
|
||||
'tripleKills': '$stats.tripleKills',
|
||||
'quadraKills': '$stats.quadraKills',
|
||||
'pentaKills': '$stats.pentaKills',
|
||||
'result': '$result',
|
||||
'date': '$date',
|
||||
'gamemode': '$gamemode',
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 0,
|
||||
maxKills: { $arrayElemAt: [{ $filter: { input: '$docs', cond: { $eq: ['$$this.kills', '$maxKills'] } } }, 0] },
|
||||
maxDeaths: { $arrayElemAt: [{ $filter: { input: '$docs', cond: { $eq: ['$$this.deaths', '$maxDeaths'] } } }, 0] },
|
||||
maxAssists: { $arrayElemAt: [{ $filter: { input: '$docs', cond: { $eq: ['$$this.assists', '$maxAssists'] } } }, 0] },
|
||||
maxGold: { $arrayElemAt: [{ $filter: { input: '$docs', cond: { $eq: ['$$this.gold', '$maxGold'] } } }, 0] },
|
||||
maxTime: { $arrayElemAt: [{ $filter: { input: '$docs', cond: { $eq: ['$$this.time', '$maxTime'] } } }, 0] },
|
||||
maxMinions: { $arrayElemAt: [{ $filter: { input: '$docs', cond: { $eq: ['$$this.minions', '$maxMinions'] } } }, 0] },
|
||||
maxKda: { $arrayElemAt: [{ $filter: { input: '$docs', cond: { $eq: ['$$this.kda', '$maxKda'] } } }, 0] },
|
||||
maxDmgTaken: { $arrayElemAt: [{ $filter: { input: '$docs', cond: { $eq: ['$$this.dmgTaken', '$maxDmgTaken'] } } }, 0] },
|
||||
maxDmgChamp: { $arrayElemAt: [{ $filter: { input: '$docs', cond: { $eq: ['$$this.dmgChamp', '$maxDmgChamp'] } } }, 0] },
|
||||
maxDmgObj: { $arrayElemAt: [{ $filter: { input: '$docs', cond: { $eq: ['$$this.dmgObj', '$maxDmgObj'] } } }, 0] },
|
||||
maxKp: { $arrayElemAt: [{ $filter: { input: '$docs', cond: { $eq: ['$$this.kp', '$maxKp'] } } }, 0] },
|
||||
maxVision: { $arrayElemAt: [{ $filter: { input: '$docs', cond: { $eq: ['$$this.vision', '$maxVision'] } } }, 0] },
|
||||
maxCriticalStrike: { $arrayElemAt: [{ $filter: { input: '$docs', cond: { $eq: ['$$this.criticalStrike', '$maxCriticalStrike'] } } }, 0] },
|
||||
maxLiving: { $arrayElemAt: [{ $filter: { input: '$docs', cond: { $eq: ['$$this.longestLiving', '$maxLiving'] } } }, 0] },
|
||||
maxHeal: { $arrayElemAt: [{ $filter: { input: '$docs', cond: { $eq: ['$$this.heal', '$maxHeal'] } } }, 0] },
|
||||
maxTowers: { $arrayElemAt: [{ $filter: { input: '$docs', cond: { $eq: ['$$this.towers', '$maxTowers'] } } }, 0] },
|
||||
maxKillingSpree: { $arrayElemAt: [{ $filter: { input: '$docs', cond: { $eq: ['$$this.killingSpree', '$maxKillingSpree'] } } }, 0] },
|
||||
maxDouble: { $arrayElemAt: [{ $filter: { input: '$docs', cond: { $eq: ['$$this.doubleKills', '$maxDouble'] } } }, 0] },
|
||||
maxTriple: { $arrayElemAt: [{ $filter: { input: '$docs', cond: { $eq: ['$$this.tripleKills', '$maxTriple'] } } }, 0] },
|
||||
maxQuadra: { $arrayElemAt: [{ $filter: { input: '$docs', cond: { $eq: ['$$this.quadraKills', '$maxQuadra'] } } }, 0] },
|
||||
maxPenta: { $arrayElemAt: [{ $filter: { input: '$docs', cond: { $eq: ['$$this.pentaKills', '$maxPenta'] } } }, 0] },
|
||||
}
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Summoner's statistics for the 5 differnt roles
|
||||
* @param puuid of the summoner
|
||||
*/
|
||||
roleStats(puuid) {
|
||||
const matchParams = {
|
||||
role: { $not: { $eq: 'NONE' } }
|
||||
}
|
||||
const finalSteps = [
|
||||
{
|
||||
$project: {
|
||||
role: '$_id',
|
||||
count: '$count',
|
||||
wins: '$wins',
|
||||
losses: '$losses',
|
||||
}
|
||||
}
|
||||
]
|
||||
return this._aggregate(puuid, matchParams, [], '$role', {}, finalSteps)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Summoner's played seasons
|
||||
* @param puuid of the summoner
|
||||
*/
|
||||
seasons(puuid) {
|
||||
this.season = null
|
||||
|
||||
return this.Match.query().aggregate([
|
||||
{
|
||||
$match: {
|
||||
...this._matchParams(puuid),
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: { _id: '$season' }
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Summoner's mates list
|
||||
* @param puuid of the summoner
|
||||
*/
|
||||
mates(puuid) {
|
||||
const intermediateSteps = [
|
||||
{ $sort: { 'gameId': -1 } },
|
||||
{ $unwind: '$allyTeam' },
|
||||
]
|
||||
const groupParams = {
|
||||
account_id: { $first: '$account_id' },
|
||||
name: { $first: '$allyTeam.name' },
|
||||
mateId: { $first: '$allyTeam.account_id' },
|
||||
}
|
||||
const finalSteps = [
|
||||
{
|
||||
'$addFields': {
|
||||
'idEq': { '$eq': ['$mateId', '$account_id'] }
|
||||
}
|
||||
},
|
||||
{
|
||||
$match: {
|
||||
'idEq': false,
|
||||
'count': { $gte: 2 }
|
||||
},
|
||||
},
|
||||
{ $sort: { 'count': -1, 'name': 1 } },
|
||||
{ $limit: 15 },
|
||||
]
|
||||
return this._aggregate(puuid, {}, intermediateSteps, '$allyTeam.account_id', groupParams, finalSteps)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MatchRepository
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
require('dotenv').config()
|
||||
const { STRATEGY } = require('riot-ratelimiter/dist/RateLimiter')
|
||||
|
||||
const JAX_CONFIG = {
|
||||
key: process.env.API_KEY,
|
||||
region: 'euw1',
|
||||
requestOptions: {
|
||||
retriesBeforeAbort: 3,
|
||||
delayBeforeRetry: 1000,
|
||||
// strategy: process.env.NODE_ENV === 'production' ? STRATEGY.SPREAD : STRATEGY.BURST,
|
||||
strategy: STRATEGY.SPREAD,
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = JAX_CONFIG
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
const Jax = require('./src/Jax')
|
||||
const Config = require('./JaxConfig')
|
||||
|
||||
module.exports = new Jax(Config)
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
const { promisify } = require('util')
|
||||
const got = require('got')
|
||||
const Logger = use('Logger')
|
||||
const Redis = use('Redis')
|
||||
|
||||
class CDragonRequest {
|
||||
constructor(config, endpoint, cacheTime) {
|
||||
this.config = config
|
||||
this.endpoint = endpoint
|
||||
this.cacheTime = cacheTime
|
||||
this.retries = config.requestOptions.retriesBeforeAbort
|
||||
|
||||
this.sleep = promisify(setTimeout)
|
||||
}
|
||||
|
||||
// https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/items.json
|
||||
// https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/perks.json
|
||||
// https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/perkstyles.json
|
||||
// https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/champion-summary.json
|
||||
|
||||
async execute() {
|
||||
const url = `https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/${this.endpoint}`
|
||||
|
||||
const requestCached = await Redis.get(url)
|
||||
if (requestCached) {
|
||||
return JSON.parse(requestCached)
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await got(url);
|
||||
|
||||
await Redis.set(url, response.body, 'EX', this.cacheTime)
|
||||
return JSON.parse(response.body)
|
||||
} catch (error) {
|
||||
this.retries--
|
||||
|
||||
Logger.transport('file').error('CDragon Error : ', error)
|
||||
|
||||
if (this.retries > 0) {
|
||||
await this.sleep(this.config.requestOptions.delayBeforeRetry)
|
||||
return this.execute()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = CDragonRequest
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
const CDragonRequest = require('../CDragonRequest')
|
||||
|
||||
class CDragonEndpoint {
|
||||
constructor(config) {
|
||||
this.config = config
|
||||
}
|
||||
|
||||
champions() {
|
||||
return new CDragonRequest(this.config, 'champion-summary.json', 36000).execute()
|
||||
}
|
||||
|
||||
items() {
|
||||
return new CDragonRequest(this.config, 'items.json', 36000).execute()
|
||||
}
|
||||
|
||||
perks() {
|
||||
return new CDragonRequest(this.config, 'perks.json', 36000).execute()
|
||||
}
|
||||
|
||||
perkstyles() {
|
||||
return new CDragonRequest(this.config, 'perkstyles.json', 36000).execute()
|
||||
}
|
||||
|
||||
summonerSpells() {
|
||||
return new CDragonRequest(this.config, 'summoner-spells.json', 36000).execute()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CDragonEndpoint
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
const JaxRequest = require('../JaxRequest')
|
||||
|
||||
class LeagueEndpoint {
|
||||
constructor(config, limiter) {
|
||||
this.config = config
|
||||
this.limiter = limiter
|
||||
}
|
||||
|
||||
summonerID(summonerID, region) {
|
||||
return new JaxRequest(
|
||||
region,
|
||||
this.config,
|
||||
`league/v4/entries/by-summoner/${summonerID}`,
|
||||
this.limiter,
|
||||
300
|
||||
).execute()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LeagueEndpoint
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
const JaxRequest = require('../JaxRequest')
|
||||
|
||||
class MatchEndpoint {
|
||||
constructor(config, limiter) {
|
||||
this.config = config
|
||||
this.limiter = limiter
|
||||
|
||||
this.get = this.get.bind(this)
|
||||
}
|
||||
|
||||
get(matchID, region) {
|
||||
return new JaxRequest(
|
||||
region,
|
||||
this.config,
|
||||
`match/v4/matches/${matchID}`,
|
||||
this.limiter,
|
||||
1500
|
||||
).execute()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MatchEndpoint
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
const JaxRequest = require('../JaxRequest')
|
||||
|
||||
class MatchlistEndpoint {
|
||||
constructor(config, limiter) {
|
||||
this.config = config
|
||||
this.limiter = limiter
|
||||
}
|
||||
|
||||
accountID(accountID, region, beginIndex = 0) {
|
||||
return new JaxRequest(
|
||||
region,
|
||||
this.config,
|
||||
`match/v4/matchlists/by-account/${accountID}?beginIndex=${beginIndex}`,
|
||||
this.limiter,
|
||||
0
|
||||
).execute()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MatchlistEndpoint
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
const JaxRequest = require('../JaxRequest')
|
||||
|
||||
class SpectatorEndpoint {
|
||||
constructor(config, limiter) {
|
||||
this.config = config
|
||||
this.limiter = limiter
|
||||
}
|
||||
|
||||
summonerID(summonerID, region) {
|
||||
return new JaxRequest(
|
||||
region,
|
||||
this.config,
|
||||
`spectator/v4/active-games/by-summoner/${summonerID}`,
|
||||
this.limiter,
|
||||
0
|
||||
).execute()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SpectatorEndpoint
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
const JaxRequest = require('../JaxRequest')
|
||||
|
||||
class SummonerEndpoint {
|
||||
constructor(config, limiter) {
|
||||
this.config = config
|
||||
this.limiter = limiter
|
||||
}
|
||||
|
||||
summonerName(summonerName, region) {
|
||||
return new JaxRequest(
|
||||
region,
|
||||
this.config,
|
||||
`summoner/v4/summoners/by-name/${encodeURI(summonerName)}`,
|
||||
this.limiter,
|
||||
36000
|
||||
).execute()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SummonerEndpoint
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
const RiotRateLimiter = require('riot-ratelimiter')
|
||||
const LeagueEndpoint = require('./Endpoints/LeagueEndpoint')
|
||||
const MatchEndpoint = require('./Endpoints/MatchEndpoint')
|
||||
const MatchlistEndpoint = require('./Endpoints/MatchlistEndpoint')
|
||||
const SpectatorEndpoint = require('./Endpoints/SpectatorEndpoint')
|
||||
const SummonerEndpoint = require('./Endpoints/SummonerEndpoint')
|
||||
|
||||
const CDragonEndpoint = require('./Endpoints/CDragonEndpoint')
|
||||
|
||||
class Jax {
|
||||
constructor(config) {
|
||||
this.key = config.key
|
||||
const limiterOptions = {
|
||||
strategy: config.requestOptions.strategy
|
||||
}
|
||||
this.limiter = new RiotRateLimiter(limiterOptions)
|
||||
this.config = config
|
||||
|
||||
this.League = new LeagueEndpoint(this.config, this.limiter)
|
||||
this.Match = new MatchEndpoint(this.config, this.limiter)
|
||||
this.Matchlist = new MatchlistEndpoint(this.config, this.limiter)
|
||||
this.Spectator = new SpectatorEndpoint(this.config, this.limiter)
|
||||
this.Summoner = new SummonerEndpoint(this.config, this.limiter)
|
||||
|
||||
this.CDragon = new CDragonEndpoint(this.config)
|
||||
}
|
||||
|
||||
set regionName(regionName) {
|
||||
this.config.region = regionName
|
||||
const blacklistedProperties = ['key', 'limiter', 'config', 'version', 'CDragon']
|
||||
|
||||
for (const key of Object.getOwnPropertyNames(this)) {
|
||||
if(blacklistedProperties.includes(key)) continue
|
||||
|
||||
this[key].region = regionName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Jax
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
const { promisify } = require('util')
|
||||
const Logger = use('Logger')
|
||||
const Redis = use('Redis')
|
||||
|
||||
class JaxRequest {
|
||||
constructor(region, config, endpoint, limiter, cacheTime) {
|
||||
this.region = region
|
||||
this.config = config
|
||||
this.endpoint = endpoint
|
||||
this.limiter = limiter
|
||||
this.cacheTime = cacheTime
|
||||
this.retries = config.requestOptions.retriesBeforeAbort
|
||||
|
||||
this.sleep = promisify(setTimeout)
|
||||
}
|
||||
|
||||
async execute() {
|
||||
const url = `https://${this.region}.api.riotgames.com/lol/${this.endpoint}`
|
||||
|
||||
// Redis cache
|
||||
if (this.cacheTime > 0) {
|
||||
const requestCached = await Redis.get(url)
|
||||
if (requestCached) {
|
||||
return JSON.parse(requestCached)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const resp = await this.limiter.executing({
|
||||
url,
|
||||
token: this.config.key,
|
||||
resolveWithFullResponse: false
|
||||
})
|
||||
|
||||
if (this.cacheTime > 0) {
|
||||
await Redis.set(url, resp, 'EX', this.cacheTime)
|
||||
}
|
||||
return JSON.parse(resp)
|
||||
} catch ({ statusCode, ...rest }) {
|
||||
this.retries--
|
||||
|
||||
if (statusCode !== 500 && statusCode !== 503 && statusCode !== 504) {
|
||||
// Don't log 404 when summoner isn't playing or the summoner doesn't exist
|
||||
if (!this.endpoint.includes('spectator/v4/active-games/by-summoner') && !this.endpoint.includes('summoner/v4/summoners/by-name')) {
|
||||
Logger.transport('file').error(`JaxRequest Error ${statusCode} : `, rest)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
console.log('====================================')
|
||||
console.log(statusCode)
|
||||
console.log('====================================')
|
||||
|
||||
if (this.retries > 0) {
|
||||
await this.sleep(this.config.requestOptions.delayBeforeRetry)
|
||||
return this.execute()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = JaxRequest
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const Logger = use('Logger')
|
||||
const Jax = use('App/Services/Jax')
|
||||
const BasicMatchTransformer = use('App/Transformers/BasicMatchTransformer')
|
||||
const { getSeasonNumber } = use('App/helpers')
|
||||
|
||||
class MatchService {
|
||||
/**
|
||||
* Add 100 matches at a time to MatchList until the stopFetching condition is true
|
||||
* @param account of the summoner
|
||||
* @param stopFetching condition to stop fetching the MatchList
|
||||
*/
|
||||
async _fetchMatchListUntil(account, stopFetching) {
|
||||
let matchList = []
|
||||
let alreadyIn = false
|
||||
let index = 0
|
||||
do {
|
||||
let newMatchList = await Jax.Matchlist.accountID(account.accountId, account.region, index)
|
||||
// Error while fetching Riot API
|
||||
if (!newMatchList) {
|
||||
matchList = matchList.map(m => {
|
||||
m.seasonMatch = getSeasonNumber(m.timestamp)
|
||||
return m
|
||||
})
|
||||
return matchList
|
||||
}
|
||||
newMatchList = newMatchList.matches
|
||||
matchList = [...matchList, ...newMatchList]
|
||||
alreadyIn = newMatchList.length === 0 || stopFetching(newMatchList)
|
||||
// If the match is made in another region : we stop fetching
|
||||
if (matchList[matchList.length - 1].platformId.toLowerCase() !== account.region) {
|
||||
alreadyIn = true;
|
||||
}
|
||||
index += 100
|
||||
} while (!alreadyIn);
|
||||
|
||||
// Remove matches from MatchList made in another region and tutorial games
|
||||
const tutorialModes = [2000, 2010, 2020]
|
||||
matchList = matchList
|
||||
.filter(m => {
|
||||
const sameRegion = m.platformId.toLowerCase() === account.region
|
||||
const notATutorialGame = !tutorialModes.includes(m.queue)
|
||||
|
||||
return sameRegion && notATutorialGame
|
||||
})
|
||||
.map(m => {
|
||||
m.seasonMatch = getSeasonNumber(m.timestamp)
|
||||
return m
|
||||
})
|
||||
|
||||
return matchList
|
||||
}
|
||||
/**
|
||||
* Update the full MatchList of the summoner (min. 4 months)
|
||||
* @param account of the summoner
|
||||
* @param summonerDB summoner in the database
|
||||
*/
|
||||
async updateMatchList(account, summonerDB) {
|
||||
console.time('matchList')
|
||||
|
||||
// Summoner has already been searched : we already have a MatchList and we need to update it
|
||||
if (summonerDB.matchList) {
|
||||
// Get MatchList
|
||||
const matchList = await this._fetchMatchListUntil(account, (newMatchList) => {
|
||||
return summonerDB.matchList.some(m => m.gameId === newMatchList[newMatchList.length - 1].gameId)
|
||||
})
|
||||
// Update Summoner's MatchList
|
||||
for (const match of matchList.reverse()) {
|
||||
if (!summonerDB.matchList.some(m => m.gameId === match.gameId)) {
|
||||
summonerDB.matchList.unshift(match)
|
||||
}
|
||||
}
|
||||
}
|
||||
// First search of the Summoner
|
||||
else {
|
||||
const today = Date.now()
|
||||
// Get MatchList
|
||||
const matchList = await this._fetchMatchListUntil(account, (newMatchList) => {
|
||||
return (newMatchList.length !== 100 || today - newMatchList[newMatchList.length - 1].timestamp > 10368000000)
|
||||
})
|
||||
// Create Summoner's MatchList in Database
|
||||
summonerDB.matchList = matchList
|
||||
}
|
||||
console.timeEnd('matchList')
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch list of matches for a specific Summoner
|
||||
* @param account of the summoner
|
||||
* @param gameIds of the matches to fetch
|
||||
* @param summonerDB summoner in the database
|
||||
*/
|
||||
async getMatches(account, gameIds, summonerDB) {
|
||||
console.time('getMatches')
|
||||
|
||||
let matchesDetails = []
|
||||
const matchesToGetFromRiot = []
|
||||
for (let i = 0; i < gameIds.length; ++i) {
|
||||
const matchSaved = await summonerDB.matches().where({ gameId: gameIds[i] }).first()
|
||||
if (matchSaved) {
|
||||
matchesDetails.push(matchSaved)
|
||||
} else {
|
||||
matchesToGetFromRiot.push(gameIds[i])
|
||||
}
|
||||
}
|
||||
|
||||
const requests = matchesToGetFromRiot.map(gameId => Jax.Match.get(gameId, account.region))
|
||||
let matchesFromApi = await Promise.all(requests)
|
||||
|
||||
/* If we have to store some matches in the db */
|
||||
if (matchesFromApi.length !== 0) {
|
||||
// Try to see why matches are sometimes undefined
|
||||
matchesFromApi.filter(m => {
|
||||
if (m === undefined) {
|
||||
Logger.transport('file').info(`Match undefined, summoner: ${summonerDB.puuid}`, m)
|
||||
}
|
||||
})
|
||||
|
||||
// Transform raw matches data
|
||||
await BasicMatchTransformer.transform(matchesFromApi, { account })
|
||||
|
||||
/* Save all matches from Riot Api in db */
|
||||
for (const match of matchesFromApi) {
|
||||
await summonerDB.matches().create(match)
|
||||
match.newMatch = true
|
||||
}
|
||||
matchesDetails = [...matchesDetails, ...matchesFromApi]
|
||||
}
|
||||
|
||||
/* Sort matches */
|
||||
matchesDetails.sort((a, b) => (a.date < b.date) ? 1 : -1)
|
||||
console.timeEnd('getMatches')
|
||||
|
||||
return matchesDetails
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new MatchService()
|
||||
|
|
@ -1,226 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const got = require('got')
|
||||
const Redis = use('Redis')
|
||||
|
||||
class RoleIdentificationService {
|
||||
_getPermutations(array) {
|
||||
const result = []
|
||||
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
const rest = this._getPermutations(array.slice(0, i).concat(array.slice(i + 1)))
|
||||
|
||||
if (!rest.length) {
|
||||
result.push([array[i]])
|
||||
} else {
|
||||
for (let j = 0; j < rest.length; j++) {
|
||||
result.push([array[i]].concat(rest[j]))
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
_calculateMetric(championPositions, bestPositions) {
|
||||
return Object.entries(bestPositions).reduce((agg, [position, champion]) => {
|
||||
return agg + (championPositions[champion][position] || 0)
|
||||
}, 0) / Object.keys(bestPositions).length
|
||||
}
|
||||
|
||||
_getPositions(championPositions, composition, top, jungle, middle, adc, support) {
|
||||
// Set the initial guess to be the champion in the composition, order doesn't matter
|
||||
let bestPositions = {
|
||||
'TOP': composition[0],
|
||||
'JUNGLE': composition[1],
|
||||
'MIDDLE': composition[2],
|
||||
'BOTTOM': composition[3],
|
||||
'UTILITY': composition[4],
|
||||
}
|
||||
|
||||
let bestMetric = this._calculateMetric(championPositions, bestPositions)
|
||||
let secondBestMetric = -Infinity
|
||||
let secondBestPositions = null
|
||||
|
||||
// Figure out which champions and positions we need to fill
|
||||
const knownChampions = [top, jungle, middle, adc, support].filter(Boolean)
|
||||
const unknownChampions = composition.filter(champ => !knownChampions.includes(champ))
|
||||
const unknownPositions = Object.entries({
|
||||
'TOP': top, 'JUNGLE': jungle, 'MIDDLE': middle, 'BOTTOM': adc, 'UTILITY': support,
|
||||
})
|
||||
.filter(pos => !pos[1])
|
||||
.map(pos => pos[0])
|
||||
|
||||
const testComposition = {
|
||||
'TOP': top,
|
||||
'JUNGLE': jungle,
|
||||
'MIDDLE': middle,
|
||||
'BOTTOM': adc,
|
||||
'UTILITY': support,
|
||||
}
|
||||
|
||||
// Iterate over the positions we need to fill and record how well each composition "performs"
|
||||
for (const champs of this._getPermutations(unknownChampions)) {
|
||||
for (let [i, position] of unknownPositions.entries()) {
|
||||
testComposition[position] = champs[i]
|
||||
}
|
||||
|
||||
const metric = this._calculateMetric(championPositions, testComposition)
|
||||
|
||||
if (metric > bestMetric) {
|
||||
secondBestMetric = bestMetric
|
||||
secondBestPositions = bestPositions
|
||||
bestMetric = metric
|
||||
bestPositions = { ...testComposition }
|
||||
}
|
||||
|
||||
if (bestMetric > metric && metric > secondBestMetric) {
|
||||
secondBestMetric = metric
|
||||
secondBestPositions = { ...testComposition }
|
||||
}
|
||||
}
|
||||
|
||||
const bestPlayPercents = {}
|
||||
for (const [position, champion] of Object.entries(bestPositions)) {
|
||||
bestPlayPercents[champion] = championPositions[champion][position]
|
||||
}
|
||||
|
||||
let secondBestPlayPercents = null
|
||||
if (secondBestPositions !== null) {
|
||||
secondBestPlayPercents = {}
|
||||
for (const [position, champion] of Object.entries(secondBestPositions)) {
|
||||
secondBestPlayPercents[champion] = championPositions[champion][position]
|
||||
}
|
||||
}
|
||||
|
||||
if (JSON.stringify(secondBestPositions) === JSON.stringify(bestPositions)) {
|
||||
secondBestPositions = null
|
||||
secondBestPlayPercents = null
|
||||
secondBestMetric = -Infinity
|
||||
}
|
||||
|
||||
return { bestPositions, bestMetric, secondBestPositions }
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CDN data of the champion playrates by role
|
||||
*/
|
||||
async pullData() {
|
||||
const url = 'http://cdn.merakianalytics.com/riot/lol/resources/latest/en-US/championrates.json'
|
||||
|
||||
// Check if cached
|
||||
const requestCached = await Redis.get(url)
|
||||
if (requestCached) {
|
||||
return JSON.parse(requestCached)
|
||||
}
|
||||
|
||||
const data = {}
|
||||
const response = await got(url, { responseType: 'json' })
|
||||
|
||||
for (const [championId, roles] of Object.entries(response.body.data)) {
|
||||
const playRates = {}
|
||||
|
||||
for (const [position, rates] of Object.entries(roles)) {
|
||||
playRates[position.toUpperCase()] = rates['playRate']
|
||||
}
|
||||
|
||||
for (const position of ['TOP', 'JUNGLE', 'MIDDLE', 'BOTTOM', 'UTILITY']) {
|
||||
if (playRates[position] === undefined) {
|
||||
playRates[position] = 0
|
||||
}
|
||||
}
|
||||
|
||||
data[championId] = playRates
|
||||
}
|
||||
|
||||
// Cache result
|
||||
await Redis.set(url, JSON.stringify(data), 'EX', 36000)
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* Get roles for the 5 players of a team
|
||||
* @param championPositions
|
||||
* @param composition
|
||||
* @param jungle
|
||||
* @param support
|
||||
*/
|
||||
getRoles(championPositions, composition, jungle = null, support = null) {
|
||||
// Set composition champion playrate to 0% if not present in the json data
|
||||
for (const compChamp of composition) {
|
||||
if (championPositions[compChamp]) {
|
||||
continue
|
||||
}
|
||||
|
||||
championPositions[compChamp] = {
|
||||
MIDDLE: 0,
|
||||
UTILITY: 0,
|
||||
TOP: 0,
|
||||
JUNGLE: 0,
|
||||
BOTTOM: 0
|
||||
}
|
||||
}
|
||||
|
||||
const identified = {}
|
||||
let positions = {}
|
||||
let secondaryPositions = null
|
||||
let secondaryMetric = -Infinity
|
||||
|
||||
if (jungle) {
|
||||
identified['JUNGLE'] = jungle
|
||||
}
|
||||
|
||||
if (support) {
|
||||
identified['UTILITY'] = support
|
||||
}
|
||||
|
||||
while (Object.keys(identified).length < composition.length - 1) {
|
||||
let { bestPositions, bestMetric: metric, secondBestPositions: sbp } =
|
||||
this._getPositions(championPositions, composition,
|
||||
identified.TOP, identified.JUNGLE, identified.MIDDLE, identified.ADC, identified.UTILITY
|
||||
)
|
||||
|
||||
positions = bestPositions
|
||||
|
||||
if (sbp !== null) {
|
||||
let _metric = this._calculateMetric(championPositions, { ...sbp })
|
||||
|
||||
if (secondaryPositions === null) {
|
||||
secondaryPositions = sbp
|
||||
secondaryMetric = _metric
|
||||
} else if (metric > _metric && _metric > secondaryMetric) {
|
||||
secondaryMetric = _metric
|
||||
secondaryPositions = sbp
|
||||
}
|
||||
}
|
||||
|
||||
// Done! Grab the results.
|
||||
const positionsWithMetric = {}
|
||||
for (const [position, champion] of Object.entries(positions)) {
|
||||
if (Object.keys(identified).includes(position) || champion === jungle || champion === support) {
|
||||
continue
|
||||
}
|
||||
positionsWithMetric[position] = {
|
||||
champion,
|
||||
metric: championPositions[champion][position],
|
||||
}
|
||||
}
|
||||
const bestPosition = Object.keys(positionsWithMetric).reduce((posA, posB) => {
|
||||
return positionsWithMetric[posA].metric > positionsWithMetric[posB].metric ? posA : posB
|
||||
})
|
||||
|
||||
const best = [bestPosition, positionsWithMetric[bestPosition].champion]
|
||||
identified[best[0]] = best[1]
|
||||
}
|
||||
|
||||
// Rename UTILITY to SUPPORT
|
||||
const {
|
||||
UTILITY: SUPPORT,
|
||||
...rest
|
||||
} = positions
|
||||
|
||||
return { ...rest, SUPPORT }
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new RoleIdentificationService()
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const Helpers = use('App/helpers')
|
||||
const MatchRepository = make('App/Repositories/MatchRepository')
|
||||
|
||||
class StatsService {
|
||||
constructor() {
|
||||
this.matchRepository = MatchRepository
|
||||
}
|
||||
|
||||
async getSummonerStats(account, season) {
|
||||
this.matchRepository.season = season
|
||||
console.time('GLOBAL')
|
||||
const globalStats = await this.matchRepository.globalStats(account.puuid)
|
||||
console.timeEnd('GLOBAL')
|
||||
console.time('GAMEMODE')
|
||||
const gamemodeStats = await this.matchRepository.gamemodeStats(account.puuid)
|
||||
console.timeEnd('GAMEMODE')
|
||||
console.time('ROLE')
|
||||
const roleStats = await this.matchRepository.roleStats(account.puuid)
|
||||
// Check if all roles are in the array
|
||||
const roles = ['TOP', 'JUNGLE', 'MIDDLE', 'BOTTOM', 'SUPPORT']
|
||||
for (const role of roles) {
|
||||
if (!roleStats.find(r => r.role === role)) {
|
||||
roleStats.push({
|
||||
count: 0,
|
||||
losses: 0,
|
||||
role,
|
||||
wins: 0
|
||||
})
|
||||
}
|
||||
}
|
||||
console.timeEnd('ROLE')
|
||||
console.time('CHAMPION')
|
||||
const championStats = await this.matchRepository.championStats(account.puuid, 5)
|
||||
console.timeEnd('CHAMPION')
|
||||
console.time('CHAMPION-CLASS')
|
||||
const championClassStats = await this.matchRepository.championClassStats(account.puuid)
|
||||
console.timeEnd('CHAMPION-CLASS')
|
||||
console.time('MATES')
|
||||
const mates = await this.matchRepository.mates(account.puuid)
|
||||
console.timeEnd('MATES')
|
||||
|
||||
return {
|
||||
global: globalStats[0],
|
||||
league: gamemodeStats,
|
||||
role: roleStats.sort(Helpers.sortTeamByRole),
|
||||
class: championClassStats,
|
||||
mates,
|
||||
champion: championStats,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new StatsService()
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const Jax = use('App/Services/Jax')
|
||||
|
||||
class SummonerService {
|
||||
constructor() {
|
||||
this.uniqueLeagues = ['CHALLENGER', 'GRANDMASTER', 'MASTER']
|
||||
this.leaguesNumbers = { 'I': 1, 'II': 2, 'III': 3, 'IV': 4 }
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to transform League Data from the Riot API
|
||||
* @param league raw data of the league from Riot API
|
||||
*/
|
||||
_getleagueData(league) {
|
||||
if (!league) return null
|
||||
league.fullRank = this.uniqueLeagues.includes(league.tier) ? league.tier : `${league.tier} ${league.rank}`
|
||||
league.winrate = +(league.wins * 100 / (league.wins + league.losses)).toFixed(1) + '%'
|
||||
league.shortName = this.uniqueLeagues.includes(league.tier) ? league.leaguePoints : league.tier[0] + this.leaguesNumbers[league.rank]
|
||||
return league
|
||||
}
|
||||
|
||||
/**
|
||||
* Get account infos for a searched summoner name
|
||||
* @param summonerName
|
||||
* @param region
|
||||
*/
|
||||
async getAccount(summonerName, region) {
|
||||
const name = summonerName.toLowerCase().replace(/ /g, '')
|
||||
const account = await Jax.Summoner.summonerName(name, region)
|
||||
return account
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the full list of old and actual summoner names
|
||||
* @param account of the summoner
|
||||
* @param summonerDB summoner in the database
|
||||
*/
|
||||
getAllSummonerNames(account, summonerDB) {
|
||||
const names = summonerDB.names ? summonerDB.names : []
|
||||
|
||||
if (!names.find(n => n.name === account.name)) {
|
||||
names.push({
|
||||
name: account.name,
|
||||
date: new Date()
|
||||
})
|
||||
summonerDB.names = names
|
||||
}
|
||||
|
||||
return names
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ranked data for a specific Summoner
|
||||
* @param account
|
||||
* @param region
|
||||
*/
|
||||
async getRanked(account, region) {
|
||||
const ranked = await Jax.League.summonerID(account.id, region)
|
||||
const result = {
|
||||
soloQ: this._getleagueData(ranked.find(e => e.queueType === 'RANKED_SOLO_5x5')) || null,
|
||||
flex5v5: this._getleagueData(ranked.find(e => e.queueType === 'RANKED_FLEX_SR')) || null,
|
||||
flex3v3: this._getleagueData(ranked.find(e => e.queueType === 'RANKED_FLEX_TT')) || null
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new SummonerService()
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const MatchTransformer = use('App/Transformers/MatchTransformer')
|
||||
const { queuesWithRole } = use('App/helpers')
|
||||
|
||||
/**
|
||||
* BasicMatchTransformer class
|
||||
*
|
||||
* @class BasicMatchTransformer
|
||||
*/
|
||||
class BasicMatchTransformer extends MatchTransformer {
|
||||
/**
|
||||
* Transform raw data from Riot API
|
||||
* @param matches data from Riot API, Array of match or a single match
|
||||
* @param ctx context
|
||||
*/
|
||||
async transform(matches, { account }) {
|
||||
await super.getContext()
|
||||
|
||||
if (Array.isArray(matches)) {
|
||||
matches.forEach((match, index) => {
|
||||
matches[index] = this.transformOneMatch(match, account)
|
||||
})
|
||||
} else {
|
||||
matches = this.transformOneMatch(matches, account)
|
||||
}
|
||||
|
||||
return matches
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform raw data for 1 match
|
||||
*/
|
||||
transformOneMatch(match, account) {
|
||||
// Global data about the match
|
||||
const globalInfos = super.getGameInfos(match)
|
||||
|
||||
const identity = match.participantIdentities.find((p) => p.player.currentAccountId === account.accountId)
|
||||
const player = match.participants[identity.participantId - 1]
|
||||
|
||||
let win = match.teams.find((t) => t.teamId === player.teamId).win
|
||||
|
||||
// Match less than 5min
|
||||
if (match.gameDuration < 300) {
|
||||
win = 'Remake'
|
||||
}
|
||||
|
||||
// Player data
|
||||
const playerData = super.getPlayerData(match, player, false)
|
||||
|
||||
// Teams data
|
||||
const allyTeam = []
|
||||
const enemyTeam = []
|
||||
for (let summoner of match.participantIdentities) {
|
||||
const allData = match.participants[summoner.participantId - 1]
|
||||
const playerInfos = {
|
||||
account_id: summoner.player.currentAccountId,
|
||||
name: summoner.player.summonerName,
|
||||
role: super.getRoleName(allData.timeline, match.queueId),
|
||||
champion: super.getChampion(allData.championId)
|
||||
}
|
||||
|
||||
if (allData.teamId === player.teamId) {
|
||||
allyTeam.push(playerInfos)
|
||||
} else {
|
||||
enemyTeam.push(playerInfos)
|
||||
}
|
||||
}
|
||||
|
||||
// Roles
|
||||
super.getMatchRoles(match, allyTeam, enemyTeam, player.teamId, playerData)
|
||||
|
||||
return {
|
||||
account_id: identity.player.currentAccountId,
|
||||
summoner_puuid: account.puuid,
|
||||
gameId: match.gameId,
|
||||
result: win,
|
||||
allyTeam,
|
||||
enemyTeam,
|
||||
...globalInfos,
|
||||
...playerData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new BasicMatchTransformer()
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const MatchTransformer = use('App/Transformers/MatchTransformer')
|
||||
const SummonerService = use('App/Services/SummonerService')
|
||||
|
||||
/**
|
||||
* DetailedMatchTransformer class
|
||||
*
|
||||
* @class DetailedMatchTransformer
|
||||
*/
|
||||
class DetailedMatchTransformer extends MatchTransformer {
|
||||
/**
|
||||
* Transform raw data from Riot API
|
||||
* @param match data from Riot API
|
||||
*/
|
||||
async transform(match) {
|
||||
await super.getContext()
|
||||
|
||||
// Global data
|
||||
const globalInfos = super.getGameInfos(match)
|
||||
|
||||
// Teams
|
||||
const firstTeam = this.getTeamData(match, match.teams[0])
|
||||
const secondTeam = this.getTeamData(match, match.teams[1])
|
||||
|
||||
// Roles
|
||||
super.getMatchRoles(match, firstTeam.players, secondTeam.players)
|
||||
|
||||
return {
|
||||
gameId: match.gameId,
|
||||
season: match.seasonId,
|
||||
blueTeam: firstTeam.color === 'Blue' ? firstTeam : secondTeam,
|
||||
redTeam: firstTeam.color === 'Blue' ? secondTeam : firstTeam,
|
||||
...globalInfos
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all data of one team
|
||||
* @param match raw match data from Riot API
|
||||
* @param team raw team data from Riot API
|
||||
*/
|
||||
getTeamData(match, team) {
|
||||
let win = team.win
|
||||
if (match.gameDuration < 300) {
|
||||
win = 'Remake'
|
||||
}
|
||||
|
||||
// Global stats of the team
|
||||
const teamPlayers = match.participants.filter(p => p.teamId === team.teamId)
|
||||
const teamStats = teamPlayers.reduce((prev, cur) => {
|
||||
prev.kills += cur.stats.kills
|
||||
prev.deaths += cur.stats.deaths
|
||||
prev.assists += cur.stats.assists
|
||||
prev.gold += cur.stats.goldEarned
|
||||
prev.dmgChamp += cur.stats.totalDamageDealtToChampions
|
||||
prev.dmgObj += cur.stats.damageDealtToObjectives
|
||||
prev.dmgTaken += cur.stats.totalDamageTaken
|
||||
return prev;
|
||||
}, { kills: 0, deaths: 0, assists: 0, gold: 0, dmgChamp: 0, dmgObj: 0, dmgTaken: 0 });
|
||||
|
||||
// Bans
|
||||
let bans = null
|
||||
if (team.bans) {
|
||||
bans = team.bans.map(b => {
|
||||
if (b.championId === -1) {
|
||||
b.champion = {
|
||||
id: null,
|
||||
name: null
|
||||
}
|
||||
} else {
|
||||
b.champion = super.getChampion(b.championId)
|
||||
}
|
||||
return b
|
||||
})
|
||||
}
|
||||
|
||||
// Players
|
||||
let players = teamPlayers
|
||||
.map(p => super.getPlayerData(match, p, true, teamStats))
|
||||
.map(p => {
|
||||
p.firstSum = super.getSummonerSpell(p.firstSum)
|
||||
p.secondSum = super.getSummonerSpell(p.secondSum)
|
||||
return p
|
||||
})
|
||||
.sort(this.sortTeamByRole)
|
||||
|
||||
return {
|
||||
bans,
|
||||
barons: team.baronKills,
|
||||
color: team.teamId === 100 ? 'Blue' : 'Red',
|
||||
dragons: team.dragonKills,
|
||||
inhibitors: team.inhibitorKills,
|
||||
players,
|
||||
result: win,
|
||||
riftHerald: team.riftHeraldKills,
|
||||
teamStats,
|
||||
towers: team.towerKills,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new DetailedMatchTransformer()
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const MatchTransformer = use('App/Transformers/MatchTransformer')
|
||||
const SummonerService = use('App/Services/SummonerService')
|
||||
const { queuesWithRole } = use('App/helpers')
|
||||
|
||||
/**
|
||||
* LiveMatchTransformer class
|
||||
*
|
||||
* @class LiveMatchTransformer
|
||||
*/
|
||||
class LiveMatchTransformer extends MatchTransformer {
|
||||
async _getPlayerRank(participant, region) {
|
||||
const account = await SummonerService.getAccount(participant.summonerName, region)
|
||||
if (account) {
|
||||
participant.level = account.summonerLevel
|
||||
const ranked = await SummonerService.getRanked(account, region)
|
||||
participant.rank = ranked
|
||||
} else {
|
||||
participant.rank = null
|
||||
}
|
||||
|
||||
return participant
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform raw data from Riot API
|
||||
* @param match data from Riot API, one live match
|
||||
*/
|
||||
async transform(match, { region }) {
|
||||
await super.getContext()
|
||||
|
||||
// Roles
|
||||
const blueTeam = [] // 100
|
||||
const redTeam = [] // 200
|
||||
let blueRoles = []
|
||||
let redRoles = []
|
||||
const needsRole = this.championRoles && queuesWithRole.includes(match.gameQueueConfigId)
|
||||
if (needsRole) {
|
||||
match.participants.map(p => {
|
||||
const playerRole = { champion: p.championId, jungle: p.spell1Id === 11 || p.spell2Id === 11 }
|
||||
p.teamId === 100 ? blueTeam.push(playerRole) : redTeam.push(playerRole)
|
||||
})
|
||||
|
||||
blueRoles = super.getTeamRoles(blueTeam)
|
||||
redRoles = super.getTeamRoles(redTeam)
|
||||
}
|
||||
|
||||
for (const participant of match.participants) {
|
||||
// Perks
|
||||
participant.runes = participant.perks ? super.getPerksImages(participant.perks.perkIds[0], participant.perks.perkSubStyle) : {}
|
||||
|
||||
// Roles
|
||||
if (needsRole) {
|
||||
const roles = participant.teamId === 100 ? blueRoles : redRoles
|
||||
participant.role = Object.entries(roles).find(([, champion]) => participant.championId === champion)[0]
|
||||
}
|
||||
}
|
||||
|
||||
// Ranks
|
||||
const requestsParticipants = match.participants.map(p => this._getPlayerRank(p, region))
|
||||
match.participants = await Promise.all(requestsParticipants)
|
||||
|
||||
return match
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new LiveMatchTransformer()
|
||||
|
|
@ -1,288 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const Jax = use('App/Services/Jax')
|
||||
const RoleIdentificationService = use('App/Services/RoleIdentificationService')
|
||||
const { getSeasonNumber, queuesWithRole, sortTeamByRole, supportItems } = use('App/helpers')
|
||||
|
||||
/**
|
||||
* MatchTransformer class
|
||||
*
|
||||
* @class MatchTransformer
|
||||
*/
|
||||
class MatchTransformer {
|
||||
/**
|
||||
* Get global Context with CDragon Data
|
||||
*/
|
||||
async getContext() {
|
||||
const items = await Jax.CDragon.items()
|
||||
const champions = await Jax.CDragon.champions()
|
||||
const perks = await Jax.CDragon.perks()
|
||||
const perkstyles = await Jax.CDragon.perkstyles()
|
||||
const summonerSpells = await Jax.CDragon.summonerSpells()
|
||||
const championRoles = await RoleIdentificationService.pullData().catch(() => { })
|
||||
|
||||
this.champions = champions
|
||||
this.items = items
|
||||
this.perks = perks
|
||||
this.perkstyles = perkstyles.styles
|
||||
this.summonerSpells = summonerSpells
|
||||
this.championRoles = championRoles
|
||||
this.sortTeamByRole = sortTeamByRole
|
||||
}
|
||||
|
||||
/**
|
||||
* Get champion specific data
|
||||
* @param id of the champion
|
||||
*/
|
||||
getChampion(id) {
|
||||
const champion = { ...this.champions.find(c => c.id === id) }
|
||||
champion.icon = `https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/${champion.squarePortraitPath.split('/assets/')[1].toLowerCase()}`
|
||||
delete champion.squarePortraitPath
|
||||
return champion
|
||||
}
|
||||
|
||||
/**
|
||||
* Get global data about the match
|
||||
*/
|
||||
getGameInfos(match) {
|
||||
return {
|
||||
map: match.mapId,
|
||||
gamemode: match.queueId,
|
||||
date: match.gameCreation,
|
||||
region: match.platformId.toLowerCase(),
|
||||
season: getSeasonNumber(match.gameCreation),
|
||||
time: match.gameDuration
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get player specific data during the match
|
||||
* @param match
|
||||
* @param player
|
||||
* @param detailed : detailed or not stats
|
||||
* @param teamStats : if detailed, the teamStats argument is mandatory
|
||||
*/
|
||||
getPlayerData(match, player, detailed, teamStats = {}) {
|
||||
const identity = match.participantIdentities.find(p => p.participantId === player.participantId)
|
||||
const name = identity.player.summonerName
|
||||
const champion = this.getChampion(player.championId)
|
||||
const role = this.getRoleName(player.timeline, match.queueId)
|
||||
const level = player.stats.champLevel
|
||||
|
||||
// Regular stats / Full match stats
|
||||
const stats = {
|
||||
kills: player.stats.kills,
|
||||
deaths: player.stats.deaths,
|
||||
assists: player.stats.assists,
|
||||
minions: player.stats.totalMinionsKilled + player.stats.neutralMinionsKilled,
|
||||
vision: player.stats.visionScore,
|
||||
gold: player.stats.goldEarned,
|
||||
dmgChamp: player.stats.totalDamageDealtToChampions,
|
||||
dmgObj: player.stats.damageDealtToObjectives,
|
||||
dmgTaken: player.stats.totalDamageTaken,
|
||||
}
|
||||
|
||||
if (stats.kills + stats.assists !== 0 && stats.deaths === 0) {
|
||||
stats.kda = '∞'
|
||||
stats.realKda = stats.kills + stats.assists
|
||||
} else {
|
||||
stats.kda = +(stats.deaths === 0 ? 0 : ((stats.kills + stats.assists) / stats.deaths)).toFixed(2)
|
||||
stats.realKda = stats.kda
|
||||
}
|
||||
|
||||
// Percent stats / Per minute stats : only for detailed match
|
||||
let percentStats
|
||||
if (detailed) {
|
||||
percentStats = {
|
||||
minions: +(stats.minions / (match.gameDuration / 60)).toFixed(2),
|
||||
vision: +(stats.vision / (match.gameDuration / 60)).toFixed(2),
|
||||
gold: +(player.stats.goldEarned * 100 / teamStats.gold).toFixed(1) + '%',
|
||||
dmgChamp: +(player.stats.totalDamageDealtToChampions * 100 / teamStats.dmgChamp).toFixed(1) + '%',
|
||||
dmgObj: +(teamStats.dmgObj ? player.stats.damageDealtToObjectives * 100 / teamStats.dmgObj : 0).toFixed(1) + '%',
|
||||
dmgTaken: +(player.stats.totalDamageTaken * 100 / teamStats.dmgTaken).toFixed(1) + '%',
|
||||
}
|
||||
|
||||
stats.kp = teamStats.kills === 0 ? '0%' : +((stats.kills + stats.assists) * 100 / teamStats.kills).toFixed(1) + '%'
|
||||
} else {
|
||||
const totalKills = match.participants.reduce((prev, current) => {
|
||||
if (current.teamId !== player.teamId) {
|
||||
return prev
|
||||
}
|
||||
return prev + current.stats.kills
|
||||
}, 0)
|
||||
|
||||
stats.criticalStrike = player.stats.largestCriticalStrike
|
||||
stats.killingSpree = player.stats.largestKillingSpree
|
||||
stats.doubleKills = player.stats.doubleKills
|
||||
stats.tripleKills = player.stats.tripleKills
|
||||
stats.quadraKills = player.stats.quadraKills
|
||||
stats.pentaKills = player.stats.pentaKills
|
||||
stats.heal = player.stats.totalHeal
|
||||
stats.towers = player.stats.turretKills
|
||||
stats.longestLiving = player.stats.longestTimeSpentLiving
|
||||
stats.kp = totalKills === 0 ? 0 : +((stats.kills + stats.assists) * 100 / totalKills).toFixed(1)
|
||||
}
|
||||
|
||||
let primaryRune = null
|
||||
let secondaryRune = null
|
||||
if (player.stats.perkPrimaryStyle) {
|
||||
({ primaryRune, secondaryRune } = this.getPerksImages(player.stats.perk0, player.stats.perkSubStyle))
|
||||
}
|
||||
|
||||
const items = []
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const id = player.stats['item' + i]
|
||||
if (id === 0) {
|
||||
items.push(null)
|
||||
continue
|
||||
}
|
||||
|
||||
const item = this.items.find(i => i.id === id)
|
||||
const itemUrl = item.iconPath.split('/assets/')[1].toLowerCase()
|
||||
|
||||
items.push({
|
||||
image: `https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/${itemUrl}`,
|
||||
name: item.name,
|
||||
description: item.description,
|
||||
price: item.priceTotal
|
||||
})
|
||||
}
|
||||
|
||||
const firstSum = player.spell1Id
|
||||
const secondSum = player.spell2Id
|
||||
|
||||
return {
|
||||
name,
|
||||
summonerId: identity.player.summonerId,
|
||||
champion,
|
||||
role,
|
||||
primaryRune,
|
||||
secondaryRune,
|
||||
level,
|
||||
items,
|
||||
firstSum,
|
||||
secondSum,
|
||||
stats,
|
||||
percentStats,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the icons of the primary rune and secondary category
|
||||
* @param perk0 primary perks id
|
||||
* @param perkSubStyle secondary perks category
|
||||
*/
|
||||
getPerksImages(perk0, perkSubStyle) {
|
||||
const firstRune = this.perks.find(p => p.id === perk0)
|
||||
const firstRuneUrl = firstRune.iconPath.split('/assets/')[1].toLowerCase()
|
||||
const primaryRune = `https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/${firstRuneUrl}`
|
||||
|
||||
const secondRuneStyle = this.perkstyles.find(p => p.id === perkSubStyle)
|
||||
|
||||
const secondRuneStyleUrl = secondRuneStyle ? secondRuneStyle.iconPath.split('/assets/')[1].toLowerCase() : null
|
||||
const secondaryRune = secondRuneStyleUrl ? `https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/${secondRuneStyleUrl}` : ''
|
||||
|
||||
return { primaryRune, secondaryRune }
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the lane of the summoner according to timeline
|
||||
* @param timeline from Riot Api
|
||||
* @param gamemode of the match to check if a role is needed
|
||||
*/
|
||||
getRoleName(timeline, gamemode) {
|
||||
if (!queuesWithRole.includes(gamemode)) {
|
||||
return 'NONE'
|
||||
}
|
||||
|
||||
if (timeline.lane === 'BOTTOM' && timeline.role.includes('SUPPORT')) {
|
||||
return 'SUPPORT'
|
||||
}
|
||||
|
||||
return timeline.lane
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the 5 roles of a team based on champions
|
||||
* @param team 5 champions + smite from a team
|
||||
*/
|
||||
getTeamRoles(team) {
|
||||
const teamJunglers = team.filter(p => p.jungle && !p.support)
|
||||
const jungle = teamJunglers.length === 1 ? teamJunglers[0].champion : null
|
||||
const teamSupports = team.filter(p => p.support && !p.jungle)
|
||||
const support = teamSupports.length === 1 ? teamSupports[0].champion : null
|
||||
|
||||
return RoleIdentificationService.getRoles(this.championRoles, team.map(p => p.champion), jungle, support)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update roles for a team if Riot's ones are badly identified
|
||||
* @param {Object} team 5 players data of the team
|
||||
* @param {Array} champs 5 champions + smite from the team
|
||||
* @param {Object} playerData data of the searched player, only for basic matches
|
||||
*/
|
||||
updateTeamRoles(team, champs, playerData = null) {
|
||||
// const actualRoles = [...new Set(team.map(p => p.role))]
|
||||
// if (actualRoles.length === 5) {
|
||||
// return
|
||||
// }
|
||||
|
||||
champs = this.getTeamRoles(champs)
|
||||
for (const summoner of team) {
|
||||
summoner.role = Object.entries(champs).find(([, champion]) => summoner.champion.id === champion)[0]
|
||||
|
||||
if (playerData && summoner.champion.id === playerData.champion.id) {
|
||||
playerData.role = summoner.role
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} match from Riot Api
|
||||
* @param {Array} allyTeam 5 players of the first team
|
||||
* @param {Array} enemyTeam 5 players of the second team
|
||||
* @param {Number} allyTeamId team id of the searched player, only for basic matches
|
||||
* @param {Object} playerData data of the searched player, only for basic matches
|
||||
*/
|
||||
getMatchRoles(match, allyTeam, enemyTeam, allyTeamId = 100, playerData = null) {
|
||||
if (!this.championRoles || !queuesWithRole.includes(match.queueId)) {
|
||||
return
|
||||
}
|
||||
|
||||
let allyChamps = []
|
||||
let enemyChamps = []
|
||||
match.participants.map(p => {
|
||||
const items = [p.stats.item0, p.stats.item1, p.stats.item2, p.stats.item3, p.stats.item4, p.stats.item5]
|
||||
const playerRole = {
|
||||
champion: p.championId,
|
||||
jungle: p.spell1Id === 11 || p.spell2Id === 11,
|
||||
support: supportItems.some(suppItem => items.includes(suppItem))
|
||||
}
|
||||
p.teamId === allyTeamId ? allyChamps.push(playerRole) : enemyChamps.push(playerRole)
|
||||
})
|
||||
|
||||
this.updateTeamRoles(allyTeam, allyChamps, playerData)
|
||||
this.updateTeamRoles(enemyTeam, enemyChamps)
|
||||
|
||||
allyTeam.sort(this.sortTeamByRole)
|
||||
enemyTeam.sort(this.sortTeamByRole)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Summoner Spell Data from CDragon
|
||||
* @param id of the summonerSpell
|
||||
*/
|
||||
getSummonerSpell(id) {
|
||||
if (id === 0) return null
|
||||
const spell = this.summonerSpells.find(s => s.id === id)
|
||||
const spellName = spell.iconPath.split('/assets/')[1].toLowerCase()
|
||||
return {
|
||||
name: spell.name,
|
||||
description: spell.description,
|
||||
icon: `https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/${spellName}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MatchTransformer
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
/**
|
||||
* League of Legends queues with defined role for each summoner
|
||||
*/
|
||||
const queuesWithRole = [
|
||||
0, // Custom
|
||||
400, // Draft
|
||||
420, // Solo/Duo
|
||||
430, // Blind,
|
||||
440, // Flex
|
||||
700, // Clash
|
||||
]
|
||||
|
||||
/**
|
||||
* League of Legends seasons timestamps
|
||||
*/
|
||||
const seasons = {
|
||||
0: 9,
|
||||
1578628800000: 10
|
||||
}
|
||||
|
||||
/**
|
||||
* League of Legends all support item ids
|
||||
*/
|
||||
const supportItems = [3850, 3851, 3853, 3854, 3855, 3857, 3858, 3859, 3860, 3862, 3863, 3864]
|
||||
|
||||
module.exports = {
|
||||
queuesWithRole,
|
||||
seasons,
|
||||
supportItems,
|
||||
/**
|
||||
* Get season number for a match
|
||||
*/
|
||||
getSeasonNumber(timestamp) {
|
||||
const arrSeasons = Object.keys(seasons)
|
||||
arrSeasons.push(timestamp)
|
||||
arrSeasons.sort()
|
||||
const indexSeason = arrSeasons.indexOf(timestamp) - 1
|
||||
return seasons[arrSeasons[indexSeason]]
|
||||
},
|
||||
/**
|
||||
*
|
||||
* Sort array of Roles according to a specific order
|
||||
* @param a first role
|
||||
* @param b second role
|
||||
*/
|
||||
sortTeamByRole(a, b) {
|
||||
const sortingArr = ['TOP', 'JUNGLE', 'MIDDLE', 'BOTTOM', 'SUPPORT']
|
||||
return sortingArr.indexOf(a.role) - sortingArr.indexOf(b.role)
|
||||
},
|
||||
}
|
||||
|
|
@ -1,243 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
/** @type {import('@adonisjs/framework/src/Env')} */
|
||||
const Env = use('Env')
|
||||
|
||||
module.exports = {
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value is the name of your application and can used when you
|
||||
| need to place the application's name in a email, view or
|
||||
| other location.
|
||||
|
|
||||
*/
|
||||
|
||||
name: Env.get('APP_NAME', 'AdonisJs'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| App Key
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| App key is a randomly generated 16 or 32 characters long string required
|
||||
| to encrypt cookies, sessions and other sensitive data.
|
||||
|
|
||||
*/
|
||||
appKey: Env.getOrFail('APP_KEY'),
|
||||
|
||||
http: {
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Allow Method Spoofing
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Method spoofing allows to make requests by spoofing the http verb.
|
||||
| Which means you can make a GET request but instruct the server to
|
||||
| treat as a POST or PUT request. If you want this feature, set the
|
||||
| below value to true.
|
||||
|
|
||||
*/
|
||||
allowMethodSpoofing: true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Trust Proxy
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Trust proxy defines whether X-Forwarded-* headers should be trusted or not.
|
||||
| When your application is behind a proxy server like nginx, these values
|
||||
| are set automatically and should be trusted. Apart from setting it
|
||||
| to true or false Adonis supports handful or ways to allow proxy
|
||||
| values. Read documentation for that.
|
||||
|
|
||||
*/
|
||||
trustProxy: false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Subdomains
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Offset to be used for returning subdomains for a given request.For
|
||||
| majority of applications it will be 2, until you have nested
|
||||
| sudomains.
|
||||
| cheatsheet.adonisjs.com - offset - 2
|
||||
| virk.cheatsheet.adonisjs.com - offset - 3
|
||||
|
|
||||
*/
|
||||
subdomainOffset: 2,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| JSONP Callback
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Default jsonp callback to be used when callback query string is missing
|
||||
| in request url.
|
||||
|
|
||||
*/
|
||||
jsonpCallback: 'callback',
|
||||
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Etag
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Set etag on all HTTP response. In order to disable for selected routes,
|
||||
| you can call the `response.send` with an options object as follows.
|
||||
|
|
||||
| response.send('Hello', { ignoreEtag: true })
|
||||
|
|
||||
*/
|
||||
etag: false
|
||||
},
|
||||
|
||||
views: {
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Views
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Define whether or not to cache the compiled view. Set it to true in
|
||||
| production to optimize view loading time.
|
||||
|
|
||||
*/
|
||||
cache: Env.get('CACHE_VIEWS', true)
|
||||
},
|
||||
|
||||
static: {
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Dot Files
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Define how to treat dot files when trying to server static resources.
|
||||
| By default it is set to ignore, which will pretend that dotfiles
|
||||
| does not exists.
|
||||
|
|
||||
| Can be one of the following
|
||||
| ignore, deny, allow
|
||||
|
|
||||
*/
|
||||
dotfiles: 'ignore',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| ETag
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Enable or disable etag generation
|
||||
|
|
||||
*/
|
||||
etag: true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Extensions
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Set file extension fallbacks. When set, if a file is not found, the given
|
||||
| extensions will be added to the file name and search for. The first
|
||||
| that exists will be served. Example: ['html', 'htm'].
|
||||
|
|
||||
*/
|
||||
extensions: false
|
||||
},
|
||||
|
||||
locales: {
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Loader
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The loader to be used for fetching and updating locales. Below is the
|
||||
| list of available options.
|
||||
|
|
||||
| file, database
|
||||
|
|
||||
*/
|
||||
loader: 'file',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Locale
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Default locale to be used by Antl provider. You can always switch drivers
|
||||
| in runtime or use the official Antl middleware to detect the driver
|
||||
| based on HTTP headers/query string.
|
||||
|
|
||||
*/
|
||||
locale: 'en'
|
||||
},
|
||||
|
||||
logger: {
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Transport
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Transport to be used for logging messages. You can have multiple
|
||||
| transports using same driver.
|
||||
|
|
||||
| Available drivers are: `file` and `console`.
|
||||
|
|
||||
*/
|
||||
transport: 'console',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Console Transport
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Using `console` driver for logging. This driver writes to `stdout`
|
||||
| and `stderr`
|
||||
|
|
||||
*/
|
||||
console: {
|
||||
driver: 'console',
|
||||
name: 'adonis-app',
|
||||
level: 'info'
|
||||
},
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| File Transport
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| File transport uses file driver and writes log messages for a given
|
||||
| file inside `tmp` directory for your app.
|
||||
|
|
||||
| For a different directory, set an absolute path for the filename.
|
||||
|
|
||||
*/
|
||||
file: {
|
||||
driver: 'file',
|
||||
name: 'adonis-app',
|
||||
filename: 'adonis.log',
|
||||
level: 'info'
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Generic Cookie Options
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following cookie options are generic settings used by AdonisJs to create
|
||||
| cookies. However, some parts of the application like `sessions` can have
|
||||
| separate settings for cookies inside `config/session.js`.
|
||||
|
|
||||
*/
|
||||
cookie: {
|
||||
httpOnly: true,
|
||||
sameSite: false,
|
||||
path: '/',
|
||||
maxAge: 7200
|
||||
}
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
/** @type {import('@adonisjs/framework/src/Env')} */
|
||||
const Env = use('Env')
|
||||
|
||||
module.exports = {
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authenticator
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Authentication is a combination of serializer and scheme with extra
|
||||
| config to define on how to authenticate a user.
|
||||
|
|
||||
| Available Schemes - basic, session, jwt, api
|
||||
| Available Serializers - lucid, database
|
||||
|
|
||||
*/
|
||||
authenticator: 'jwt',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Session authenticator makes use of sessions to authenticate a user.
|
||||
| Session authentication is always persistent.
|
||||
|
|
||||
*/
|
||||
session: {
|
||||
serializer: 'lucid',
|
||||
model: 'App/Models/User',
|
||||
scheme: 'session',
|
||||
uid: 'email',
|
||||
password: 'password'
|
||||
},
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Basic Auth
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The basic auth authenticator uses basic auth header to authenticate a
|
||||
| user.
|
||||
|
|
||||
| NOTE:
|
||||
| This scheme is not persistent and users are supposed to pass
|
||||
| login credentials on each request.
|
||||
|
|
||||
*/
|
||||
basic: {
|
||||
serializer: 'lucid',
|
||||
model: 'App/Models/User',
|
||||
scheme: 'basic',
|
||||
uid: 'email',
|
||||
password: 'password'
|
||||
},
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Jwt
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The jwt authenticator works by passing a jwt token on each HTTP request
|
||||
| via HTTP `Authorization` header.
|
||||
|
|
||||
*/
|
||||
jwt: {
|
||||
serializer: 'lucid',
|
||||
model: 'App/Models/User',
|
||||
scheme: 'jwt',
|
||||
uid: 'email',
|
||||
password: 'password',
|
||||
options: {
|
||||
secret: Env.get('APP_KEY')
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Api
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The Api scheme makes use of API personal tokens to authenticate a user.
|
||||
|
|
||||
*/
|
||||
api: {
|
||||
serializer: 'lucid',
|
||||
model: 'App/Models/User',
|
||||
scheme: 'api',
|
||||
uid: 'email',
|
||||
password: 'password'
|
||||
}
|
||||
}
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| JSON Parser
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Below settings are applied when the request body contains a JSON payload.
|
||||
| If you want body parser to ignore JSON payloads, then simply set `types`
|
||||
| to an empty array.
|
||||
*/
|
||||
json: {
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| limit
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Defines the limit of JSON that can be sent by the client. If payload
|
||||
| is over 1mb it will not be processed.
|
||||
|
|
||||
*/
|
||||
limit: '1mb',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| strict
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When `strict` is set to true, body parser will only parse Arrays and
|
||||
| Object. Otherwise everything parseable by `JSON.parse` is parsed.
|
||||
|
|
||||
*/
|
||||
strict: true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| types
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Which content types are processed as JSON payloads. You are free to
|
||||
| add your own types here, but the request body should be parseable
|
||||
| by `JSON.parse` method.
|
||||
|
|
||||
*/
|
||||
types: [
|
||||
'application/json',
|
||||
'application/json-patch+json',
|
||||
'application/vnd.api+json',
|
||||
'application/csp-report'
|
||||
]
|
||||
},
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Raw Parser
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
|
|
||||
|
|
||||
*/
|
||||
raw: {
|
||||
types: [
|
||||
'text/*'
|
||||
]
|
||||
},
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Form Parser
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
|
|
||||
|
|
||||
*/
|
||||
form: {
|
||||
types: [
|
||||
'application/x-www-form-urlencoded'
|
||||
]
|
||||
},
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Files Parser
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
|
|
||||
|
|
||||
*/
|
||||
files: {
|
||||
types: [
|
||||
'multipart/form-data'
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Max Size
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Below value is the max size of all the files uploaded to the server. It
|
||||
| is validated even before files have been processed and hard exception
|
||||
| is thrown.
|
||||
|
|
||||
| Consider setting a reasonable value here, otherwise people may upload GB's
|
||||
| of files which will keep your server busy.
|
||||
|
|
||||
| Also this value is considered when `autoProcess` is set to true.
|
||||
|
|
||||
*/
|
||||
maxSize: '20mb',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Auto Process
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Whether or not to auto-process files. Since HTTP servers handle files via
|
||||
| couple of specific endpoints. It is better to set this value off and
|
||||
| manually process the files when required.
|
||||
|
|
||||
| This value can contain a boolean or an array of route patterns
|
||||
| to be autoprocessed.
|
||||
*/
|
||||
autoProcess: true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Process Manually
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The list of routes that should not process files and instead rely on
|
||||
| manual process. This list should only contain routes when autoProcess
|
||||
| is to true. Otherwise everything is processed manually.
|
||||
|
|
||||
*/
|
||||
processManually: []
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Temporary file name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Define a function, which should return a string to be used as the
|
||||
| tmp file name.
|
||||
|
|
||||
| If not defined, Bodyparser will use `uuid` as the tmp file name.
|
||||
|
|
||||
| To be defined as. If you are defining the function, then do make sure
|
||||
| to return a value from it.
|
||||
|
|
||||
| tmpFileName () {
|
||||
| return 'some-unique-value'
|
||||
| }
|
||||
|
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Origin
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Set a list of origins to be allowed. The value can be one of the following
|
||||
|
|
||||
| Boolean: true - Allow current request origin
|
||||
| Boolean: false - Disallow all
|
||||
| String - Comma separated list of allowed origins
|
||||
| Array - An array of allowed origins
|
||||
| String: * - A wildcard to allow current request origin
|
||||
| Function - Receives the current origin and should return one of the above values.
|
||||
|
|
||||
*/
|
||||
origin: (origin) => {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return true
|
||||
}
|
||||
|
||||
if (origin.includes('leaguestats.gg')) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Methods
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| HTTP methods to be allowed. The value can be one of the following
|
||||
|
|
||||
| String - Comma separated list of allowed methods
|
||||
| Array - An array of allowed methods
|
||||
|
|
||||
*/
|
||||
methods: ['GET', 'PUT', 'PATCH', 'POST', 'DELETE'],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Headers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| List of headers to be allowed via Access-Control-Request-Headers header.
|
||||
| The value can be one of the following.
|
||||
|
|
||||
| Boolean: true - Allow current request headers
|
||||
| Boolean: false - Disallow all
|
||||
| String - Comma separated list of allowed headers
|
||||
| Array - An array of allowed headers
|
||||
| String: * - A wildcard to allow current request headers
|
||||
| Function - Receives the current header and should return one of the above values.
|
||||
|
|
||||
*/
|
||||
headers: true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Expose Headers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| A list of headers to be exposed via `Access-Control-Expose-Headers`
|
||||
| header. The value can be one of the following.
|
||||
|
|
||||
| Boolean: false - Disallow all
|
||||
| String: Comma separated list of allowed headers
|
||||
| Array - An array of allowed headers
|
||||
|
|
||||
*/
|
||||
exposeHeaders: false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Credentials
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Define Access-Control-Allow-Credentials header. It should always be a
|
||||
| boolean.
|
||||
|
|
||||
*/
|
||||
credentials: false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| MaxAge
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Define Access-Control-Allow-Max-Age
|
||||
|
|
||||
*/
|
||||
maxAge: 90
|
||||
}
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
/** @type {import('@adonisjs/framework/src/Env')} */
|
||||
const Env = use('Env')
|
||||
|
||||
/** @type {import('@adonisjs/ignitor/src/Helpers')} */
|
||||
const Helpers = use('Helpers')
|
||||
|
||||
module.exports = {
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Connection
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Connection defines the default connection settings to be used while
|
||||
| interacting with SQL databases.
|
||||
|
|
||||
*/
|
||||
connection: Env.get('DB_CONNECTION', 'mongodb'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Sqlite
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Sqlite is a flat file database and can be a good choice for a development
|
||||
| environment.
|
||||
|
|
||||
| npm i --save sqlite3
|
||||
|
|
||||
*/
|
||||
sqlite: {
|
||||
client: 'sqlite3',
|
||||
connection: {
|
||||
filename: Helpers.databasePath(`${Env.get('DB_DATABASE', 'development')}.sqlite`)
|
||||
},
|
||||
useNullAsDefault: true,
|
||||
debug: Env.get('DB_DEBUG', false)
|
||||
},
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| MySQL
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here we define connection settings for MySQL database.
|
||||
|
|
||||
| npm i --save mysql
|
||||
|
|
||||
*/
|
||||
mysql: {
|
||||
client: 'mysql',
|
||||
connection: {
|
||||
host: Env.get('DB_HOST', 'localhost'),
|
||||
port: Env.get('DB_PORT', ''),
|
||||
user: Env.get('DB_USER', 'root'),
|
||||
password: Env.get('DB_PASSWORD', ''),
|
||||
database: Env.get('DB_DATABASE', 'adonis')
|
||||
},
|
||||
debug: Env.get('DB_DEBUG', false)
|
||||
},
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| PostgreSQL
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here we define connection settings for PostgreSQL database.
|
||||
|
|
||||
| npm i --save pg
|
||||
|
|
||||
*/
|
||||
pg: {
|
||||
client: 'pg',
|
||||
connection: {
|
||||
host: Env.get('DB_HOST', 'localhost'),
|
||||
port: Env.get('DB_PORT', ''),
|
||||
user: Env.get('DB_USER', 'root'),
|
||||
password: Env.get('DB_PASSWORD', ''),
|
||||
database: Env.get('DB_DATABASE', 'adonis')
|
||||
},
|
||||
debug: Env.get('DB_DEBUG', false)
|
||||
},
|
||||
|
||||
mongodb: {
|
||||
client: 'mongodb',
|
||||
connectionString: Env.get('DB_CONNECTION_STRING', ''),
|
||||
connection: {
|
||||
host: Env.get('DB_HOST', 'localhost'),
|
||||
port: Env.get('DB_PORT', 27017),
|
||||
username: Env.get('DB_USER', 'admin'),
|
||||
password: Env.get('DB_PASSWORD', ''),
|
||||
database: Env.get('DB_DATABASE', 'adonis'),
|
||||
options: {
|
||||
// replicaSet: Env.get('DB_REPLICA_SET', '')
|
||||
// ssl: Env.get('DB_SSL, '')
|
||||
// connectTimeoutMS: Env.get('DB_CONNECT_TIMEOUT_MS', 15000),
|
||||
// socketTimeoutMS: Env.get('DB_SOCKET_TIMEOUT_MS', 180000),
|
||||
// w: Env.get('DB_W, 0),
|
||||
// readPreference: Env.get('DB_READ_PREFERENCE', 'secondary'),
|
||||
// authSource: Env.get('DB_AUTH_SOURCE', ''),
|
||||
// authMechanism: Env.get('DB_AUTH_MECHANISM', ''),
|
||||
// other options
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
/** @type {import('@adonisjs/framework/src/Env')} */
|
||||
const Env = use('Env')
|
||||
|
||||
module.exports = {
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Driver to be used for hashing values. The same driver is used by the
|
||||
| auth module too.
|
||||
|
|
||||
*/
|
||||
driver: Env.get('HASH_DRIVER', 'bcrypt'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Bcrypt
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Config related to bcrypt hashing. https://www.npmjs.com/package/bcrypt
|
||||
| package is used internally.
|
||||
|
|
||||
*/
|
||||
bcrypt: {
|
||||
rounds: 10
|
||||
},
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Argon
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Config related to argon. https://www.npmjs.com/package/argon2 package is
|
||||
| used internally.
|
||||
|
|
||||
| Since argon is optional, you will have to install the dependency yourself
|
||||
|
|
||||
|============================================================================
|
||||
| npm i argon2
|
||||
|============================================================================
|
||||
|
|
||||
*/
|
||||
argon: {
|
||||
type: 1
|
||||
}
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Redis Configuaration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here we define the configuration for redis server. A single application
|
||||
| can make use of multiple redis connections using the redis provider.
|
||||
|
|
||||
*/
|
||||
|
||||
const Env = use('Env')
|
||||
|
||||
module.exports = {
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| connection
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Redis connection to be used by default.
|
||||
|
|
||||
*/
|
||||
connection: Env.get('REDIS_CONNECTION', 'local'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| local connection config
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Configuration for a named connection.
|
||||
|
|
||||
*/
|
||||
local: {
|
||||
host: Env.get('REDIS_HOST', '127.0.0.1'),
|
||||
port: Env.get('REDIS_PORT', 6379),
|
||||
password: Env.get('REDIS_PASSWORD', null),
|
||||
db: 0,
|
||||
keyPrefix: ''
|
||||
},
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| cluster config
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Below is the configuration for the redis cluster.
|
||||
|
|
||||
*/
|
||||
cluster: {
|
||||
clusters: [{
|
||||
host: '127.0.0.1',
|
||||
port: 6379,
|
||||
password: null,
|
||||
db: 0
|
||||
},
|
||||
{
|
||||
host: '127.0.0.1',
|
||||
port: 6380,
|
||||
password: null,
|
||||
db: 0
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Factory
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Factories are used to define blueprints for database tables or Lucid
|
||||
| models. Later you can use these blueprints to seed your database
|
||||
| with dummy data.
|
||||
|
|
||||
*/
|
||||
|
||||
/** @type {import('@adonisjs/lucid/src/Factory')} */
|
||||
// const Factory = use('Factory')
|
||||
|
||||
// Factory.blueprint('App/Models/User', (faker) => {
|
||||
// return {
|
||||
// username: faker.username()
|
||||
// }
|
||||
// })
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue