package server import ( "encoding/json" "fmt" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "log" "net/http" "opensavecloudserver/config" "opensavecloudserver/data/repository/game" "opensavecloudserver/data/repository/user" "opensavecloudserver/server/authentication" "time" ) type ( HTTPServer struct { Server *http.Server config config.Configuration deps DatasourceDependencies } DatasourceDependencies struct { UserRepository user.UserRepository GameRepository game.GameRepository Authenticator authentication.Authenticator } ) // NewServer start the http server func NewServer(config config.Configuration, deps DatasourceDependencies) *HTTPServer { s := &HTTPServer{config: config, deps: deps} router := chi.NewRouter() router.NotFound(func(writer http.ResponseWriter, request *http.Request) { notFound("This route does not exist", writer, request) }) router.MethodNotAllowed(func(writer http.ResponseWriter, request *http.Request) { methodNotAllowed(writer, request) }) router.Use(middleware.Logger) router.Use(recoverMiddleware) if config.Server.Compress { router.Use(middleware.Compress(5, "application/json", "application/octet‑stream")) } if config.Server.PingEndPoint { router.Use(middleware.Heartbeat("/heartbeat")) } router.Route("/api", func(routerAPI chi.Router) { routerAPI.Route("/v1", func(r chi.Router) { // Unsupported V1 because it was shitty, sorry about that r.HandleFunc("/*", unsupportedAPIHandler) }) routerAPI.Route("/v2", func(r chi.Router) { // Get information about the server r.Get("/version", s.Information) // Authentication routes // Login and get a token r.Post("/login", s.loginUserHandler) if config.Features.AllowRegister { // Register a user r.Post("/register", s.registerUserHandler) } // Secured routes r.Group(func(secureRouter chi.Router) { secureRouter.Use(s.authMiddleware) // Logged user routes secureRouter.Route("/user", func(userRouter chi.Router) { // Get information about the logged user userRouter.Get("/", s.currentUserHandler) // Change the password of the current user userRouter.Put("/password/update", s.updateCurrentUserPasswordHandler) }) // Save files routes secureRouter.Route("/saves", func(gameRouter chi.Router) { // Create a save entry gameRouter.Post("/create", s.createSaveEntryHandler) // List all available saves gameRouter.Get("/", s.allUserSavesInformationHandler) // Remove a save gameRouter.Delete("/{id}", s.deleteSave) // Get the information about a save gameRouter.Get("/{id}", s.saveInformationHandler) // Data routes gameRouter.Group(func(uploadRouter chi.Router) { uploadRouter.Use(s.uploadMiddleware) // Upload data uploadRouter.Put("/{id}/data", s.uploadDataHandler) // downloadDataHandler data uploadRouter.Get("/{id}/data", s.downloadDataHandler) }) }) secureRouter.Route("/admin", func(adminRouter chi.Router) { adminRouter.Use(s.adminMiddleware) // Create a user adminRouter.Post("/user/create", s.createUserHandler) // Update the username of a user adminRouter.Post("/user/{id}/username", s.updateUsernameHandler) // Update the password of a user adminRouter.Post("/user/{id}/password/update", s.updateUserPasswordHandler) // Remove a user adminRouter.Delete("/user/{id}", s.deleteUserHandler) // Get information about a user adminRouter.Get("/user/{id}", s.userHandler) // List all user registered on the server adminRouter.Get("/users", s.listAllServerUsersHandler) // Update role adminRouter.Put("/user/{id}/role", s.updateUserRoleHandler) }) }) }) }) s.Server = &http.Server{ Addr: fmt.Sprintf(":%d", config.Server.Port), Handler: router, } return s } func unsupportedAPIHandler(w http.ResponseWriter, r *http.Request) { e := httpError{ Status: http.StatusGone, Error: "API Not supported anymore", Message: "This version of the server does not support the V1 version of the API.", Path: r.RequestURI, Timestamp: time.Now(), } payload, err := json.Marshal(e) if err != nil { log.Println(err) } w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusGone) _, err = w.Write(payload) if err != nil { log.Println(err) } }