diff --git a/server/api/game.go b/server/api/game.go
index 4a7ef02..26d5040 100644
--- a/server/api/game.go
+++ b/server/api/game.go
@@ -3,7 +3,9 @@ package api
import (
"fmt"
"net/http"
+ "strconv"
+ "github.com/gorilla/mux"
"github.com/rs/zerolog/log"
)
@@ -44,3 +46,22 @@ func (a *API) ListGames(w http.ResponseWriter, r *http.Request) {
JSON(w, games, 200)
}
+
+func (a *API) GetGame(w http.ResponseWriter, r *http.Request) {
+ gameID, _ := strconv.Atoi(mux.Vars(r)["id"])
+ user, _ := UserFromRequest(r)
+
+ game, err := a.App.GetGameForUser(gameID, user.ID)
+ if err != nil {
+ log.Error().Err(err).Msg("game couldn't be fetch")
+ http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
+ return
+ }
+
+ if game == nil {
+ http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
+ return
+ }
+
+ JSON(w, game, 200)
+}
diff --git a/server/app/game.go b/server/app/game.go
index 42e1cbc..d11960a 100644
--- a/server/app/game.go
+++ b/server/app/game.go
@@ -14,6 +14,20 @@ func (a *App) AddMember(gameID, userID int, role string) (*model.GameMember, err
return a.Store.Game().AddMember(gameID, userID, role)
}
+func (a *App) GetGameForUser(gameID, userID int) (*model.Game, error) {
+ game, err := a.Store.Game().GetByID(gameID)
+ if err != nil {
+ return nil, err
+ }
+
+ // ToDo: create a helper like userIsMember or
+ // userHasPermissionToGame instead of checking ownership
+ if game.UserID != userID {
+ return nil, nil
+ }
+ return game, nil
+}
+
func (a *App) ListGames() ([]*model.Game, error) {
games, err := a.Store.Game().List()
if err == sql.ErrNoRows {
diff --git a/server/web/web.go b/server/web/web.go
index 5bf7c6a..52f6d7f 100644
--- a/server/web/web.go
+++ b/server/web/web.go
@@ -41,6 +41,7 @@ func (w *WebServer) RegisterRoutes(api *api.API) {
apiRouter.HandleFunc("/users", api.Secured(api.CreateUser)).Methods("POST")
apiRouter.HandleFunc("/games", api.Secured(api.CreateGame)).Methods("POST")
apiRouter.HandleFunc("/games", api.Secured(api.ListGames)).Methods("GET")
+ apiRouter.HandleFunc("/games/{id:[0-9]+}", api.Secured(api.GetGame)).Methods("GET")
staticFSSub, _ := fs.Sub(staticFS, "static")
staticFSHandler := StaticFSHandler{http.FileServer(http.FS(staticFSSub))}
diff --git a/webapp/src/client.js b/webapp/src/client.js
index 3320bb2..1945233 100644
--- a/webapp/src/client.js
+++ b/webapp/src/client.js
@@ -1,3 +1,14 @@
+import * as log from './log'
+
+
+function tokenHeaders(headers) {
+ return {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${localStorage.getItem('token')}`,
+ ...headers
+ }
+}
+
export class Client {
login(username, password) {
// ToDo: handle error
@@ -9,13 +20,39 @@ export class Client {
if (r.status === 200) {
return r.text()
}
- console.error("INVALID")
- throw new Error("invalid credentials")
+ log.error(`Got invalid response when trying to login. Code ${r.status}`)
+ throw new Error(`${r.status} invalid response`)
}).then(token => {
localStorage.setItem('token', token)
return token
})
}
+
+ listGames() {
+ return fetch("/api/games", {
+ method: 'GET',
+ headers: tokenHeaders(),
+ }).then(r => {
+ if (r.status === 200) {
+ return r.json()
+ }
+ log.error(`Got invalid response when trying to list games. Code ${r.status}`)
+ throw new Error(`${r.status} invalid response`)
+ })
+ }
+
+ getGame(id) {
+ return fetch(`/api/games/${id}`, {
+ method: 'GET',
+ headers: tokenHeaders(),
+ }).then(r => {
+ if (r.status === 200) {
+ return r.json()
+ }
+ log.error(`Got invalid response when trying to get game ${id}. Code ${r.status}`)
+ throw new Error(`${r.status} invalid response`)
+ })
+ }
}
const client = new Client()
diff --git a/webapp/src/index.js b/webapp/src/index.js
index a5fa1b9..7d31ab9 100644
--- a/webapp/src/index.js
+++ b/webapp/src/index.js
@@ -13,6 +13,7 @@ import { CssBaseline } from '@material-ui/core';
import { TokenContext } from './context'
import Login from './pages/login'
import Home from './pages/home'
+import Game from './pages/game'
const Secure = ({children}) => {
@@ -36,7 +37,13 @@ const App = () => {
-
+
+
+
+
+
+
+
diff --git a/webapp/src/log.js b/webapp/src/log.js
index a1ca099..3297a76 100644
--- a/webapp/src/log.js
+++ b/webapp/src/log.js
@@ -13,25 +13,25 @@ function nowStr() {
return `${now.getFullYear()}${now.getDate()}${now.getMonth()}.${now.getHours()}${now.getMinutes()}${now.getSeconds()}`
}
-export function Debug(msg) {
+export function debug(msg) {
if (level <= levelDebug) {
console.log(`[DBG ${nowStr()}] ${msg}`)
}
}
-export function Info(msg) {
+export function info(msg) {
if (level <= levelInfo) {
console.log(`[INF ${nowStr()}] ${msg}`)
}
}
-export function Warn(msg) {
+export function warn(msg) {
if (level <= levelWarn) {
console.warn(`[WRN ${nowStr()}] ${msg}`)
}
}
-export function Error(msg) {
+export function error(msg) {
if (level <= levelError) {
console.error(`[ERR ${nowStr()}] ${msg}`)
}
diff --git a/webapp/src/pages/game.js b/webapp/src/pages/game.js
new file mode 100644
index 0000000..c3c9c53
--- /dev/null
+++ b/webapp/src/pages/game.js
@@ -0,0 +1,22 @@
+import { useState, useEffect } from 'react'
+import { useParams } from 'react-router-dom'
+
+import client from '../client'
+
+
+const Game = () => {
+ const [game, setGame] = useState({})
+ const { gameId } = useParams()
+
+ useEffect(() => {
+ client.getGame(gameId).then(game => setGame(game))
+ }, [gameId])
+
+ return (
+
+
{game.name}
+
+ )
+}
+
+export default Game
diff --git a/webapp/src/pages/home.js b/webapp/src/pages/home.js
index 043c4fa..5a80e74 100644
--- a/webapp/src/pages/home.js
+++ b/webapp/src/pages/home.js
@@ -1,12 +1,36 @@
-import { useContext } from 'react'
+import {
+ useContext,
+ useEffect,
+ useState
+} from 'react'
+import { Link } from 'react-router-dom'
import {
Box,
Button
} from '@material-ui/core';
import { TokenContext } from '../context'
+import client from '../client'
+const GameList = () => {
+ const [games, setGames] = useState([])
+
+ useEffect(() => {
+ client.listGames().then(gameList => setGames(gameList))
+ }, [])
+
+ return (
+
+
Game List
+
+
+ {games.map(game => (- {game.name}
))}
+
+
+ )
+}
+
const Home = () => {
const { token, setToken } = useContext(TokenContext)
@@ -20,6 +44,8 @@ const Home = () => {
Home
+
+