From 88e3c6b77349227071bf803deb65b493e39a057a Mon Sep 17 00:00:00 2001 From: tudor <7089284+tudddorrr@users.noreply.github.com> Date: Sat, 6 Dec 2025 15:12:36 +0000 Subject: [PATCH] enrich docs with scopes --- src/docs/api-docs.ts | 7 +++- src/docs/event-api.docs.ts | 4 ++- src/docs/game-channel-api.docs.ts | 16 ++++++++- src/docs/game-config-api.docs.ts | 4 ++- src/docs/game-feedback-api.docs.ts | 5 ++- src/docs/game-save-api.docs.ts | 7 +++- src/docs/game-stat-api.docs.ts | 10 +++++- src/docs/index.ts | 12 +++++++ src/docs/leaderboard-api.docs.ts | 5 ++- src/docs/player-api.docs.ts | 9 ++++- src/docs/player-auth-api.docs.ts | 13 ++++++- src/docs/player-group-api.docs.ts | 4 ++- src/docs/player-presence-api.docs.ts | 5 ++- src/docs/socket-tickets-api.docs.ts | 2 +- src/services/api/event-api.service.ts | 2 +- src/services/api/game-channel-api.service.ts | 2 +- src/services/api/game-config-api.service.ts | 2 +- src/services/api/game-feedback-api.service.ts | 2 +- src/services/api/game-save-api.service.ts | 2 +- src/services/api/game-stat-api.service.ts | 2 +- src/services/api/leaderboard-api.service.ts | 2 +- src/services/api/player-api.service.ts | 2 +- src/services/api/player-auth-api.service.ts | 2 +- src/services/api/player-group-api.service.ts | 2 +- .../api/player-presence-api.service.ts | 2 +- src/services/api/socket-ticket-api.service.ts | 2 +- src/services/public/documentation.service.ts | 34 ++++++++++++++++++- 27 files changed, 135 insertions(+), 26 deletions(-) create mode 100644 src/docs/index.ts diff --git a/src/docs/api-docs.ts b/src/docs/api-docs.ts index b210ef7c..09b08450 100644 --- a/src/docs/api-docs.ts +++ b/src/docs/api-docs.ts @@ -1,7 +1,12 @@ import { RouteDocs } from 'koa-clay' +import { APIKeyScope } from '../entities/api-key' + +type APIRouteDocs = RouteDocs & { + scopes?: APIKeyScope[] +} type APIDocs = { - [key in keyof T]?: RouteDocs + [key in keyof T]?: APIRouteDocs } export default APIDocs diff --git a/src/docs/event-api.docs.ts b/src/docs/event-api.docs.ts index 4dfc3ebc..0280ba41 100644 --- a/src/docs/event-api.docs.ts +++ b/src/docs/event-api.docs.ts @@ -1,9 +1,11 @@ import EventAPIService from '../services/api/event-api.service' import APIDocs from './api-docs' +import { APIKeyScope } from '../entities/api-key' const EventAPIDocs: APIDocs = { post: { description: 'Track events', + scopes: [APIKeyScope.WRITE_EVENTS], params: { headers: { 'x-talo-alias': 'The ID of the player\'s alias' @@ -89,4 +91,4 @@ const EventAPIDocs: APIDocs = { } } -export default EventAPIDocs +export { EventAPIDocs } diff --git a/src/docs/game-channel-api.docs.ts b/src/docs/game-channel-api.docs.ts index 542d17fb..96cb769e 100644 --- a/src/docs/game-channel-api.docs.ts +++ b/src/docs/game-channel-api.docs.ts @@ -1,9 +1,11 @@ import GameChannelAPIService from '../services/api/game-channel-api.service' import APIDocs from './api-docs' +import { APIKeyScope } from '../entities/api-key' const GameChannelAPIDocs: APIDocs = { index: { description: 'List game channels', + scopes: [APIKeyScope.READ_GAME_CHANNELS], params: { query: { page: 'The current pagination index (starting at 0)' @@ -87,6 +89,7 @@ const GameChannelAPIDocs: APIDocs = { }, get: { description: 'Find a game channel', + scopes: [APIKeyScope.READ_GAME_CHANNELS], params: { route: { id: 'The ID of the channel' @@ -120,6 +123,7 @@ const GameChannelAPIDocs: APIDocs = { }, subscriptions: { description: 'List game channels that the player is subscribed to', + scopes: [APIKeyScope.READ_GAME_CHANNELS], params: { headers: { 'x-talo-alias': 'The ID of the player\'s alias' @@ -188,6 +192,7 @@ const GameChannelAPIDocs: APIDocs = { }, post: { description: 'Create a game channel', + scopes: [APIKeyScope.WRITE_GAME_CHANNELS], params: { headers: { 'x-talo-alias': 'The ID of the player\'s alias' @@ -251,6 +256,7 @@ const GameChannelAPIDocs: APIDocs = { }, join: { description: 'Join a game channel', + scopes: [APIKeyScope.WRITE_GAME_CHANNELS], params: { headers: { 'x-talo-alias': 'The ID of the player\'s alias' @@ -283,6 +289,7 @@ const GameChannelAPIDocs: APIDocs = { }, leave: { description: 'Leave a game channel', + scopes: [APIKeyScope.WRITE_GAME_CHANNELS], params: { headers: { 'x-talo-alias': 'The ID of the player\'s alias' @@ -294,6 +301,7 @@ const GameChannelAPIDocs: APIDocs = { }, put: { description: 'Update a game channel', + scopes: [APIKeyScope.WRITE_GAME_CHANNELS], params: { headers: { 'x-talo-alias': 'The ID of the player\'s alias' @@ -360,6 +368,7 @@ const GameChannelAPIDocs: APIDocs = { }, delete: { description: 'Delete a game channel', + scopes: [APIKeyScope.WRITE_GAME_CHANNELS], params: { headers: { 'x-talo-alias': 'The ID of the player\'s alias' @@ -371,6 +380,7 @@ const GameChannelAPIDocs: APIDocs = { }, invite: { description: 'Invite another player to a game channel', + scopes: [APIKeyScope.WRITE_GAME_CHANNELS], params: { headers: { 'x-talo-alias': 'The ID of the player\'s alias' @@ -385,6 +395,7 @@ const GameChannelAPIDocs: APIDocs = { }, members: { description: 'Get the members of a game channel', + scopes: [APIKeyScope.READ_GAME_CHANNELS], params: { headers: { 'x-talo-alias': 'The ID of the player\'s alias' @@ -433,6 +444,7 @@ const GameChannelAPIDocs: APIDocs = { }, getStorage: { description: 'Get a single storage property from a game channel', + scopes: [APIKeyScope.READ_GAME_CHANNELS], params: { headers: { 'x-talo-alias': 'The ID of the player\'s alias' @@ -498,6 +510,7 @@ const GameChannelAPIDocs: APIDocs = { }, listStorage: { description: 'Get multiple storage properties from a game channel', + scopes: [APIKeyScope.READ_GAME_CHANNELS], params: { headers: { 'x-talo-alias': 'The ID of the player\'s alias' @@ -606,6 +619,7 @@ const GameChannelAPIDocs: APIDocs = { }, putStorage: { description: 'Create or update storage properties in a game channel', + scopes: [APIKeyScope.WRITE_GAME_CHANNELS], params: { headers: { 'x-talo-alias': 'The ID of the player\'s alias' @@ -832,4 +846,4 @@ const GameChannelAPIDocs: APIDocs = { } } -export default GameChannelAPIDocs +export { GameChannelAPIDocs } diff --git a/src/docs/game-config-api.docs.ts b/src/docs/game-config-api.docs.ts index 885755ca..a094337e 100644 --- a/src/docs/game-config-api.docs.ts +++ b/src/docs/game-config-api.docs.ts @@ -1,9 +1,11 @@ import GameConfigAPIService from '../services/api/game-config-api.service' import APIDocs from './api-docs' +import { APIKeyScope } from '../entities/api-key' const GameConfigAPIDocs: APIDocs = { index: { description: 'Get the live config for the game', + scopes: [APIKeyScope.READ_GAME_CONFIG], samples: [ { title: 'Sample response', @@ -19,4 +21,4 @@ const GameConfigAPIDocs: APIDocs = { } } -export default GameConfigAPIDocs +export { GameConfigAPIDocs } diff --git a/src/docs/game-feedback-api.docs.ts b/src/docs/game-feedback-api.docs.ts index 2e4cee26..8e006817 100644 --- a/src/docs/game-feedback-api.docs.ts +++ b/src/docs/game-feedback-api.docs.ts @@ -1,9 +1,11 @@ import GameFeedbackAPIService from '../services/api/game-feedback-api.service' import APIDocs from './api-docs' +import { APIKeyScope } from '../entities/api-key' const GameFeedbackAPIDocs: APIDocs = { indexCategories: { description: 'Get available feedback categories', + scopes: [APIKeyScope.READ_GAME_FEEDBACK], samples: [ { title: 'Sample response', @@ -23,6 +25,7 @@ const GameFeedbackAPIDocs: APIDocs = { }, post: { description: 'Create a player feedback entry', + scopes: [APIKeyScope.WRITE_GAME_FEEDBACK], params: { headers: { 'x-talo-alias': 'The ID of the player\'s alias' @@ -102,4 +105,4 @@ const GameFeedbackAPIDocs: APIDocs = { } } -export default GameFeedbackAPIDocs +export { GameFeedbackAPIDocs } diff --git a/src/docs/game-save-api.docs.ts b/src/docs/game-save-api.docs.ts index 1c18cb37..4c420b44 100644 --- a/src/docs/game-save-api.docs.ts +++ b/src/docs/game-save-api.docs.ts @@ -1,5 +1,6 @@ import GameSaveAPIService from '../services/api/game-save-api.service' import APIDocs from './api-docs' +import { APIKeyScope } from '../entities/api-key' const sampleSave = { id: 143, @@ -51,6 +52,7 @@ const sampleSave = { const GameSaveAPIDocs: APIDocs = { index: { description: 'Get a player\'s saves', + scopes: [APIKeyScope.READ_GAME_SAVES], params: { headers: { 'x-talo-player': 'The ID of the player' @@ -67,6 +69,7 @@ const GameSaveAPIDocs: APIDocs = { }, post: { description: 'Create a save', + scopes: [APIKeyScope.WRITE_GAME_SAVES], params: { headers: { 'x-talo-player': 'The ID of the player' @@ -94,6 +97,7 @@ const GameSaveAPIDocs: APIDocs = { }, patch: { description: 'Update a save', + scopes: [APIKeyScope.WRITE_GAME_SAVES], params: { headers: { 'x-talo-player': 'The ID of the player' @@ -124,6 +128,7 @@ const GameSaveAPIDocs: APIDocs = { }, delete: { description: 'Delete a save', + scopes: [APIKeyScope.WRITE_GAME_SAVES], params: { headers: { 'x-talo-player': 'The ID of the player' @@ -135,4 +140,4 @@ const GameSaveAPIDocs: APIDocs = { } } -export default GameSaveAPIDocs +export { GameSaveAPIDocs } diff --git a/src/docs/game-stat-api.docs.ts b/src/docs/game-stat-api.docs.ts index 7811262f..64f338d6 100644 --- a/src/docs/game-stat-api.docs.ts +++ b/src/docs/game-stat-api.docs.ts @@ -1,9 +1,11 @@ import GameStatAPIService from '../services/api/game-stat-api.service' import APIDocs from './api-docs' +import { APIKeyScope } from '../entities/api-key' const GameStatAPIDocs: APIDocs = { index: { description: 'Get all game stats', + scopes: [APIKeyScope.READ_GAME_STATS], samples: [ { title: 'Sample response', @@ -44,6 +46,7 @@ const GameStatAPIDocs: APIDocs = { }, get: { description: 'Get an individual game stat', + scopes: [APIKeyScope.READ_GAME_STATS], params: { route: { internalName: 'The internal name of the stat' @@ -73,6 +76,7 @@ const GameStatAPIDocs: APIDocs = { }, getPlayerStat: { description: 'Get the current value of a player\'s stat', + scopes: [APIKeyScope.READ_GAME_STATS], params: { route: { internalName: 'The internal name of the stat' @@ -108,6 +112,7 @@ const GameStatAPIDocs: APIDocs = { }, put: { description: 'Update a stat value', + scopes: [APIKeyScope.WRITE_GAME_STATS], params: { headers: { 'x-talo-alias': 'The ID of the player\'s alias' @@ -155,6 +160,7 @@ const GameStatAPIDocs: APIDocs = { }, history: { description: 'Get a history of changes to a player stat', + scopes: [APIKeyScope.READ_GAME_STATS], params: { headers: { 'x-talo-player': 'The ID of the player' @@ -263,6 +269,7 @@ const GameStatAPIDocs: APIDocs = { }, globalHistory: { description: 'Get a history of changes to a global stat', + scopes: [APIKeyScope.READ_GAME_STATS], params: { route: { internalName: 'The internal name of the stat' @@ -429,6 +436,7 @@ const GameStatAPIDocs: APIDocs = { }, listPlayerStats: { description: 'Get the current values of all the player\'s stats', + scopes: [APIKeyScope.READ_GAME_STATS], samples: [ { title: 'Sample response', @@ -481,4 +489,4 @@ const GameStatAPIDocs: APIDocs = { } } -export default GameStatAPIDocs +export { GameStatAPIDocs } diff --git a/src/docs/index.ts b/src/docs/index.ts new file mode 100644 index 00000000..33fc6f08 --- /dev/null +++ b/src/docs/index.ts @@ -0,0 +1,12 @@ +export * from './player-api.docs' +export * from './game-config-api.docs' +export * from './event-api.docs' +export * from './game-feedback-api.docs' +export * from './game-save-api.docs' +export * from './player-auth-api.docs' +export * from './player-presence-api.docs' +export * from './player-group-api.docs' +export * from './game-channel-api.docs' +export * from './game-stat-api.docs' +export * from './leaderboard-api.docs' +export * from './socket-tickets-api.docs' diff --git a/src/docs/leaderboard-api.docs.ts b/src/docs/leaderboard-api.docs.ts index 5c9e729c..e0ef4620 100644 --- a/src/docs/leaderboard-api.docs.ts +++ b/src/docs/leaderboard-api.docs.ts @@ -1,9 +1,11 @@ import LeaderboardAPIService from '../services/api/leaderboard-api.service' import APIDocs from './api-docs' +import { APIKeyScope } from '../entities/api-key' const LeaderboardAPIDocs: APIDocs = { get: { description: 'Get a leaderboard\'s entries\n50 results are returned per page, in the sort order defined by the leaderboard', + scopes: [APIKeyScope.READ_LEADERBOARDS], params: { headers: { 'x-talo-alias': 'The ID of the player\'s alias' @@ -142,6 +144,7 @@ const LeaderboardAPIDocs: APIDocs = { }, post: { description: 'Create or update a leaderboard\'s entry\nIf an entry exists for the player and the leaderboard mode is set to unique, that entry will be updated with the new score (and the updated key will return true)', + scopes: [APIKeyScope.WRITE_LEADERBOARDS], params: { body: { score: 'A numeric score for the entry' @@ -190,4 +193,4 @@ const LeaderboardAPIDocs: APIDocs = { } } -export default LeaderboardAPIDocs +export { LeaderboardAPIDocs } diff --git a/src/docs/player-api.docs.ts b/src/docs/player-api.docs.ts index bbdc5540..fe513ddb 100644 --- a/src/docs/player-api.docs.ts +++ b/src/docs/player-api.docs.ts @@ -1,9 +1,11 @@ import PlayerAPIService from '../services/api/player-api.service' import APIDocs from './api-docs' +import { APIKeyScope } from '../entities/api-key' const PlayerAPIDocs: APIDocs = { identify: { description: 'Identify a player', + scopes: [APIKeyScope.READ_PLAYERS, APIKeyScope.WRITE_PLAYERS], params: { headers: { 'x-talo-session': 'The session token (required if using Talo player authentication)' @@ -62,6 +64,7 @@ const PlayerAPIDocs: APIDocs = { }, search: { description: 'Search for players', + scopes: [APIKeyScope.READ_PLAYERS], params: { route: { id: 'The ID of the player' @@ -128,6 +131,7 @@ const PlayerAPIDocs: APIDocs = { }, get: { description: 'Find a player', + scopes: [APIKeyScope.READ_PLAYERS], params: { route: { id: 'The ID of the player' @@ -163,6 +167,7 @@ const PlayerAPIDocs: APIDocs = { }, patch: { description: 'Update a player\'s props', + scopes: [APIKeyScope.WRITE_PLAYERS], params: { body: { props: 'An array of @type(Props:prop). Props that the player doesn\'t have will be added. Props with updated values will overwrite existing props. Props with a null value will be deleted from the player' @@ -209,6 +214,7 @@ const PlayerAPIDocs: APIDocs = { }, merge: { description: 'Merge two players', + scopes: [APIKeyScope.READ_PLAYERS, APIKeyScope.WRITE_PLAYERS], params: { body: { playerId1: 'The first player ID - the second player will be merged into this player', @@ -252,6 +258,7 @@ const PlayerAPIDocs: APIDocs = { }, socketToken: { description: 'Create a socket token for a player', + scopes: [APIKeyScope.WRITE_PLAYERS], params: { headers: { 'x-talo-alias': 'The ID of the player\'s alias' @@ -268,4 +275,4 @@ const PlayerAPIDocs: APIDocs = { } } -export default PlayerAPIDocs +export { PlayerAPIDocs } diff --git a/src/docs/player-auth-api.docs.ts b/src/docs/player-auth-api.docs.ts index 4867b0af..c070b6d9 100644 --- a/src/docs/player-auth-api.docs.ts +++ b/src/docs/player-auth-api.docs.ts @@ -1,9 +1,11 @@ import PlayerAuthAPIService from '../services/api/player-auth-api.service' import APIDocs from './api-docs' +import { APIKeyScope } from '../entities/api-key' const PlayerAuthAPIDocs: APIDocs = { register: { description: 'Create a new player account', + scopes: [APIKeyScope.READ_PLAYERS, APIKeyScope.WRITE_PLAYERS], params: { body: { identifier: 'The unique identifier of the player. This can be their username, an email or a numeric ID', @@ -53,6 +55,7 @@ const PlayerAuthAPIDocs: APIDocs = { }, login: { description: 'Login to a player account', + scopes: [APIKeyScope.READ_PLAYERS, APIKeyScope.WRITE_PLAYERS], params: { body: { identifier: 'The unique identifier of the player. This can be their username, an email or a numeric ID', @@ -105,6 +108,7 @@ const PlayerAuthAPIDocs: APIDocs = { }, verify: { description: 'Provide the verification code to start the player session', + scopes: [APIKeyScope.READ_PLAYERS, APIKeyScope.WRITE_PLAYERS], params: { body: { aliasId: 'The ID of the alias to verify', @@ -150,6 +154,7 @@ const PlayerAuthAPIDocs: APIDocs = { }, logout: { description: 'Logout of a player account (and invalidate the session token)', + scopes: [APIKeyScope.READ_PLAYERS, APIKeyScope.WRITE_PLAYERS], params: { headers: { 'x-talo-player': 'The ID of the player', @@ -160,6 +165,7 @@ const PlayerAuthAPIDocs: APIDocs = { }, changePassword: { description: 'Change the password of a player account', + scopes: [APIKeyScope.READ_PLAYERS, APIKeyScope.WRITE_PLAYERS], params: { headers: { 'x-talo-player': 'The ID of the player', @@ -183,6 +189,7 @@ const PlayerAuthAPIDocs: APIDocs = { }, changeEmail: { description: 'Change the email address of a player account', + scopes: [APIKeyScope.READ_PLAYERS, APIKeyScope.WRITE_PLAYERS], params: { headers: { 'x-talo-player': 'The ID of the player', @@ -206,6 +213,7 @@ const PlayerAuthAPIDocs: APIDocs = { }, forgotPassword: { description: 'Send a password reset email to an email address', + scopes: [APIKeyScope.READ_PLAYERS, APIKeyScope.WRITE_PLAYERS], params: { body: { email: 'The email address to send the verification code to. If no player with this email exists, the request will be ignored' @@ -222,6 +230,7 @@ const PlayerAuthAPIDocs: APIDocs = { }, resetPassword: { description: 'Reset the password of a player account (invalidates any existing session tokens)', + scopes: [APIKeyScope.READ_PLAYERS, APIKeyScope.WRITE_PLAYERS], params: { body: { code: 'The 6-digit verification code sent to the email address (must be a string)', @@ -240,6 +249,7 @@ const PlayerAuthAPIDocs: APIDocs = { }, toggleVerification: { description: 'Toggle verification for a player account', + scopes: [APIKeyScope.READ_PLAYERS, APIKeyScope.WRITE_PLAYERS], params: { headers: { 'x-talo-player': 'The ID of the player', @@ -279,6 +289,7 @@ const PlayerAuthAPIDocs: APIDocs = { }, delete: { description: 'Delete a player account', + scopes: [APIKeyScope.READ_PLAYERS, APIKeyScope.WRITE_PLAYERS], params: { headers: { 'x-talo-player': 'The ID of the player', @@ -300,4 +311,4 @@ const PlayerAuthAPIDocs: APIDocs = { } } -export default PlayerAuthAPIDocs +export { PlayerAuthAPIDocs } diff --git a/src/docs/player-group-api.docs.ts b/src/docs/player-group-api.docs.ts index 47ffbaed..2522f89a 100644 --- a/src/docs/player-group-api.docs.ts +++ b/src/docs/player-group-api.docs.ts @@ -1,9 +1,11 @@ import PlayerGroupAPIService from '../services/api/player-group-api.service' import APIDocs from './api-docs' +import { APIKeyScope } from '../entities/api-key' const PlayerGroupAPIDocs: APIDocs = { get: { description: 'Get a group and its members', + scopes: [APIKeyScope.READ_PLAYER_GROUPS], params: { route: { id: 'The ID of the group' @@ -98,4 +100,4 @@ const PlayerGroupAPIDocs: APIDocs = { } } -export default PlayerGroupAPIDocs +export { PlayerGroupAPIDocs } diff --git a/src/docs/player-presence-api.docs.ts b/src/docs/player-presence-api.docs.ts index 24d82b88..14bd8e5d 100644 --- a/src/docs/player-presence-api.docs.ts +++ b/src/docs/player-presence-api.docs.ts @@ -1,9 +1,11 @@ import PlayerPresenceAPIService from '../services/api/player-presence-api.service' import APIDocs from './api-docs' +import { APIKeyScope } from '../entities/api-key' const PlayerPresenceAPIDocs: APIDocs = { get: { description: 'Get a player\'s online status and custom status', + scopes: [APIKeyScope.READ_PLAYER_PRESENCE], params: { route: { id: 'The ID of the player' @@ -42,6 +44,7 @@ const PlayerPresenceAPIDocs: APIDocs = { }, put: { description: 'Update a player\'s online status and custom status', + scopes: [APIKeyScope.WRITE_PLAYER_PRESENCE], params: { headers: { 'x-talo-alias': 'The ID of the player\'s alias' @@ -91,4 +94,4 @@ const PlayerPresenceAPIDocs: APIDocs = { } } -export default PlayerPresenceAPIDocs +export { PlayerPresenceAPIDocs } diff --git a/src/docs/socket-tickets-api.docs.ts b/src/docs/socket-tickets-api.docs.ts index 15c3bc1a..6aa4acec 100644 --- a/src/docs/socket-tickets-api.docs.ts +++ b/src/docs/socket-tickets-api.docs.ts @@ -15,4 +15,4 @@ const SocketTicketAPIDocs: APIDocs = { } } -export default SocketTicketAPIDocs +export { SocketTicketAPIDocs } diff --git a/src/services/api/event-api.service.ts b/src/services/api/event-api.service.ts index 125bd41e..6394ef3b 100644 --- a/src/services/api/event-api.service.ts +++ b/src/services/api/event-api.service.ts @@ -2,7 +2,7 @@ import { EntityManager } from '@mikro-orm/mysql' import { HasPermission, Request, Response, Validate, ValidationCondition, Route } from 'koa-clay' import EventAPIPolicy from '../../policies/api/event-api.policy' import APIService from './api-service' -import EventAPIDocs from '../../docs/event-api.docs' +import { EventAPIDocs } from '../../docs/event-api.docs' import Event from '../../entities/event' import Prop from '../../entities/prop' import { PropSizeError } from '../../lib/errors/propSizeError' diff --git a/src/services/api/game-channel-api.service.ts b/src/services/api/game-channel-api.service.ts index 8c2bb755..ace8fe6e 100644 --- a/src/services/api/game-channel-api.service.ts +++ b/src/services/api/game-channel-api.service.ts @@ -3,7 +3,7 @@ import GameChannelAPIPolicy from '../../policies/api/game-channel-api.policy' import APIService from './api-service' import GameChannel, { GameChannelLeavingReason } from '../../entities/game-channel' import { EntityManager, FilterQuery, LockMode } from '@mikro-orm/mysql' -import GameChannelAPIDocs from '../../docs/game-channel-api.docs' +import { GameChannelAPIDocs } from '../../docs/game-channel-api.docs' import PlayerAlias from '../../entities/player-alias' import GameChannelStorageProp from '../../entities/game-channel-storage-prop' import { PropSizeError } from '../../lib/errors/propSizeError' diff --git a/src/services/api/game-config-api.service.ts b/src/services/api/game-config-api.service.ts index a6b1ddf6..11db488f 100644 --- a/src/services/api/game-config-api.service.ts +++ b/src/services/api/game-config-api.service.ts @@ -2,7 +2,7 @@ import { HasPermission, Request, Response, Route } from 'koa-clay' import GameConfigAPIPolicy from '../../policies/api/game-config-api.policy' import APIService from './api-service' import { EntityManager } from '@mikro-orm/mysql' -import GameConfigAPIDocs from '../../docs/game-config-api.docs' +import { GameConfigAPIDocs } from '../../docs/game-config-api.docs' import Game from '../../entities/game' import { getResultCacheOptions } from '../../lib/perf/getResultCacheOptions' diff --git a/src/services/api/game-feedback-api.service.ts b/src/services/api/game-feedback-api.service.ts index daafb9a1..703df554 100644 --- a/src/services/api/game-feedback-api.service.ts +++ b/src/services/api/game-feedback-api.service.ts @@ -2,7 +2,7 @@ import { ForwardTo, HasPermission, Request, Response, Route, Validate, forwardRe import GameFeedbackAPIPolicy from '../../policies/api/game-feedback-api.policy' import APIService from './api-service' import GameFeedback from '../../entities/game-feedback' -import GameFeedbackAPIDocs from '../../docs/game-feedback-api.docs' +import { GameFeedbackAPIDocs } from '../../docs/game-feedback-api.docs' import { EntityManager } from '@mikro-orm/mysql' import GameFeedbackCategory from '../../entities/game-feedback-category' import { hardSanitiseProps } from '../../lib/props/sanitiseProps' diff --git a/src/services/api/game-save-api.service.ts b/src/services/api/game-save-api.service.ts index 5383362a..83d9592c 100644 --- a/src/services/api/game-save-api.service.ts +++ b/src/services/api/game-save-api.service.ts @@ -3,7 +3,7 @@ import GameSaveAPIPolicy from '../../policies/api/game-save-api.policy' import APIService from './api-service' import GameSave from '../../entities/game-save' import { EntityManager } from '@mikro-orm/mysql' -import GameSaveAPIDocs from '../../docs/game-save-api.docs' +import { GameSaveAPIDocs } from '../../docs/game-save-api.docs' import handleSQLError from '../../lib/errors/handleSQLError' function decodeContent(content: unknown) { diff --git a/src/services/api/game-stat-api.service.ts b/src/services/api/game-stat-api.service.ts index 198b6a78..4208fe70 100644 --- a/src/services/api/game-stat-api.service.ts +++ b/src/services/api/game-stat-api.service.ts @@ -1,7 +1,7 @@ import { EntityManager, LockMode } from '@mikro-orm/mysql' import { differenceInSeconds } from 'date-fns' import { HasPermission, Request, Response, Route, Validate } from 'koa-clay' -import GameStatAPIDocs from '../../docs/game-stat-api.docs' +import { GameStatAPIDocs } from '../../docs/game-stat-api.docs' import GameStat from '../../entities/game-stat' import PlayerGameStat from '../../entities/player-game-stat' import triggerIntegrations from '../../lib/integrations/triggerIntegrations' diff --git a/src/services/api/leaderboard-api.service.ts b/src/services/api/leaderboard-api.service.ts index 3fdf34b8..46f9efc6 100644 --- a/src/services/api/leaderboard-api.service.ts +++ b/src/services/api/leaderboard-api.service.ts @@ -4,7 +4,7 @@ import APIService from './api-service' import { EntityManager, NotFoundError, LockMode } from '@mikro-orm/mysql' import LeaderboardEntry from '../../entities/leaderboard-entry' import Leaderboard, { LeaderboardSortMode } from '../../entities/leaderboard' -import LeaderboardAPIDocs from '../../docs/leaderboard-api.docs' +import { LeaderboardAPIDocs } from '../../docs/leaderboard-api.docs' import triggerIntegrations from '../../lib/integrations/triggerIntegrations' import { hardSanitiseProps, mergeAndSanitiseProps } from '../../lib/props/sanitiseProps' import { PropSizeError } from '../../lib/errors/propSizeError' diff --git a/src/services/api/player-api.service.ts b/src/services/api/player-api.service.ts index 4752d6f6..270f3dc5 100644 --- a/src/services/api/player-api.service.ts +++ b/src/services/api/player-api.service.ts @@ -7,7 +7,7 @@ import PlayerAlias, { PlayerAliasService } from '../../entities/player-alias' import PlayerAPIPolicy from '../../policies/api/player-api.policy' import APIService from './api-service' import { uniqWith } from 'lodash' -import PlayerAPIDocs from '../../docs/player-api.docs' +import { PlayerAPIDocs } from '../../docs/player-api.docs' import PlayerGameStat from '../../entities/player-game-stat' import checkScope from '../../policies/checkScope' import { validateAuthSessionToken } from '../../middleware/player-auth-middleware' diff --git a/src/services/api/player-auth-api.service.ts b/src/services/api/player-auth-api.service.ts index b0741522..ef128c93 100644 --- a/src/services/api/player-auth-api.service.ts +++ b/src/services/api/player-auth-api.service.ts @@ -7,7 +7,7 @@ import { createPlayerFromIdentifyRequest, findAliasFromIdentifyRequest } from '. import PlayerAuth from '../../entities/player-auth' import bcrypt from 'bcrypt' import PlayerAuthAPIPolicy from '../../policies/api/player-auth-api.policy' -import PlayerAuthAPIDocs from '../../docs/player-auth-api.docs' +import { PlayerAuthAPIDocs } from '../../docs/player-auth-api.docs' import generateSixDigitCode from '../../lib/auth/generateSixDigitCode' import queueEmail from '../../lib/messaging/queueEmail' import PlayerAuthCode from '../../emails/player-auth-code-mail' diff --git a/src/services/api/player-group-api.service.ts b/src/services/api/player-group-api.service.ts index 3f09b248..0b37ff08 100644 --- a/src/services/api/player-group-api.service.ts +++ b/src/services/api/player-group-api.service.ts @@ -3,7 +3,7 @@ import APIService from './api-service' import PlayerGroupAPIPolicy from '../../policies/api/player-group-api.policy' import PlayerGroup from '../../entities/player-group' import Player from '../../entities/player' -import PlayerGroupAPIDocs from '../../docs/player-group-api.docs' +import { PlayerGroupAPIDocs } from '../../docs/player-group-api.docs' import { EntityManager } from '@mikro-orm/mysql' import { DEFAULT_PAGE_SIZE } from '../../lib/pagination/itemsPerPage' import { pageValidation } from '../../lib/pagination/pageValidation' diff --git a/src/services/api/player-presence-api.service.ts b/src/services/api/player-presence-api.service.ts index 737d240d..b1274b96 100644 --- a/src/services/api/player-presence-api.service.ts +++ b/src/services/api/player-presence-api.service.ts @@ -5,7 +5,7 @@ import PlayerPresenceAPIPolicy from '../../policies/api/player-presence-api.poli import Player from '../../entities/player' import PlayerPresence from '../../entities/player-presence' import PlayerAlias from '../../entities/player-alias' -import PlayerPresenceAPIDocs from '../../docs/player-presence-api.docs' +import { PlayerPresenceAPIDocs } from '../../docs/player-presence-api.docs' export default class PlayerPresenceAPIService extends APIService { @Route({ diff --git a/src/services/api/socket-ticket-api.service.ts b/src/services/api/socket-ticket-api.service.ts index 33531261..b81eb6d3 100644 --- a/src/services/api/socket-ticket-api.service.ts +++ b/src/services/api/socket-ticket-api.service.ts @@ -3,7 +3,7 @@ import APIService from './api-service' import { v4 } from 'uuid' import Redis from 'ioredis' import APIKey from '../../entities/api-key' -import SocketTicketAPIDocs from '../../docs/socket-tickets-api.docs' +import { SocketTicketAPIDocs } from '../../docs/socket-tickets-api.docs' export async function createSocketTicket(redis: Redis, key: APIKey, devBuild: boolean): Promise { const ticket = v4() diff --git a/src/services/public/documentation.service.ts b/src/services/public/documentation.service.ts index 88c6edf6..cb16238e 100644 --- a/src/services/public/documentation.service.ts +++ b/src/services/public/documentation.service.ts @@ -1,15 +1,47 @@ import { Response, Route, Service } from 'koa-clay' +import * as APIDocs from '../../docs' + +// ServiceName -> APIDocs +const docsMap = Object.fromEntries( + Object.entries(APIDocs).map(([key, value]) => [ + key.replace('APIDocs', 'APIService'), + value + ]) +) export default class DocumentationService extends Service { @Route({ method: 'GET' }) async index(): Promise { + const clayDocs = clay.docs + const enrichedDocs = this.enrichDocsWithScopes(clayDocs as unknown as Record) + return { status: 200, body: { - docs: clay.docs + docs: enrichedDocs } } } + + private enrichDocsWithScopes(clayDocs: Record): Record { + const docs = JSON.parse(JSON.stringify(clayDocs)) + const services = (docs as { services?: Record[] }).services + if (!Array.isArray(services)) return docs + + for (const service of services) { + const apiDocs = docsMap[service.name as keyof typeof docsMap] + if (!apiDocs || !Array.isArray(service.routes)) continue + + for (const route of service.routes as Record[]) { + const match = Object.values(apiDocs).find( + (doc) => doc?.description === route.description && doc.scopes + ) + if (match) route.scopes = match.scopes + } + } + + return docs + } }