package main import ( "encoding/json" "fmt" "local/router" "local/storage" "log" "net/http" "strings" ) type Server struct { config Config gm *GameMaster *router.Router } func NewServer(config Config, gm *GameMaster) *Server { return &Server{ config: config, gm: gm, Router: router.New(), } } func (server *Server) Routes() error { cases := map[string]map[string]http.HandlerFunc{ fmt.Sprintf("%s/%s%s", server.config.Server.File.Prefix, router.Wildcard, router.Wildcard): map[string]http.HandlerFunc{ http.MethodGet: server.File, }, fmt.Sprintf("%s/games", server.config.Server.API.Prefix): map[string]http.HandlerFunc{ http.MethodGet: server.GameList, }, fmt.Sprintf("%s/games/%s", server.config.Server.API.Prefix, router.Wildcard): map[string]http.HandlerFunc{ http.MethodGet: server.GameGet, http.MethodPost: server.GameInsert, http.MethodPut: server.GameReplace, }, } for path, spec := range cases { log.Println("listening for:", path) handler := server.ByMethod(spec) if err := server.Add(path, handler); err != nil { return err } } return nil } func (server *Server) ByMethod(m map[string]http.HandlerFunc) http.HandlerFunc { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { foo, ok := m[r.Method] if ok { foo(w, r) } else { http.NotFound(w, r) } }) } func (server *Server) File(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.NotFound(w, r) return } s := http.FileServer(http.Dir(server.config.Server.File.Root)) r.URL.Path = strings.TrimPrefix(r.URL.Path, server.config.Server.File.Prefix) s.ServeHTTP(w, r) } func (server *Server) GameList(w http.ResponseWriter, r *http.Request) { resp, err := server.gm.ListGames() if err != nil { internalError(w, err.Error()) return } json.NewEncoder(w).Encode(resp) } func (server *Server) GameGet(w http.ResponseWriter, r *http.Request) { var gameID string err := router.Params(r, &gameID) if err != nil { badRequest(w, err.Error()) return } resp, err := server.gm.GetGame(gameID) if err != nil { internalError(w, err.Error()) return } json.NewEncoder(w).Encode(resp) } func (server *Server) GameReplace(w http.ResponseWriter, r *http.Request) { var gameID string err := router.Params(r, &gameID) if err != nil { badRequest(w, err.Error()) return } var game Game err = json.NewDecoder(r.Body).Decode(&game) if err != nil { badRequest(w, err.Error()) return } err = server.gm.ReplaceGame(gameID, game) if err != nil { internalError(w, err.Error()) return } json.NewEncoder(w).Encode(game) } func (server *Server) GameInsert(w http.ResponseWriter, r *http.Request) { var gameID string err := router.Params(r, &gameID) if err != nil { badRequest(w, err.Error()) return } err = server.gm.CreateGame(gameID) if err != nil { internalError(w, err.Error()) return } json.NewEncoder(w).Encode(Game{}) } func notImpl(w http.ResponseWriter) { errHandler(w, http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented)) } func internalError(w http.ResponseWriter, message string) { errHandler(w, http.StatusInternalServerError, message) } func badRequest(w http.ResponseWriter, message string) { errHandler(w, http.StatusBadRequest, message) } func errHandler(w http.ResponseWriter, status int, message string) { if message == storage.ErrNotFound.Error() { status = http.StatusNotFound } w.WriteHeader(status) json.NewEncoder(w).Encode(map[string]interface{}{ "status": http.StatusText(status), "statusCode": status, "error": message, }) }