@@ -17,6 +17,9 @@ import { CreateGameReducer } from '../core/reducer';
1717
1818const createCredentials = ( ) => uuid ( ) ;
1919const getGameMetadataKey = gameID => `${ gameID } :metadata` ;
20+ const isGameMetadataKey = ( key , gameName ) =>
21+ key . match ( gameName + ':.*:metadata' ) ;
22+ const getNamespacedGameID = ( gameID , gameName ) => `${ gameName } :${ gameID } ` ;
2023const getNewGameInstanceID = ( ) => uuid ( ) ;
2124const createGameMetadata = ( ) => ( {
2225 players : { } ,
@@ -90,7 +93,7 @@ export const createApiServer = ({ db, games }) => {
9093 }
9194
9295 const gameID = getNewGameInstanceID ( ) ;
93- const namespacedGameID = ` ${ gameName } : ${ gameID } ` ;
96+ const namespacedGameID = getNamespacedGameID ( gameID , gameName ) ;
9497
9598 await db . set ( getGameMetadataKey ( namespacedGameID ) , gameMetadata ) ;
9699 await db . set ( namespacedGameID , state ) ;
@@ -100,17 +103,47 @@ export const createApiServer = ({ db, games }) => {
100103 } ;
101104 } ) ;
102105
106+ router . get ( '/games/:name' , async ctx => {
107+ const gameName = ctx . params . name ;
108+ const gameList = await db . list ( ) ;
109+ let gameInstances = [ ] ;
110+ for ( let key of Array . from ( gameList ) ) {
111+ if ( isGameMetadataKey ( key , gameName ) ) {
112+ const gameID = key . slice (
113+ gameName . length + 1 ,
114+ key . lastIndexOf ( ':metadata' )
115+ ) ;
116+ const metadata = await db . get ( key ) ;
117+ gameInstances . push ( {
118+ gameID : gameID ,
119+ players : Object . values ( metadata . players ) . map ( player => {
120+ // strip away credentials
121+ return { id : player . id , name : player . name } ;
122+ } ) ,
123+ } ) ;
124+ }
125+ }
126+ ctx . body = {
127+ gameInstances : gameInstances ,
128+ } ;
129+ } ) ;
130+
103131 router . post ( '/games/:name/:id/join' , koaBody ( ) , async ctx => {
104132 const gameName = ctx . params . name ;
105133 const gameID = ctx . params . id ;
106134 const playerID = ctx . request . body . playerID ;
107135 const playerName = ctx . request . body . playerName ;
108-
109- const namespacedGameID = `${ gameName } :${ gameID } ` ;
136+ const namespacedGameID = getNamespacedGameID ( gameID , gameName ) ;
110137 const gameMetadata = await db . get ( getGameMetadataKey ( namespacedGameID ) ) ;
111138
112- if ( gameMetadata === null ) {
113- ctx . throw ( 404 , 'Game not found' ) ;
139+ if ( ! gameMetadata ) {
140+ ctx . throw ( 404 , 'Game ' + gameID + ' not found' ) ;
141+ }
142+ if ( ! gameMetadata . players [ playerID ] ) {
143+ ctx . throw ( 404 , 'Player ' + playerID + ' not found' ) ;
144+ }
145+ if ( gameMetadata . players [ playerID ] . name ) {
146+ ctx . throw ( 409 , 'Player ' + playerID + ' not available' ) ;
114147 }
115148
116149 gameMetadata . players [ playerID ] . name = playerName ;
@@ -123,6 +156,37 @@ export const createApiServer = ({ db, games }) => {
123156 } ;
124157 } ) ;
125158
159+ router . post ( '/games/:name/:id/leave' , koaBody ( ) , async ctx => {
160+ const gameName = ctx . params . name ;
161+ const gameID = ctx . params . id ;
162+ const playerID = ctx . request . body . playerID ;
163+ const playerCredentials = ctx . request . body . playerCredentials ;
164+ const namespacedGameID = getNamespacedGameID ( gameID , gameName ) ;
165+ const gameMetadata = await db . get ( getGameMetadataKey ( namespacedGameID ) ) ;
166+
167+ if ( ! gameMetadata ) {
168+ ctx . throw ( 404 , 'Game ' + gameID + ' not found' ) ;
169+ }
170+ if ( ! gameMetadata . players [ playerID ] ) {
171+ ctx . throw ( 404 , 'Player ' + playerID + ' not found' ) ;
172+ }
173+ if (
174+ playerCredentials !== gameMetadata . players [ playerID ] . playerCredentials
175+ ) {
176+ ctx . throw ( 403 , 'Invalid credentials ' + playerCredentials ) ;
177+ }
178+
179+ delete gameMetadata . players [ playerID ] . name ;
180+ if ( Object . values ( gameMetadata . players ) . some ( val => val . name ) ) {
181+ await db . set ( getGameMetadataKey ( namespacedGameID ) , gameMetadata ) ;
182+ } else {
183+ // remove game
184+ await db . remove ( gameID ) ;
185+ await db . remove ( getGameMetadataKey ( namespacedGameID ) ) ;
186+ }
187+ ctx . body = { } ;
188+ } ) ;
189+
126190 app . use ( cors ( ) ) ;
127191
128192 // If API_SECRET is set, then require that requests set an
0 commit comments