funcional a parte de token
This commit is contained in:
@@ -58,6 +58,7 @@ JWT_ISSUER=
|
|||||||
JWT_AUDIENCE=
|
JWT_AUDIENCE=
|
||||||
JWT_PUBLIC_KEY_PEM=
|
JWT_PUBLIC_KEY_PEM=
|
||||||
JWT_TOKEN=
|
JWT_TOKEN=
|
||||||
|
JWT_ALLOW_ANY_TOKEN=false
|
||||||
|
|
||||||
# Railway production example:
|
# Railway production example:
|
||||||
# APP_ENV=production
|
# APP_ENV=production
|
||||||
|
|||||||
@@ -141,6 +141,76 @@ endpoints:
|
|||||||
controller: null
|
controller: null
|
||||||
method: null
|
method: null
|
||||||
route: null
|
route: null
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
httpMethods:
|
||||||
|
- GET
|
||||||
|
uri: api/v1/rankings/history
|
||||||
|
metadata:
|
||||||
|
custom: []
|
||||||
|
groupName: Rankings
|
||||||
|
groupDescription: ''
|
||||||
|
subgroup: ''
|
||||||
|
subgroupDescription: ''
|
||||||
|
title: 'Histórico de ranking por query string'
|
||||||
|
description: 'Retorna a evolução de um jogo específico usando o parâmetro `id` na query string.'
|
||||||
|
authenticated: true
|
||||||
|
deprecated: false
|
||||||
|
headers:
|
||||||
|
Authorization: 'Bearer {YOUR_JWT_TOKEN}'
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
urlParameters: []
|
||||||
|
cleanUrlParameters: []
|
||||||
|
queryParameters:
|
||||||
|
id:
|
||||||
|
custom: []
|
||||||
|
name: id
|
||||||
|
description: 'O ID do jogo.'
|
||||||
|
required: true
|
||||||
|
example: 1
|
||||||
|
type: integer
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanQueryParameters:
|
||||||
|
id: 1
|
||||||
|
bodyParameters:
|
||||||
|
id:
|
||||||
|
custom: []
|
||||||
|
name: id
|
||||||
|
description: 'The <code>id</code> of an existing record in the games table.'
|
||||||
|
required: true
|
||||||
|
example: 16
|
||||||
|
type: integer
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: false
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanBodyParameters:
|
||||||
|
id: 16
|
||||||
|
fileParameters: []
|
||||||
|
responses:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 422
|
||||||
|
content: '{"message":"The selected id is invalid.","errors":{"id":["The selected id is invalid."]}}'
|
||||||
|
headers:
|
||||||
|
cache-control: 'no-cache, private'
|
||||||
|
content-type: application/json
|
||||||
|
x-ratelimit-limit: '60'
|
||||||
|
x-ratelimit-remaining: '56'
|
||||||
|
access-control-allow-origin: '*'
|
||||||
|
description: null
|
||||||
|
responseFields: []
|
||||||
|
auth:
|
||||||
|
- headers
|
||||||
|
- Authorization
|
||||||
|
- 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnYW1ldmVyc2UtYXV0aCIsImF1ZCI6InJhbmtpbmctYXBpIiwic3ViIjoiZGVtby11c2VyIiwiaWF0IjoxNzc5MTQxNTcxLCJleHAiOjE4MTA2Nzc1NzF9.aiCMcNXMs1GxvGqY5Ln87D1VJG-J2CzQ2lktqJstEzm2ogcj9M4WxI1ye2Ps3p4IHExr5IQ9KwoNn3hTgnDI5C8LiMmRa6yqdB8ZlrkZZ_eSlNxFhuAhGiCIqLsQwHony4UpxFjS1MpSuJKPyY1Z4VSulOzUExcTt0Y-G1ynq8IYnsfjqoCTP20oQGP2pb2TTbZFf4jACxctnz2oIijvgWEMAiqn72G4DJ-8nWFXZ9Yf6Of2S76MDLtWjysgFoQQYriye_Ns9ynoPjIo9igUCFyzc_AgIjh_VE0IrGW9ifkx5kOISf0b95bh7rhMuDzyvBQbFay7lIUyKMRKi_i-qw'
|
||||||
|
controller: null
|
||||||
|
method: null
|
||||||
|
route: null
|
||||||
-
|
-
|
||||||
custom: []
|
custom: []
|
||||||
httpMethods:
|
httpMethods:
|
||||||
@@ -188,7 +258,7 @@ endpoints:
|
|||||||
cache-control: 'no-cache, private'
|
cache-control: 'no-cache, private'
|
||||||
content-type: application/json
|
content-type: application/json
|
||||||
x-ratelimit-limit: '60'
|
x-ratelimit-limit: '60'
|
||||||
x-ratelimit-remaining: '56'
|
x-ratelimit-remaining: '55'
|
||||||
access-control-allow-origin: '*'
|
access-control-allow-origin: '*'
|
||||||
description: null
|
description: null
|
||||||
responseFields: []
|
responseFields: []
|
||||||
@@ -246,7 +316,53 @@ endpoints:
|
|||||||
cache-control: 'no-cache, private'
|
cache-control: 'no-cache, private'
|
||||||
content-type: application/json
|
content-type: application/json
|
||||||
x-ratelimit-limit: '60'
|
x-ratelimit-limit: '60'
|
||||||
x-ratelimit-remaining: '55'
|
x-ratelimit-remaining: '54'
|
||||||
|
access-control-allow-origin: '*'
|
||||||
|
description: null
|
||||||
|
responseFields: []
|
||||||
|
auth:
|
||||||
|
- headers
|
||||||
|
- Authorization
|
||||||
|
- 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnYW1ldmVyc2UtYXV0aCIsImF1ZCI6InJhbmtpbmctYXBpIiwic3ViIjoiZGVtby11c2VyIiwiaWF0IjoxNzc5MTQxNTcxLCJleHAiOjE4MTA2Nzc1NzF9.aiCMcNXMs1GxvGqY5Ln87D1VJG-J2CzQ2lktqJstEzm2ogcj9M4WxI1ye2Ps3p4IHExr5IQ9KwoNn3hTgnDI5C8LiMmRa6yqdB8ZlrkZZ_eSlNxFhuAhGiCIqLsQwHony4UpxFjS1MpSuJKPyY1Z4VSulOzUExcTt0Y-G1ynq8IYnsfjqoCTP20oQGP2pb2TTbZFf4jACxctnz2oIijvgWEMAiqn72G4DJ-8nWFXZ9Yf6Of2S76MDLtWjysgFoQQYriye_Ns9ynoPjIo9igUCFyzc_AgIjh_VE0IrGW9ifkx5kOISf0b95bh7rhMuDzyvBQbFay7lIUyKMRKi_i-qw'
|
||||||
|
controller: null
|
||||||
|
method: null
|
||||||
|
route: null
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
httpMethods:
|
||||||
|
- GET
|
||||||
|
uri: api/v1/games
|
||||||
|
metadata:
|
||||||
|
custom: []
|
||||||
|
groupName: Rankings
|
||||||
|
groupDescription: ''
|
||||||
|
subgroup: ''
|
||||||
|
subgroupDescription: ''
|
||||||
|
title: 'Listar jogos'
|
||||||
|
description: 'Retorna os jogos cadastrados com seus IDs para o frontend escolher qual histórico consultar.'
|
||||||
|
authenticated: true
|
||||||
|
deprecated: false
|
||||||
|
headers:
|
||||||
|
Authorization: 'Bearer {YOUR_JWT_TOKEN}'
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
urlParameters: []
|
||||||
|
cleanUrlParameters: []
|
||||||
|
queryParameters: []
|
||||||
|
cleanQueryParameters: []
|
||||||
|
bodyParameters: []
|
||||||
|
cleanBodyParameters: []
|
||||||
|
fileParameters: []
|
||||||
|
responses:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 200
|
||||||
|
content: '[{"id":11,"name":"Apex Legends","platform":"Steam","active_players":218457,"weekly_points":945,"monthly_points":8776,"yearly_points":56526,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":5,"name":"Baldur''s Gate 3","platform":"Steam","active_players":296988,"weekly_points":352,"monthly_points":3595,"yearly_points":62260,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":12,"name":"Call of Duty: Warzone","platform":"Battle.net","active_players":243114,"weekly_points":877,"monthly_points":2426,"yearly_points":36655,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":1,"name":"Counter-Strike 2","platform":"Steam","active_players":1086549,"weekly_points":729,"monthly_points":1215,"yearly_points":71182,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":14,"name":"Cyberpunk 2077","platform":"Steam","active_players":1161973,"weekly_points":874,"monthly_points":4853,"yearly_points":27988,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":8,"name":"EA SPORTS FC 24","platform":"Steam","active_players":398998,"weekly_points":872,"monthly_points":5333,"yearly_points":81468,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":2,"name":"Elden Ring","platform":"Steam","active_players":715531,"weekly_points":697,"monthly_points":7369,"yearly_points":44291,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":6,"name":"Fortnite","platform":"Epic Games","active_players":1091171,"weekly_points":611,"monthly_points":5678,"yearly_points":96832,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":7,"name":"Grand Theft Auto V","platform":"Steam","active_players":262363,"weekly_points":199,"monthly_points":2257,"yearly_points":62350,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":4,"name":"Helldivers 2","platform":"Steam","active_players":217823,"weekly_points":617,"monthly_points":5232,"yearly_points":24531,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":10,"name":"League of Legends","platform":"Riot Launcher","active_players":1166370,"weekly_points":786,"monthly_points":4506,"yearly_points":21445,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":13,"name":"Minecraft","platform":"Multiplataforma","active_players":242066,"weekly_points":184,"monthly_points":9278,"yearly_points":33053,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":9,"name":"Roblox","platform":"Multiplataforma","active_players":991415,"weekly_points":770,"monthly_points":2080,"yearly_points":22209,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":15,"name":"Stardew Valley","platform":"Steam","active_players":1117483,"weekly_points":702,"monthly_points":7545,"yearly_points":42912,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":3,"name":"Valorant","platform":"Riot Launcher","active_players":821498,"weekly_points":241,"monthly_points":1030,"yearly_points":57266,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"}]'
|
||||||
|
headers:
|
||||||
|
cache-control: 'no-cache, private'
|
||||||
|
content-type: application/json
|
||||||
|
x-ratelimit-limit: '60'
|
||||||
|
x-ratelimit-remaining: '53'
|
||||||
access-control-allow-origin: '*'
|
access-control-allow-origin: '*'
|
||||||
description: null
|
description: null
|
||||||
responseFields: []
|
responseFields: []
|
||||||
@@ -292,7 +408,7 @@ endpoints:
|
|||||||
cache-control: 'no-cache, private'
|
cache-control: 'no-cache, private'
|
||||||
content-type: application/json
|
content-type: application/json
|
||||||
x-ratelimit-limit: '60'
|
x-ratelimit-limit: '60'
|
||||||
x-ratelimit-remaining: '54'
|
x-ratelimit-remaining: '52'
|
||||||
access-control-allow-origin: '*'
|
access-control-allow-origin: '*'
|
||||||
description: null
|
description: null
|
||||||
responseFields: []
|
responseFields: []
|
||||||
|
|||||||
@@ -139,6 +139,76 @@ endpoints:
|
|||||||
controller: null
|
controller: null
|
||||||
method: null
|
method: null
|
||||||
route: null
|
route: null
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
httpMethods:
|
||||||
|
- GET
|
||||||
|
uri: api/v1/rankings/history
|
||||||
|
metadata:
|
||||||
|
custom: []
|
||||||
|
groupName: Rankings
|
||||||
|
groupDescription: ''
|
||||||
|
subgroup: ''
|
||||||
|
subgroupDescription: ''
|
||||||
|
title: 'Histórico de ranking por query string'
|
||||||
|
description: 'Retorna a evolução de um jogo específico usando o parâmetro `id` na query string.'
|
||||||
|
authenticated: true
|
||||||
|
deprecated: false
|
||||||
|
headers:
|
||||||
|
Authorization: 'Bearer {YOUR_JWT_TOKEN}'
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
urlParameters: []
|
||||||
|
cleanUrlParameters: []
|
||||||
|
queryParameters:
|
||||||
|
id:
|
||||||
|
custom: []
|
||||||
|
name: id
|
||||||
|
description: 'O ID do jogo.'
|
||||||
|
required: true
|
||||||
|
example: 1
|
||||||
|
type: integer
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanQueryParameters:
|
||||||
|
id: 1
|
||||||
|
bodyParameters:
|
||||||
|
id:
|
||||||
|
custom: []
|
||||||
|
name: id
|
||||||
|
description: 'The <code>id</code> of an existing record in the games table.'
|
||||||
|
required: true
|
||||||
|
example: 16
|
||||||
|
type: integer
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: false
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanBodyParameters:
|
||||||
|
id: 16
|
||||||
|
fileParameters: []
|
||||||
|
responses:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 422
|
||||||
|
content: '{"message":"The selected id is invalid.","errors":{"id":["The selected id is invalid."]}}'
|
||||||
|
headers:
|
||||||
|
cache-control: 'no-cache, private'
|
||||||
|
content-type: application/json
|
||||||
|
x-ratelimit-limit: '60'
|
||||||
|
x-ratelimit-remaining: '56'
|
||||||
|
access-control-allow-origin: '*'
|
||||||
|
description: null
|
||||||
|
responseFields: []
|
||||||
|
auth:
|
||||||
|
- headers
|
||||||
|
- Authorization
|
||||||
|
- 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnYW1ldmVyc2UtYXV0aCIsImF1ZCI6InJhbmtpbmctYXBpIiwic3ViIjoiZGVtby11c2VyIiwiaWF0IjoxNzc5MTQxNTcxLCJleHAiOjE4MTA2Nzc1NzF9.aiCMcNXMs1GxvGqY5Ln87D1VJG-J2CzQ2lktqJstEzm2ogcj9M4WxI1ye2Ps3p4IHExr5IQ9KwoNn3hTgnDI5C8LiMmRa6yqdB8ZlrkZZ_eSlNxFhuAhGiCIqLsQwHony4UpxFjS1MpSuJKPyY1Z4VSulOzUExcTt0Y-G1ynq8IYnsfjqoCTP20oQGP2pb2TTbZFf4jACxctnz2oIijvgWEMAiqn72G4DJ-8nWFXZ9Yf6Of2S76MDLtWjysgFoQQYriye_Ns9ynoPjIo9igUCFyzc_AgIjh_VE0IrGW9ifkx5kOISf0b95bh7rhMuDzyvBQbFay7lIUyKMRKi_i-qw'
|
||||||
|
controller: null
|
||||||
|
method: null
|
||||||
|
route: null
|
||||||
-
|
-
|
||||||
custom: []
|
custom: []
|
||||||
httpMethods:
|
httpMethods:
|
||||||
@@ -186,7 +256,7 @@ endpoints:
|
|||||||
cache-control: 'no-cache, private'
|
cache-control: 'no-cache, private'
|
||||||
content-type: application/json
|
content-type: application/json
|
||||||
x-ratelimit-limit: '60'
|
x-ratelimit-limit: '60'
|
||||||
x-ratelimit-remaining: '56'
|
x-ratelimit-remaining: '55'
|
||||||
access-control-allow-origin: '*'
|
access-control-allow-origin: '*'
|
||||||
description: null
|
description: null
|
||||||
responseFields: []
|
responseFields: []
|
||||||
@@ -244,7 +314,53 @@ endpoints:
|
|||||||
cache-control: 'no-cache, private'
|
cache-control: 'no-cache, private'
|
||||||
content-type: application/json
|
content-type: application/json
|
||||||
x-ratelimit-limit: '60'
|
x-ratelimit-limit: '60'
|
||||||
x-ratelimit-remaining: '55'
|
x-ratelimit-remaining: '54'
|
||||||
|
access-control-allow-origin: '*'
|
||||||
|
description: null
|
||||||
|
responseFields: []
|
||||||
|
auth:
|
||||||
|
- headers
|
||||||
|
- Authorization
|
||||||
|
- 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnYW1ldmVyc2UtYXV0aCIsImF1ZCI6InJhbmtpbmctYXBpIiwic3ViIjoiZGVtby11c2VyIiwiaWF0IjoxNzc5MTQxNTcxLCJleHAiOjE4MTA2Nzc1NzF9.aiCMcNXMs1GxvGqY5Ln87D1VJG-J2CzQ2lktqJstEzm2ogcj9M4WxI1ye2Ps3p4IHExr5IQ9KwoNn3hTgnDI5C8LiMmRa6yqdB8ZlrkZZ_eSlNxFhuAhGiCIqLsQwHony4UpxFjS1MpSuJKPyY1Z4VSulOzUExcTt0Y-G1ynq8IYnsfjqoCTP20oQGP2pb2TTbZFf4jACxctnz2oIijvgWEMAiqn72G4DJ-8nWFXZ9Yf6Of2S76MDLtWjysgFoQQYriye_Ns9ynoPjIo9igUCFyzc_AgIjh_VE0IrGW9ifkx5kOISf0b95bh7rhMuDzyvBQbFay7lIUyKMRKi_i-qw'
|
||||||
|
controller: null
|
||||||
|
method: null
|
||||||
|
route: null
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
httpMethods:
|
||||||
|
- GET
|
||||||
|
uri: api/v1/games
|
||||||
|
metadata:
|
||||||
|
custom: []
|
||||||
|
groupName: Rankings
|
||||||
|
groupDescription: ''
|
||||||
|
subgroup: ''
|
||||||
|
subgroupDescription: ''
|
||||||
|
title: 'Listar jogos'
|
||||||
|
description: 'Retorna os jogos cadastrados com seus IDs para o frontend escolher qual histórico consultar.'
|
||||||
|
authenticated: true
|
||||||
|
deprecated: false
|
||||||
|
headers:
|
||||||
|
Authorization: 'Bearer {YOUR_JWT_TOKEN}'
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
urlParameters: []
|
||||||
|
cleanUrlParameters: []
|
||||||
|
queryParameters: []
|
||||||
|
cleanQueryParameters: []
|
||||||
|
bodyParameters: []
|
||||||
|
cleanBodyParameters: []
|
||||||
|
fileParameters: []
|
||||||
|
responses:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 200
|
||||||
|
content: '[{"id":11,"name":"Apex Legends","platform":"Steam","active_players":218457,"weekly_points":945,"monthly_points":8776,"yearly_points":56526,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":5,"name":"Baldur''s Gate 3","platform":"Steam","active_players":296988,"weekly_points":352,"monthly_points":3595,"yearly_points":62260,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":12,"name":"Call of Duty: Warzone","platform":"Battle.net","active_players":243114,"weekly_points":877,"monthly_points":2426,"yearly_points":36655,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":1,"name":"Counter-Strike 2","platform":"Steam","active_players":1086549,"weekly_points":729,"monthly_points":1215,"yearly_points":71182,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":14,"name":"Cyberpunk 2077","platform":"Steam","active_players":1161973,"weekly_points":874,"monthly_points":4853,"yearly_points":27988,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":8,"name":"EA SPORTS FC 24","platform":"Steam","active_players":398998,"weekly_points":872,"monthly_points":5333,"yearly_points":81468,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":2,"name":"Elden Ring","platform":"Steam","active_players":715531,"weekly_points":697,"monthly_points":7369,"yearly_points":44291,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":6,"name":"Fortnite","platform":"Epic Games","active_players":1091171,"weekly_points":611,"monthly_points":5678,"yearly_points":96832,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":7,"name":"Grand Theft Auto V","platform":"Steam","active_players":262363,"weekly_points":199,"monthly_points":2257,"yearly_points":62350,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":4,"name":"Helldivers 2","platform":"Steam","active_players":217823,"weekly_points":617,"monthly_points":5232,"yearly_points":24531,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":10,"name":"League of Legends","platform":"Riot Launcher","active_players":1166370,"weekly_points":786,"monthly_points":4506,"yearly_points":21445,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":13,"name":"Minecraft","platform":"Multiplataforma","active_players":242066,"weekly_points":184,"monthly_points":9278,"yearly_points":33053,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":9,"name":"Roblox","platform":"Multiplataforma","active_players":991415,"weekly_points":770,"monthly_points":2080,"yearly_points":22209,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":15,"name":"Stardew Valley","platform":"Steam","active_players":1117483,"weekly_points":702,"monthly_points":7545,"yearly_points":42912,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"},{"id":3,"name":"Valorant","platform":"Riot Launcher","active_players":821498,"weekly_points":241,"monthly_points":1030,"yearly_points":57266,"created_at":"2026-05-18T21:57:31.000000Z","updated_at":"2026-05-18T21:57:31.000000Z"}]'
|
||||||
|
headers:
|
||||||
|
cache-control: 'no-cache, private'
|
||||||
|
content-type: application/json
|
||||||
|
x-ratelimit-limit: '60'
|
||||||
|
x-ratelimit-remaining: '53'
|
||||||
access-control-allow-origin: '*'
|
access-control-allow-origin: '*'
|
||||||
description: null
|
description: null
|
||||||
responseFields: []
|
responseFields: []
|
||||||
@@ -290,7 +406,7 @@ endpoints:
|
|||||||
cache-control: 'no-cache, private'
|
cache-control: 'no-cache, private'
|
||||||
content-type: application/json
|
content-type: application/json
|
||||||
x-ratelimit-limit: '60'
|
x-ratelimit-limit: '60'
|
||||||
x-ratelimit-remaining: '54'
|
x-ratelimit-remaining: '52'
|
||||||
access-control-allow-origin: '*'
|
access-control-allow-origin: '*'
|
||||||
description: null
|
description: null
|
||||||
responseFields: []
|
responseFields: []
|
||||||
|
|||||||
30
README.md
30
README.md
@@ -145,6 +145,7 @@ Principais variáveis usadas pelo projeto:
|
|||||||
| `JWT_ISSUER` | Emissor esperado no token JWT |
|
| `JWT_ISSUER` | Emissor esperado no token JWT |
|
||||||
| `JWT_AUDIENCE` | Audiência esperada no token JWT |
|
| `JWT_AUDIENCE` | Audiência esperada no token JWT |
|
||||||
| `JWT_PUBLIC_KEY_PEM` | Chave pública usada para validar JWT RS256 |
|
| `JWT_PUBLIC_KEY_PEM` | Chave pública usada para validar JWT RS256 |
|
||||||
|
| `JWT_ALLOW_ANY_TOKEN` | Quando `true`, aceita qualquer Bearer token. Use apenas para integração/demo |
|
||||||
| `SCRIBE_AUTH_KEY` | Token usado apenas para gerar exemplos 200 na documentação |
|
| `SCRIBE_AUTH_KEY` | Token usado apenas para gerar exemplos 200 na documentação |
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -171,6 +172,14 @@ O token precisa:
|
|||||||
|
|
||||||
Sem o header `Authorization`, a API retorna `401`.
|
Sem o header `Authorization`, a API retorna `401`.
|
||||||
|
|
||||||
|
Para ambientes de demonstração ou integração inicial com o frontend, é possível configurar:
|
||||||
|
|
||||||
|
```env
|
||||||
|
JWT_ALLOW_ANY_TOKEN=true
|
||||||
|
```
|
||||||
|
|
||||||
|
Nesse modo, a API aceita qualquer valor enviado como `Bearer token`. Para produção, mantenha `JWT_ALLOW_ANY_TOKEN=false` e use a validação JWT completa.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Executando o Projeto
|
## Executando o Projeto
|
||||||
@@ -220,8 +229,10 @@ Links auxiliares:
|
|||||||
| GET | `/api/v1/rankings/weekly` | Lista o top 10 jogos por pontuação semanal | JWT |
|
| GET | `/api/v1/rankings/weekly` | Lista o top 10 jogos por pontuação semanal | JWT |
|
||||||
| GET | `/api/v1/rankings/monthly` | Lista o top 10 jogos por pontuação mensal | JWT |
|
| GET | `/api/v1/rankings/monthly` | Lista o top 10 jogos por pontuação mensal | JWT |
|
||||||
| GET | `/api/v1/rankings/yearly` | Lista o top 10 jogos por pontuação anual | JWT |
|
| GET | `/api/v1/rankings/yearly` | Lista o top 10 jogos por pontuação anual | JWT |
|
||||||
|
| GET | `/api/v1/rankings/history?id={id}` | Retorna o histórico de pontuação usando query string | JWT |
|
||||||
| GET | `/api/v1/rankings/history/{id}` | Retorna o histórico de pontuação de um jogo | JWT |
|
| GET | `/api/v1/rankings/history/{id}` | Retorna o histórico de pontuação de um jogo | JWT |
|
||||||
| GET | `/api/v1/rankings/platforms/{platform}` | Lista jogos filtrados por plataforma | JWT |
|
| GET | `/api/v1/rankings/platforms/{platform}` | Lista jogos filtrados por plataforma | JWT |
|
||||||
|
| GET | `/api/v1/games` | Lista os jogos cadastrados com seus IDs | JWT |
|
||||||
| GET | `/api/v1/games/most-played` | Lista o top 10 jogos por jogadores ativos | JWT |
|
| GET | `/api/v1/games/most-played` | Lista o top 10 jogos por jogadores ativos | JWT |
|
||||||
|
|
||||||
Existe também a rota técnica `GET /api/test-auth`, usada apenas para validar o token JWT. Ela não faz parte da documentação pública principal.
|
Existe também a rota técnica `GET /api/test-auth`, usada apenas para validar o token JWT. Ela não faz parte da documentação pública principal.
|
||||||
@@ -257,6 +268,24 @@ Accept: application/json
|
|||||||
Authorization: Bearer SEU_TOKEN_JWT
|
Authorization: Bearer SEU_TOKEN_JWT
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Histórico de um jogo usando query string:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/v1/rankings/history?id=1 HTTP/1.1
|
||||||
|
Host: localhost:8000
|
||||||
|
Accept: application/json
|
||||||
|
Authorization: Bearer SEU_TOKEN_JWT
|
||||||
|
```
|
||||||
|
|
||||||
|
Listar jogos para o frontend escolher o ID:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/v1/games HTTP/1.1
|
||||||
|
Host: localhost:8000
|
||||||
|
Accept: application/json
|
||||||
|
Authorization: Bearer SEU_TOKEN_JWT
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Exemplo de Resposta
|
## Exemplo de Resposta
|
||||||
@@ -286,6 +315,7 @@ Authorization: Bearer SEU_TOKEN_JWT
|
|||||||
| 200 | Requisição autenticada com sucesso | Lista de jogos ou histórico |
|
| 200 | Requisição autenticada com sucesso | Lista de jogos ou histórico |
|
||||||
| 401 | Token ausente, inválido ou expirado | `{"message":"Missing Authorization header"}` |
|
| 401 | Token ausente, inválido ou expirado | `{"message":"Missing Authorization header"}` |
|
||||||
| 404 | Jogo inexistente em `/rankings/history/{id}` | Resposta padrão do Laravel para model não encontrado |
|
| 404 | Jogo inexistente em `/rankings/history/{id}` | Resposta padrão do Laravel para model não encontrado |
|
||||||
|
| 422 | ID ausente ou inválido em `/rankings/history?id={id}` | Erro de validação |
|
||||||
| 500 | Erro inesperado no servidor | Falha interna |
|
| 500 | Erro inesperado no servidor | Falha interna |
|
||||||
|
|
||||||
Observação: quando uma plataforma não possui jogos, `/api/v1/rankings/platforms/{platform}` retorna `200` com lista vazia.
|
Observação: quando uma plataforma não possui jogos, `/api/v1/rankings/platforms/{platform}` retorna `200` com lista vazia.
|
||||||
|
|||||||
@@ -10,6 +10,17 @@ use Illuminate\Http\Request;
|
|||||||
*/
|
*/
|
||||||
class GameController extends Controller
|
class GameController extends Controller
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Listar jogos
|
||||||
|
*
|
||||||
|
* Retorna os jogos cadastrados com seus IDs para o frontend escolher qual histórico consultar.
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$games = Game::orderBy('name')->get();
|
||||||
|
return response()->json($games);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Top semanal
|
* Top semanal
|
||||||
*
|
*
|
||||||
@@ -73,6 +84,23 @@ class GameController extends Controller
|
|||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Histórico de ranking por query string
|
||||||
|
*
|
||||||
|
* Retorna a evolução de um jogo específico usando o parâmetro `id` na query string.
|
||||||
|
*
|
||||||
|
* @queryParam id int required O ID do jogo. Example: 1
|
||||||
|
*/
|
||||||
|
public function historyByQuery(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'id' => ['required', 'integer', 'exists:games,id'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $this->history($request->integer('id'));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ranking por Plataforma
|
* Ranking por Plataforma
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -22,6 +22,15 @@ class JwtAuthMiddleware
|
|||||||
|
|
||||||
$token = $matches[1];
|
$token = $matches[1];
|
||||||
|
|
||||||
|
if (config('jwt.allow_any_token')) {
|
||||||
|
$request->attributes->set('auth', [
|
||||||
|
'id' => $this->subjectFromUnverifiedToken($token),
|
||||||
|
'token' => $token
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
|
||||||
[$header, $payload, $signature] = $this->decodeToken($token);
|
[$header, $payload, $signature] = $this->decodeToken($token);
|
||||||
|
|
||||||
if (($header['alg'] ?? null) !== 'RS256') {
|
if (($header['alg'] ?? null) !== 'RS256') {
|
||||||
@@ -113,4 +122,20 @@ class JwtAuthMiddleware
|
|||||||
|
|
||||||
return time() >= (int) $payload['exp'];
|
return time() >= (int) $payload['exp'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function subjectFromUnverifiedToken(string $token): string
|
||||||
|
{
|
||||||
|
$parts = explode('.', $token);
|
||||||
|
|
||||||
|
if (count($parts) !== 3) {
|
||||||
|
return 'external-consumer';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$payload = $this->base64UrlDecodeJson($parts[1]);
|
||||||
|
return (string) ($payload['sub'] ?? 'external-consumer');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return 'external-consumer';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ return [
|
|||||||
'issuer' => env('JWT_ISSUER'),
|
'issuer' => env('JWT_ISSUER'),
|
||||||
'audience' => env('JWT_AUDIENCE'),
|
'audience' => env('JWT_AUDIENCE'),
|
||||||
'public_key' => env('JWT_PUBLIC_KEY_PEM'),
|
'public_key' => env('JWT_PUBLIC_KEY_PEM'),
|
||||||
|
'allow_any_token' => env('JWT_ALLOW_ANY_TOKEN', false),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ return [
|
|||||||
// Exclude these routes even if they matched the rules above.
|
// Exclude these routes even if they matched the rules above.
|
||||||
'exclude' => [
|
'exclude' => [
|
||||||
'GET api/test-auth',
|
'GET api/test-auth',
|
||||||
|
'GET api/health',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -79,12 +79,18 @@
|
|||||||
</li>
|
</li>
|
||||||
<li class="tocify-item level-2" data-unique="rankings-GETapi-v1-rankings-yearly">
|
<li class="tocify-item level-2" data-unique="rankings-GETapi-v1-rankings-yearly">
|
||||||
<a href="#rankings-GETapi-v1-rankings-yearly">Top anual</a>
|
<a href="#rankings-GETapi-v1-rankings-yearly">Top anual</a>
|
||||||
|
</li>
|
||||||
|
<li class="tocify-item level-2" data-unique="rankings-GETapi-v1-rankings-history">
|
||||||
|
<a href="#rankings-GETapi-v1-rankings-history">Histórico de ranking por query string</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="tocify-item level-2" data-unique="rankings-GETapi-v1-rankings-history--id-">
|
<li class="tocify-item level-2" data-unique="rankings-GETapi-v1-rankings-history--id-">
|
||||||
<a href="#rankings-GETapi-v1-rankings-history--id-">Histórico de ranking</a>
|
<a href="#rankings-GETapi-v1-rankings-history--id-">Histórico de ranking</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="tocify-item level-2" data-unique="rankings-GETapi-v1-rankings-platforms--platform-">
|
<li class="tocify-item level-2" data-unique="rankings-GETapi-v1-rankings-platforms--platform-">
|
||||||
<a href="#rankings-GETapi-v1-rankings-platforms--platform-">Ranking por Plataforma</a>
|
<a href="#rankings-GETapi-v1-rankings-platforms--platform-">Ranking por Plataforma</a>
|
||||||
|
</li>
|
||||||
|
<li class="tocify-item level-2" data-unique="rankings-GETapi-v1-games">
|
||||||
|
<a href="#rankings-GETapi-v1-games">Listar jogos</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="tocify-item level-2" data-unique="rankings-GETapi-v1-games-most-played">
|
<li class="tocify-item level-2" data-unique="rankings-GETapi-v1-games-most-played">
|
||||||
<a href="#rankings-GETapi-v1-games-most-played">Jogos mais jogados</a>
|
<a href="#rankings-GETapi-v1-games-most-played">Jogos mais jogados</a>
|
||||||
@@ -100,7 +106,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ul class="toc-footer" id="last-updated">
|
<ul class="toc-footer" id="last-updated">
|
||||||
<li>Last updated: May 18, 2026</li>
|
<li>Last updated: May 19, 2026</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -884,6 +890,195 @@ You can check the Dev Tools console for debugging information.</code></pre>
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<h2 id="rankings-GETapi-v1-rankings-history">Histórico de ranking por query string</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<small class="badge badge-darkred">requires authentication</small>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>Retorna a evolução de um jogo específico usando o parâmetro <code>id</code> na query string.</p>
|
||||||
|
|
||||||
|
<span id="example-requests-GETapi-v1-rankings-history">
|
||||||
|
<blockquote>Example request:</blockquote>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="bash-example">
|
||||||
|
<pre><code class="language-bash">curl --request GET \
|
||||||
|
--get "http://127.0.0.1:8000/api/v1/rankings/history?id=1" \
|
||||||
|
--header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
|
||||||
|
--header "Content-Type: application/json" \
|
||||||
|
--header "Accept: application/json" \
|
||||||
|
--data "{
|
||||||
|
\"id\": 16
|
||||||
|
}"
|
||||||
|
</code></pre></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="javascript-example">
|
||||||
|
<pre><code class="language-javascript">const url = new URL(
|
||||||
|
"http://127.0.0.1:8000/api/v1/rankings/history"
|
||||||
|
);
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
"id": "1",
|
||||||
|
};
|
||||||
|
Object.keys(params)
|
||||||
|
.forEach(key => url.searchParams.append(key, params[key]));
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
"Authorization": "Bearer {YOUR_JWT_TOKEN}",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Accept": "application/json",
|
||||||
|
};
|
||||||
|
|
||||||
|
let body = {
|
||||||
|
"id": 16
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch(url, {
|
||||||
|
method: "GET",
|
||||||
|
headers,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
}).then(response => response.json());</code></pre></div>
|
||||||
|
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span id="example-responses-GETapi-v1-rankings-history">
|
||||||
|
<blockquote>
|
||||||
|
<p>Example response (422):</p>
|
||||||
|
</blockquote>
|
||||||
|
<details class="annotation">
|
||||||
|
<summary style="cursor: pointer;">
|
||||||
|
<small onclick="textContent = parentElement.parentElement.open ? 'Show headers' : 'Hide headers'">Show headers</small>
|
||||||
|
</summary>
|
||||||
|
<pre><code class="language-http">cache-control: no-cache, private
|
||||||
|
content-type: application/json
|
||||||
|
x-ratelimit-limit: 60
|
||||||
|
x-ratelimit-remaining: 56
|
||||||
|
access-control-allow-origin: *
|
||||||
|
</code></pre></details> <pre>
|
||||||
|
|
||||||
|
<code class="language-json" style="max-height: 300px;">{
|
||||||
|
"message": "The selected id is invalid.",
|
||||||
|
"errors": {
|
||||||
|
"id": [
|
||||||
|
"The selected id is invalid."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}</code>
|
||||||
|
</pre>
|
||||||
|
</span>
|
||||||
|
<span id="execution-results-GETapi-v1-rankings-history" hidden>
|
||||||
|
<blockquote>Received response<span
|
||||||
|
id="execution-response-status-GETapi-v1-rankings-history"></span>:
|
||||||
|
</blockquote>
|
||||||
|
<pre class="json"><code id="execution-response-content-GETapi-v1-rankings-history"
|
||||||
|
data-empty-response-text="<Empty response>" style="max-height: 400px;"></code></pre>
|
||||||
|
</span>
|
||||||
|
<span id="execution-error-GETapi-v1-rankings-history" hidden>
|
||||||
|
<blockquote>Request failed with error:</blockquote>
|
||||||
|
<pre><code id="execution-error-message-GETapi-v1-rankings-history">
|
||||||
|
|
||||||
|
Tip: Check that you're properly connected to the network.
|
||||||
|
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
|
||||||
|
You can check the Dev Tools console for debugging information.</code></pre>
|
||||||
|
</span>
|
||||||
|
<form id="form-GETapi-v1-rankings-history" data-method="GET"
|
||||||
|
data-path="api/v1/rankings/history"
|
||||||
|
data-authed="1"
|
||||||
|
data-hasfiles="0"
|
||||||
|
data-isarraybody="0"
|
||||||
|
autocomplete="off"
|
||||||
|
onsubmit="event.preventDefault(); executeTryOut('GETapi-v1-rankings-history', this);">
|
||||||
|
<h3>
|
||||||
|
Request
|
||||||
|
<button type="button"
|
||||||
|
style="background-color: #8fbcd4; padding: 5px 10px; border-radius: 5px; border-width: thin;"
|
||||||
|
id="btn-tryout-GETapi-v1-rankings-history"
|
||||||
|
onclick="tryItOut('GETapi-v1-rankings-history');">Try it out ⚡
|
||||||
|
</button>
|
||||||
|
<button type="button"
|
||||||
|
style="background-color: #c97a7e; padding: 5px 10px; border-radius: 5px; border-width: thin;"
|
||||||
|
id="btn-canceltryout-GETapi-v1-rankings-history"
|
||||||
|
onclick="cancelTryOut('GETapi-v1-rankings-history');" hidden>Cancel 🛑
|
||||||
|
</button>
|
||||||
|
<button type="submit"
|
||||||
|
style="background-color: #6ac174; padding: 5px 10px; border-radius: 5px; border-width: thin;"
|
||||||
|
id="btn-executetryout-GETapi-v1-rankings-history"
|
||||||
|
data-initial-text="Send Request 💥"
|
||||||
|
data-loading-text="⏱ Sending..."
|
||||||
|
hidden>Send Request 💥
|
||||||
|
</button>
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
<small class="badge badge-green">GET</small>
|
||||||
|
<b><code>api/v1/rankings/history</code></b>
|
||||||
|
</p>
|
||||||
|
<h4 class="fancy-heading-panel"><b>Headers</b></h4>
|
||||||
|
<div style="padding-left: 28px; clear: unset;">
|
||||||
|
<b style="line-height: 2;"><code>Authorization</code></b>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<input type="text" style="display: none"
|
||||||
|
name="Authorization" class="auth-value" data-endpoint="GETapi-v1-rankings-history"
|
||||||
|
value="Bearer {YOUR_JWT_TOKEN}"
|
||||||
|
data-component="header">
|
||||||
|
<br>
|
||||||
|
<p>Example: <code>Bearer {YOUR_JWT_TOKEN}</code></p>
|
||||||
|
</div>
|
||||||
|
<div style="padding-left: 28px; clear: unset;">
|
||||||
|
<b style="line-height: 2;"><code>Content-Type</code></b>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<input type="text" style="display: none"
|
||||||
|
name="Content-Type" data-endpoint="GETapi-v1-rankings-history"
|
||||||
|
value="application/json"
|
||||||
|
data-component="header">
|
||||||
|
<br>
|
||||||
|
<p>Example: <code>application/json</code></p>
|
||||||
|
</div>
|
||||||
|
<div style="padding-left: 28px; clear: unset;">
|
||||||
|
<b style="line-height: 2;"><code>Accept</code></b>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<input type="text" style="display: none"
|
||||||
|
name="Accept" data-endpoint="GETapi-v1-rankings-history"
|
||||||
|
value="application/json"
|
||||||
|
data-component="header">
|
||||||
|
<br>
|
||||||
|
<p>Example: <code>application/json</code></p>
|
||||||
|
</div>
|
||||||
|
<h4 class="fancy-heading-panel"><b>Query Parameters</b></h4>
|
||||||
|
<div style="padding-left: 28px; clear: unset;">
|
||||||
|
<b style="line-height: 2;"><code>id</code></b>
|
||||||
|
<small>integer</small>
|
||||||
|
|
||||||
|
|
||||||
|
<input type="number" style="display: none"
|
||||||
|
step="any" name="id" data-endpoint="GETapi-v1-rankings-history"
|
||||||
|
value="1"
|
||||||
|
data-component="query">
|
||||||
|
<br>
|
||||||
|
<p>O ID do jogo. Example: <code>1</code></p>
|
||||||
|
</div>
|
||||||
|
<h4 class="fancy-heading-panel"><b>Body Parameters</b></h4>
|
||||||
|
<div style=" padding-left: 28px; clear: unset;">
|
||||||
|
<b style="line-height: 2;"><code>id</code></b>
|
||||||
|
<small>integer</small>
|
||||||
|
|
||||||
|
|
||||||
|
<input type="number" style="display: none"
|
||||||
|
step="any" name="id" data-endpoint="GETapi-v1-rankings-history"
|
||||||
|
value="16"
|
||||||
|
data-component="body">
|
||||||
|
<br>
|
||||||
|
<p>The <code>id</code> of an existing record in the games table. Example: <code>16</code></p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
<h2 id="rankings-GETapi-v1-rankings-history--id-">Histórico de ranking</h2>
|
<h2 id="rankings-GETapi-v1-rankings-history--id-">Histórico de ranking</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
@@ -934,7 +1129,7 @@ fetch(url, {
|
|||||||
<pre><code class="language-http">cache-control: no-cache, private
|
<pre><code class="language-http">cache-control: no-cache, private
|
||||||
content-type: application/json
|
content-type: application/json
|
||||||
x-ratelimit-limit: 60
|
x-ratelimit-limit: 60
|
||||||
x-ratelimit-remaining: 56
|
x-ratelimit-remaining: 55
|
||||||
access-control-allow-origin: *
|
access-control-allow-origin: *
|
||||||
</code></pre></details> <pre>
|
</code></pre></details> <pre>
|
||||||
|
|
||||||
@@ -1105,7 +1300,7 @@ fetch(url, {
|
|||||||
<pre><code class="language-http">cache-control: no-cache, private
|
<pre><code class="language-http">cache-control: no-cache, private
|
||||||
content-type: application/json
|
content-type: application/json
|
||||||
x-ratelimit-limit: 60
|
x-ratelimit-limit: 60
|
||||||
x-ratelimit-remaining: 55
|
x-ratelimit-remaining: 54
|
||||||
access-control-allow-origin: *
|
access-control-allow-origin: *
|
||||||
</code></pre></details> <pre>
|
</code></pre></details> <pre>
|
||||||
|
|
||||||
@@ -1310,6 +1505,314 @@ You can check the Dev Tools console for debugging information.</code></pre>
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<h2 id="rankings-GETapi-v1-games">Listar jogos</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<small class="badge badge-darkred">requires authentication</small>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>Retorna os jogos cadastrados com seus IDs para o frontend escolher qual histórico consultar.</p>
|
||||||
|
|
||||||
|
<span id="example-requests-GETapi-v1-games">
|
||||||
|
<blockquote>Example request:</blockquote>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="bash-example">
|
||||||
|
<pre><code class="language-bash">curl --request GET \
|
||||||
|
--get "http://127.0.0.1:8000/api/v1/games" \
|
||||||
|
--header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
|
||||||
|
--header "Content-Type: application/json" \
|
||||||
|
--header "Accept: application/json"</code></pre></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="javascript-example">
|
||||||
|
<pre><code class="language-javascript">const url = new URL(
|
||||||
|
"http://127.0.0.1:8000/api/v1/games"
|
||||||
|
);
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
"Authorization": "Bearer {YOUR_JWT_TOKEN}",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Accept": "application/json",
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
fetch(url, {
|
||||||
|
method: "GET",
|
||||||
|
headers,
|
||||||
|
}).then(response => response.json());</code></pre></div>
|
||||||
|
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span id="example-responses-GETapi-v1-games">
|
||||||
|
<blockquote>
|
||||||
|
<p>Example response (200):</p>
|
||||||
|
</blockquote>
|
||||||
|
<details class="annotation">
|
||||||
|
<summary style="cursor: pointer;">
|
||||||
|
<small onclick="textContent = parentElement.parentElement.open ? 'Show headers' : 'Hide headers'">Show headers</small>
|
||||||
|
</summary>
|
||||||
|
<pre><code class="language-http">cache-control: no-cache, private
|
||||||
|
content-type: application/json
|
||||||
|
x-ratelimit-limit: 60
|
||||||
|
x-ratelimit-remaining: 53
|
||||||
|
access-control-allow-origin: *
|
||||||
|
</code></pre></details> <pre>
|
||||||
|
|
||||||
|
<code class="language-json" style="max-height: 300px;">[
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"name": "Apex Legends",
|
||||||
|
"platform": "Steam",
|
||||||
|
"active_players": 218457,
|
||||||
|
"weekly_points": 945,
|
||||||
|
"monthly_points": 8776,
|
||||||
|
"yearly_points": 56526,
|
||||||
|
"created_at": "2026-05-18T21:57:31.000000Z",
|
||||||
|
"updated_at": "2026-05-18T21:57:31.000000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"name": "Baldur's Gate 3",
|
||||||
|
"platform": "Steam",
|
||||||
|
"active_players": 296988,
|
||||||
|
"weekly_points": 352,
|
||||||
|
"monthly_points": 3595,
|
||||||
|
"yearly_points": 62260,
|
||||||
|
"created_at": "2026-05-18T21:57:31.000000Z",
|
||||||
|
"updated_at": "2026-05-18T21:57:31.000000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"name": "Call of Duty: Warzone",
|
||||||
|
"platform": "Battle.net",
|
||||||
|
"active_players": 243114,
|
||||||
|
"weekly_points": 877,
|
||||||
|
"monthly_points": 2426,
|
||||||
|
"yearly_points": 36655,
|
||||||
|
"created_at": "2026-05-18T21:57:31.000000Z",
|
||||||
|
"updated_at": "2026-05-18T21:57:31.000000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Counter-Strike 2",
|
||||||
|
"platform": "Steam",
|
||||||
|
"active_players": 1086549,
|
||||||
|
"weekly_points": 729,
|
||||||
|
"monthly_points": 1215,
|
||||||
|
"yearly_points": 71182,
|
||||||
|
"created_at": "2026-05-18T21:57:31.000000Z",
|
||||||
|
"updated_at": "2026-05-18T21:57:31.000000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 14,
|
||||||
|
"name": "Cyberpunk 2077",
|
||||||
|
"platform": "Steam",
|
||||||
|
"active_players": 1161973,
|
||||||
|
"weekly_points": 874,
|
||||||
|
"monthly_points": 4853,
|
||||||
|
"yearly_points": 27988,
|
||||||
|
"created_at": "2026-05-18T21:57:31.000000Z",
|
||||||
|
"updated_at": "2026-05-18T21:57:31.000000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"name": "EA SPORTS FC 24",
|
||||||
|
"platform": "Steam",
|
||||||
|
"active_players": 398998,
|
||||||
|
"weekly_points": 872,
|
||||||
|
"monthly_points": 5333,
|
||||||
|
"yearly_points": 81468,
|
||||||
|
"created_at": "2026-05-18T21:57:31.000000Z",
|
||||||
|
"updated_at": "2026-05-18T21:57:31.000000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "Elden Ring",
|
||||||
|
"platform": "Steam",
|
||||||
|
"active_players": 715531,
|
||||||
|
"weekly_points": 697,
|
||||||
|
"monthly_points": 7369,
|
||||||
|
"yearly_points": 44291,
|
||||||
|
"created_at": "2026-05-18T21:57:31.000000Z",
|
||||||
|
"updated_at": "2026-05-18T21:57:31.000000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"name": "Fortnite",
|
||||||
|
"platform": "Epic Games",
|
||||||
|
"active_players": 1091171,
|
||||||
|
"weekly_points": 611,
|
||||||
|
"monthly_points": 5678,
|
||||||
|
"yearly_points": 96832,
|
||||||
|
"created_at": "2026-05-18T21:57:31.000000Z",
|
||||||
|
"updated_at": "2026-05-18T21:57:31.000000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"name": "Grand Theft Auto V",
|
||||||
|
"platform": "Steam",
|
||||||
|
"active_players": 262363,
|
||||||
|
"weekly_points": 199,
|
||||||
|
"monthly_points": 2257,
|
||||||
|
"yearly_points": 62350,
|
||||||
|
"created_at": "2026-05-18T21:57:31.000000Z",
|
||||||
|
"updated_at": "2026-05-18T21:57:31.000000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"name": "Helldivers 2",
|
||||||
|
"platform": "Steam",
|
||||||
|
"active_players": 217823,
|
||||||
|
"weekly_points": 617,
|
||||||
|
"monthly_points": 5232,
|
||||||
|
"yearly_points": 24531,
|
||||||
|
"created_at": "2026-05-18T21:57:31.000000Z",
|
||||||
|
"updated_at": "2026-05-18T21:57:31.000000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"name": "League of Legends",
|
||||||
|
"platform": "Riot Launcher",
|
||||||
|
"active_players": 1166370,
|
||||||
|
"weekly_points": 786,
|
||||||
|
"monthly_points": 4506,
|
||||||
|
"yearly_points": 21445,
|
||||||
|
"created_at": "2026-05-18T21:57:31.000000Z",
|
||||||
|
"updated_at": "2026-05-18T21:57:31.000000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"name": "Minecraft",
|
||||||
|
"platform": "Multiplataforma",
|
||||||
|
"active_players": 242066,
|
||||||
|
"weekly_points": 184,
|
||||||
|
"monthly_points": 9278,
|
||||||
|
"yearly_points": 33053,
|
||||||
|
"created_at": "2026-05-18T21:57:31.000000Z",
|
||||||
|
"updated_at": "2026-05-18T21:57:31.000000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"name": "Roblox",
|
||||||
|
"platform": "Multiplataforma",
|
||||||
|
"active_players": 991415,
|
||||||
|
"weekly_points": 770,
|
||||||
|
"monthly_points": 2080,
|
||||||
|
"yearly_points": 22209,
|
||||||
|
"created_at": "2026-05-18T21:57:31.000000Z",
|
||||||
|
"updated_at": "2026-05-18T21:57:31.000000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 15,
|
||||||
|
"name": "Stardew Valley",
|
||||||
|
"platform": "Steam",
|
||||||
|
"active_players": 1117483,
|
||||||
|
"weekly_points": 702,
|
||||||
|
"monthly_points": 7545,
|
||||||
|
"yearly_points": 42912,
|
||||||
|
"created_at": "2026-05-18T21:57:31.000000Z",
|
||||||
|
"updated_at": "2026-05-18T21:57:31.000000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"name": "Valorant",
|
||||||
|
"platform": "Riot Launcher",
|
||||||
|
"active_players": 821498,
|
||||||
|
"weekly_points": 241,
|
||||||
|
"monthly_points": 1030,
|
||||||
|
"yearly_points": 57266,
|
||||||
|
"created_at": "2026-05-18T21:57:31.000000Z",
|
||||||
|
"updated_at": "2026-05-18T21:57:31.000000Z"
|
||||||
|
}
|
||||||
|
]</code>
|
||||||
|
</pre>
|
||||||
|
</span>
|
||||||
|
<span id="execution-results-GETapi-v1-games" hidden>
|
||||||
|
<blockquote>Received response<span
|
||||||
|
id="execution-response-status-GETapi-v1-games"></span>:
|
||||||
|
</blockquote>
|
||||||
|
<pre class="json"><code id="execution-response-content-GETapi-v1-games"
|
||||||
|
data-empty-response-text="<Empty response>" style="max-height: 400px;"></code></pre>
|
||||||
|
</span>
|
||||||
|
<span id="execution-error-GETapi-v1-games" hidden>
|
||||||
|
<blockquote>Request failed with error:</blockquote>
|
||||||
|
<pre><code id="execution-error-message-GETapi-v1-games">
|
||||||
|
|
||||||
|
Tip: Check that you're properly connected to the network.
|
||||||
|
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
|
||||||
|
You can check the Dev Tools console for debugging information.</code></pre>
|
||||||
|
</span>
|
||||||
|
<form id="form-GETapi-v1-games" data-method="GET"
|
||||||
|
data-path="api/v1/games"
|
||||||
|
data-authed="1"
|
||||||
|
data-hasfiles="0"
|
||||||
|
data-isarraybody="0"
|
||||||
|
autocomplete="off"
|
||||||
|
onsubmit="event.preventDefault(); executeTryOut('GETapi-v1-games', this);">
|
||||||
|
<h3>
|
||||||
|
Request
|
||||||
|
<button type="button"
|
||||||
|
style="background-color: #8fbcd4; padding: 5px 10px; border-radius: 5px; border-width: thin;"
|
||||||
|
id="btn-tryout-GETapi-v1-games"
|
||||||
|
onclick="tryItOut('GETapi-v1-games');">Try it out ⚡
|
||||||
|
</button>
|
||||||
|
<button type="button"
|
||||||
|
style="background-color: #c97a7e; padding: 5px 10px; border-radius: 5px; border-width: thin;"
|
||||||
|
id="btn-canceltryout-GETapi-v1-games"
|
||||||
|
onclick="cancelTryOut('GETapi-v1-games');" hidden>Cancel 🛑
|
||||||
|
</button>
|
||||||
|
<button type="submit"
|
||||||
|
style="background-color: #6ac174; padding: 5px 10px; border-radius: 5px; border-width: thin;"
|
||||||
|
id="btn-executetryout-GETapi-v1-games"
|
||||||
|
data-initial-text="Send Request 💥"
|
||||||
|
data-loading-text="⏱ Sending..."
|
||||||
|
hidden>Send Request 💥
|
||||||
|
</button>
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
<small class="badge badge-green">GET</small>
|
||||||
|
<b><code>api/v1/games</code></b>
|
||||||
|
</p>
|
||||||
|
<h4 class="fancy-heading-panel"><b>Headers</b></h4>
|
||||||
|
<div style="padding-left: 28px; clear: unset;">
|
||||||
|
<b style="line-height: 2;"><code>Authorization</code></b>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<input type="text" style="display: none"
|
||||||
|
name="Authorization" class="auth-value" data-endpoint="GETapi-v1-games"
|
||||||
|
value="Bearer {YOUR_JWT_TOKEN}"
|
||||||
|
data-component="header">
|
||||||
|
<br>
|
||||||
|
<p>Example: <code>Bearer {YOUR_JWT_TOKEN}</code></p>
|
||||||
|
</div>
|
||||||
|
<div style="padding-left: 28px; clear: unset;">
|
||||||
|
<b style="line-height: 2;"><code>Content-Type</code></b>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<input type="text" style="display: none"
|
||||||
|
name="Content-Type" data-endpoint="GETapi-v1-games"
|
||||||
|
value="application/json"
|
||||||
|
data-component="header">
|
||||||
|
<br>
|
||||||
|
<p>Example: <code>application/json</code></p>
|
||||||
|
</div>
|
||||||
|
<div style="padding-left: 28px; clear: unset;">
|
||||||
|
<b style="line-height: 2;"><code>Accept</code></b>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<input type="text" style="display: none"
|
||||||
|
name="Accept" data-endpoint="GETapi-v1-games"
|
||||||
|
value="application/json"
|
||||||
|
data-component="header">
|
||||||
|
<br>
|
||||||
|
<p>Example: <code>application/json</code></p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
<h2 id="rankings-GETapi-v1-games-most-played">Jogos mais jogados</h2>
|
<h2 id="rankings-GETapi-v1-games-most-played">Jogos mais jogados</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
@@ -1360,7 +1863,7 @@ fetch(url, {
|
|||||||
<pre><code class="language-http">cache-control: no-cache, private
|
<pre><code class="language-http">cache-control: no-cache, private
|
||||||
content-type: application/json
|
content-type: application/json
|
||||||
x-ratelimit-limit: 60
|
x-ratelimit-limit: 60
|
||||||
x-ratelimit-remaining: 54
|
x-ratelimit-remaining: 52
|
||||||
access-control-allow-origin: *
|
access-control-allow-origin: *
|
||||||
</code></pre></details> <pre>
|
</code></pre></details> <pre>
|
||||||
|
|
||||||
|
|||||||
@@ -16,10 +16,12 @@ Route::prefix('v1')->middleware(['jwt.auth'])->group(function () {
|
|||||||
Route::get('/rankings/weekly', [GameController::class, 'weeklyRanking']);
|
Route::get('/rankings/weekly', [GameController::class, 'weeklyRanking']);
|
||||||
Route::get('/rankings/monthly', [GameController::class, 'monthlyRanking']);
|
Route::get('/rankings/monthly', [GameController::class, 'monthlyRanking']);
|
||||||
Route::get('/rankings/yearly', [GameController::class, 'yearlyRanking']);
|
Route::get('/rankings/yearly', [GameController::class, 'yearlyRanking']);
|
||||||
|
Route::get('/rankings/history', [GameController::class, 'historyByQuery']);
|
||||||
Route::get('/rankings/history/{id}', [GameController::class, 'history']);
|
Route::get('/rankings/history/{id}', [GameController::class, 'history']);
|
||||||
Route::get('/rankings/platforms/{platform}', [GameController::class, 'platformRanking']);
|
Route::get('/rankings/platforms/{platform}', [GameController::class, 'platformRanking']);
|
||||||
|
|
||||||
// Jogos
|
// Jogos
|
||||||
|
Route::get('/games', [GameController::class, 'index']);
|
||||||
Route::get('/games/most-played', [GameController::class, 'mostPlayed']);
|
Route::get('/games/most-played', [GameController::class, 'mostPlayed']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -20,11 +20,14 @@ class DocumentationRoutesTest extends TestCase
|
|||||||
'/api/v1/rankings/weekly',
|
'/api/v1/rankings/weekly',
|
||||||
'/api/v1/rankings/monthly',
|
'/api/v1/rankings/monthly',
|
||||||
'/api/v1/rankings/yearly',
|
'/api/v1/rankings/yearly',
|
||||||
|
'/api/v1/rankings/history',
|
||||||
'/api/v1/rankings/history/{id}',
|
'/api/v1/rankings/history/{id}',
|
||||||
'/api/v1/rankings/platforms/{platform}',
|
'/api/v1/rankings/platforms/{platform}',
|
||||||
|
'/api/v1/games',
|
||||||
'/api/v1/games/most-played',
|
'/api/v1/games/most-played',
|
||||||
], $paths);
|
], $paths);
|
||||||
|
|
||||||
$this->assertNotContains('/api/test-auth', $paths);
|
$this->assertNotContains('/api/test-auth', $paths);
|
||||||
|
$this->assertNotContains('/api/health', $paths);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,15 @@ class GameRankingApiTest extends TestCase
|
|||||||
->assertJsonPath('9.name', 'Game 3');
|
->assertJsonPath('9.name', 'Game 3');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_games_route_returns_ids_for_frontend_selection(): void
|
||||||
|
{
|
||||||
|
$this->getJsonWithJwt('/api/v1/games')
|
||||||
|
->assertOk()
|
||||||
|
->assertJsonCount(12)
|
||||||
|
->assertJsonPath('0.id', 1)
|
||||||
|
->assertJsonPath('0.name', 'Game 1');
|
||||||
|
}
|
||||||
|
|
||||||
public function test_history_returns_score_evolution_for_a_game(): void
|
public function test_history_returns_score_evolution_for_a_game(): void
|
||||||
{
|
{
|
||||||
$this->getJsonWithJwt('/api/v1/rankings/history/5')
|
$this->getJsonWithJwt('/api/v1/rankings/history/5')
|
||||||
@@ -67,6 +76,14 @@ class GameRankingApiTest extends TestCase
|
|||||||
->assertJsonPath('history.2.points', 50000);
|
->assertJsonPath('history.2.points', 50000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_history_can_be_requested_with_query_string_id(): void
|
||||||
|
{
|
||||||
|
$this->getJsonWithJwt('/api/v1/rankings/history?id=6')
|
||||||
|
->assertOk()
|
||||||
|
->assertJsonPath('game', 'Game 6')
|
||||||
|
->assertJsonPath('history.0.points', 600);
|
||||||
|
}
|
||||||
|
|
||||||
public function test_platform_ranking_returns_only_requested_platform_ordered_by_active_players(): void
|
public function test_platform_ranking_returns_only_requested_platform_ordered_by_active_players(): void
|
||||||
{
|
{
|
||||||
$this->getJsonWithJwt('/api/v1/rankings/platforms/Steam')
|
$this->getJsonWithJwt('/api/v1/rankings/platforms/Steam')
|
||||||
@@ -84,6 +101,16 @@ class GameRankingApiTest extends TestCase
|
|||||||
->assertJson(['userId' => 'consumer-project']);
|
->assertJson(['userId' => 'consumer-project']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_can_accept_any_bearer_token_when_enabled_for_demo_integration(): void
|
||||||
|
{
|
||||||
|
config(['jwt.allow_any_token' => true]);
|
||||||
|
|
||||||
|
$this->withHeader('Authorization', 'Bearer token-do-front')
|
||||||
|
->getJson('/api/v1/rankings/weekly')
|
||||||
|
->assertOk()
|
||||||
|
->assertJsonCount(10);
|
||||||
|
}
|
||||||
|
|
||||||
private function getJsonWithJwt(string $uri)
|
private function getJsonWithJwt(string $uri)
|
||||||
{
|
{
|
||||||
return $this->withHeader('Authorization', 'Bearer '.$this->jwt)
|
return $this->withHeader('Authorization', 'Bearer '.$this->jwt)
|
||||||
|
|||||||
Reference in New Issue
Block a user