diff --git a/src/auth.go b/src/api/auth.go similarity index 85% rename from src/auth.go rename to src/api/auth.go index 34551f9..5180eee 100644 --- a/src/auth.go +++ b/src/api/auth.go @@ -1,8 +1,10 @@ -package main +package api import ( + "github.com/mroote/factorio-server-manager/bootstrap" "log" "os" + "sync" "github.com/apexskier/httpauth" ) @@ -19,8 +21,18 @@ type User struct { Email string `json:"email"` } -func initAuth() *AuthHTTP { - return &AuthHTTP{} +var once sync.Once +var instantiated *AuthHTTP + +func GetAuth() *AuthHTTP { + once.Do(func() { + Auth := &AuthHTTP{} + config := bootstrap.GetConfig() + _ = Auth.CreateAuth(config.DatabaseFile, config.CookieEncryptionKey) + _ = Auth.CreateOrUpdateUser(config.Username, config.Password, "admin", "") + instantiated = Auth + }) + return instantiated } func (auth *AuthHTTP) CreateAuth(backendFile string, cookieKey string) error { diff --git a/src/handlers.go b/src/api/handlers.go similarity index 87% rename from src/handlers.go rename to src/api/handlers.go index d97cecd..3ecad45 100644 --- a/src/handlers.go +++ b/src/api/handlers.go @@ -1,9 +1,11 @@ -package main +package api import ( "encoding/json" "errors" "fmt" + "github.com/mroote/factorio-server-manager/bootstrap" + "github.com/mroote/factorio-server-manager/factorio" "io" "io/ioutil" "log" @@ -52,14 +54,14 @@ func ReadRequestBody(w http.ResponseWriter, r *http.Request, resp *interface{}) // Lists all save files in the factorio/saves directory func ListSaves(w http.ResponseWriter, r *http.Request) { var resp interface{} - + config := bootstrap.GetConfig() defer func() { WriteResponse(w, resp) }() w.Header().Set("Content-Type", "application/json;charset=UTF-8") - savesList, err := listSaves(config.FactorioSavesDir) + savesList, err := factorio.ListSaves(config.FactorioSavesDir) if err != nil { resp = fmt.Sprintf("Error listing save files: %s", err) log.Println(resp) @@ -67,7 +69,7 @@ func ListSaves(w http.ResponseWriter, r *http.Request) { return } - loadLatest := Save{Name: "Load Latest"} + loadLatest := factorio.Save{Name: "Load Latest"} savesList = append(savesList, loadLatest) resp = savesList @@ -75,7 +77,7 @@ func ListSaves(w http.ResponseWriter, r *http.Request) { func DLSave(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/octet-stream") - + config := bootstrap.GetConfig() vars := mux.Vars(r) save := vars["save"] saveName := filepath.Join(config.FactorioSavesDir, save) @@ -96,6 +98,7 @@ func UploadSave(w http.ResponseWriter, r *http.Request) { log.Println("Uploading save file") r.ParseMultipartForm(32 << 20) + config := bootstrap.GetConfig() for _, saveFile := range r.MultipartForm.File["savefile"] { ext := filepath.Ext(saveFile.Filename) @@ -149,7 +152,7 @@ func RemoveSave(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) name := vars["save"] - save, err := findSave(name) + save, err := factorio.FindSave(name) if err != nil { resp = fmt.Sprintf("Error finding save {%s}: %s", name, err) log.Println(resp) @@ -157,7 +160,7 @@ func RemoveSave(w http.ResponseWriter, r *http.Request) { return } - err = save.remove() + err = save.Remove() if err != nil { resp = fmt.Sprintf("Error removing save {%s}: %s", name, err) log.Println(resp) @@ -188,9 +191,9 @@ func CreateSaveHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadRequest) return } - + config := bootstrap.GetConfig() saveFile := filepath.Join(config.FactorioSavesDir, saveName) - cmdOut, err := createSave(saveFile) + cmdOut, err := factorio.CreateSave(saveFile) if err != nil { resp = fmt.Sprintf("Error creating save {%s}: %s", saveName, err) log.Println(resp) @@ -211,8 +214,8 @@ func LogTail(w http.ResponseWriter, r *http.Request) { }() w.Header().Set("Content-Type", "application/json;charset=UTF-8") - - resp, err = tailLog(config.FactorioLog) + config := bootstrap.GetConfig() + resp, err = factorio.TailLog(config.FactorioLog) if err != nil { resp = fmt.Sprintf("Could not tail %s: %s", config.FactorioLog, err) return @@ -229,8 +232,8 @@ func LoadConfig(w http.ResponseWriter, r *http.Request) { }() w.Header().Set("Content-Type", "application/json;charset=UTF-8") - - configContents, err := loadConfig(config.FactorioConfigFile) + config := bootstrap.GetConfig() + configContents, err := factorio.LoadConfig(config.FactorioConfigFile) if err != nil { resp = fmt.Sprintf("Could not retrieve config.ini: %s", err) log.Println(resp) @@ -246,14 +249,14 @@ func LoadConfig(w http.ResponseWriter, r *http.Request) { func StartServer(w http.ResponseWriter, r *http.Request) { var err error var resp interface{} - + var server, _ = factorio.GetFactorioServer() defer func() { WriteResponse(w, resp) }() w.Header().Set("Content-Type", "application/json;charset=UTF-8") - if FactorioServ.Running { + if server.Running { resp = "Factorio server is already running" w.WriteHeader(http.StatusConflict) return @@ -268,7 +271,7 @@ func StartServer(w http.ResponseWriter, r *http.Request) { log.Printf("Starting Factorio server with settings: %v", string(body)) - err = json.Unmarshal(body, &FactorioServ) + err = json.Unmarshal(body, &server) if err != nil { resp = fmt.Sprintf("Error unmarshalling server settings JSON: %s", err) log.Println(resp) @@ -277,7 +280,7 @@ func StartServer(w http.ResponseWriter, r *http.Request) { } // Check if savefile was submitted with request to start server. - if FactorioServ.Savefile == "" { + if server.Savefile == "" { resp = "Error starting Factorio server: No save file provided" log.Println(resp) w.WriteHeader(http.StatusBadRequest) @@ -285,7 +288,7 @@ func StartServer(w http.ResponseWriter, r *http.Request) { } go func() { - err = FactorioServ.Run() + err = server.Run() if err != nil { log.Printf("Error starting Factorio server: %+v", err) return @@ -295,7 +298,7 @@ func StartServer(w http.ResponseWriter, r *http.Request) { timeout := 0 for timeout <= 3 { time.Sleep(1 * time.Second) - if FactorioServ.Running { + if server.Running { break } else { log.Printf("Did not detect running Factorio server attempt: %+v", timeout) @@ -304,14 +307,14 @@ func StartServer(w http.ResponseWriter, r *http.Request) { timeout++ } - if FactorioServ.Running == false { + if server.Running == false { resp = fmt.Sprintf("Error starting Factorio server: %s", err) log.Println(resp) w.WriteHeader(http.StatusInternalServerError) return } - resp = fmt.Sprintf("Factorio server with save: %s started on port: %d", FactorioServ.Savefile, FactorioServ.Port) + resp = fmt.Sprintf("Factorio server with save: %s started on port: %d", server.Savefile, server.Port) log.Println(resp) } @@ -323,9 +326,9 @@ func StopServer(w http.ResponseWriter, r *http.Request) { }() w.Header().Set("Content-Type", "application/json;charset=UTF-8") - - if FactorioServ.Running { - err := FactorioServ.Stop() + var server, _ = factorio.GetFactorioServer() + if server.Running { + err := server.Stop() if err != nil { resp = fmt.Sprintf("Error stopping factorio server: %s", err) log.Println(resp) @@ -350,9 +353,9 @@ func KillServer(w http.ResponseWriter, r *http.Request) { }() w.Header().Set("Content-Type", "application/json;charset=UTF-8") - - if FactorioServ.Running { - err := FactorioServ.Kill() + var server, _ = factorio.GetFactorioServer() + if server.Running { + err := server.Kill() if err != nil { resp = fmt.Sprintf("Error killing factorio server: %s", err) log.Println(resp) @@ -375,12 +378,12 @@ func CheckServer(w http.ResponseWriter, r *http.Request) { }() w.Header().Set("Content-Type", "application/json;charset=UTF-8") - - if FactorioServ.Running { + var server, _ = factorio.GetFactorioServer() + if server.Running { resp["status"] = "running" - resp["port"] = strconv.Itoa(FactorioServ.Port) - resp["savefile"] = FactorioServ.Savefile - resp["address"] = FactorioServ.BindIP + resp["port"] = strconv.Itoa(server.Port) + resp["savefile"] = server.Savefile + resp["address"] = server.BindIP } else { resp["status"] = "stopped" } @@ -394,9 +397,9 @@ func FactorioVersion(w http.ResponseWriter, r *http.Request) { }() w.Header().Set("Content-Type", "application/json;charset=UTF-8") - - resp["version"] = FactorioServ.Version.String() - resp["base_mod_version"] = FactorioServ.BaseModVersion + var server, _ = factorio.GetFactorioServer() + resp["version"] = server.Version.String() + resp["base_mod_version"] = server.BaseModVersion } // Unmarshall the User object from the given bytearray @@ -433,7 +436,7 @@ func LoginUser(w http.ResponseWriter, r *http.Request) { } log.Printf("Logging in user: %s", user.Username) - + Auth := GetAuth() err = Auth.aaa.Login(w, r, user.Username, user.Password, "/") if err != nil { resp = fmt.Sprintf("Error loggin in user: %s, error: %s", user.Username, err) @@ -453,7 +456,7 @@ func LogoutUser(w http.ResponseWriter, r *http.Request) { }() w.Header().Set("Content-Type", "application/json;charset=UTF-8") - + Auth := GetAuth() if err = Auth.aaa.Logout(w, r); err != nil { log.Printf("Error logging out current user") w.WriteHeader(http.StatusInternalServerError) @@ -473,7 +476,7 @@ func GetCurrentLogin(w http.ResponseWriter, r *http.Request) { }() w.Header().Set("Content-Type", "application/json;charset=UTF-8") - + Auth := GetAuth() user, err := Auth.aaa.CurrentUser(w, r) if err != nil { resp = fmt.Sprintf("Error getting user status: %s, error: %s", user.Username, err) @@ -493,7 +496,7 @@ func ListUsers(w http.ResponseWriter, r *http.Request) { }() w.Header().Set("Content-Type", "application/json;charset=UTF-8") - + Auth := GetAuth() users, err := Auth.listUsers() if err != nil { resp = fmt.Sprintf("Error listing users: %s", err) @@ -524,7 +527,7 @@ func AddUser(w http.ResponseWriter, r *http.Request) { if err != nil { return } - + Auth := GetAuth() err = Auth.addUser(user.Username, user.Password, user.Email, user.Role) if err != nil { resp = fmt.Sprintf("Error in adding user {%s}: %s", user.Username, err) @@ -554,7 +557,7 @@ func RemoveUser(w http.ResponseWriter, r *http.Request) { if err != nil { return } - + Auth := GetAuth() err = Auth.removeUser(user.Username) if err != nil { resp = fmt.Sprintf("Error in removing user {%s}, error: %s", user.Username, err) @@ -574,8 +577,8 @@ func GetServerSettings(w http.ResponseWriter, r *http.Request) { }() w.Header().Set("Content-Type", "application/json;charset=UTF-8") - - resp = FactorioServ.Settings + var server, _ = factorio.GetFactorioServer() + resp = server.Settings log.Printf("Sent server settings response") } @@ -594,8 +597,8 @@ func UpdateServerSettings(w http.ResponseWriter, r *http.Request) { return } log.Printf("Received settings JSON: %s", body) - - err = json.Unmarshal(body, &FactorioServ.Settings) + var server, _ = factorio.GetFactorioServer() + err = json.Unmarshal(body, &server.Settings) if err != nil { resp = fmt.Sprintf("Error unmarhaling server settings JSON: %s", err) log.Println(resp) @@ -603,14 +606,14 @@ func UpdateServerSettings(w http.ResponseWriter, r *http.Request) { return } - settings, err := json.MarshalIndent(&FactorioServ.Settings, "", " ") + settings, err := json.MarshalIndent(&server.Settings, "", " ") if err != nil { resp = fmt.Sprintf("Failed to marshal server settings: %s", err) log.Println(resp) w.WriteHeader(http.StatusInternalServerError) return } - + config := bootstrap.GetConfig() err = ioutil.WriteFile(filepath.Join(config.FactorioConfigDir, config.SettingsFile), settings, 0644) if err != nil { resp = fmt.Sprintf("Failed to save server settings: %v\n", err) @@ -621,9 +624,9 @@ func UpdateServerSettings(w http.ResponseWriter, r *http.Request) { log.Printf("Saved Factorio server settings in server-settings.json") - if (FactorioServ.Version.Greater(Version{0, 17, 0})) { + if (server.Version.Greater(factorio.Version{0, 17, 0})) { // save admins to adminJson - admins, err := json.MarshalIndent(FactorioServ.Settings["admins"], "", " ") + admins, err := json.MarshalIndent(server.Settings["admins"], "", " ") if err != nil { resp = fmt.Sprintf("Failed to marshal admins-Setting: %s", err) log.Println(resp) diff --git a/src/mod_modpack_handler.go b/src/api/mod_modpack_handler.go similarity index 85% rename from src/mod_modpack_handler.go rename to src/api/mod_modpack_handler.go index 0a0a93c..3a7f406 100644 --- a/src/mod_modpack_handler.go +++ b/src/api/mod_modpack_handler.go @@ -1,10 +1,12 @@ -package main +package api import ( "archive/zip" "errors" "fmt" "github.com/gorilla/mux" + "github.com/mroote/factorio-server-manager/bootstrap" + "github.com/mroote/factorio-server-manager/factorio" "io" "log" "net/http" @@ -12,8 +14,8 @@ import ( "path/filepath" ) -func CheckModPackExists(modPackMap ModPackMap, modPackName string, w http.ResponseWriter, resp interface{}) error { - exists := modPackMap.checkModPackExists(modPackName) +func CheckModPackExists(modPackMap factorio.ModPackMap, modPackName string, w http.ResponseWriter, resp interface{}) error { + exists := modPackMap.CheckModPackExists(modPackName) if !exists { resp = fmt.Sprintf("requested modPack {%s} does not exist", modPackName) log.Println(resp) @@ -23,8 +25,8 @@ func CheckModPackExists(modPackMap ModPackMap, modPackName string, w http.Respon return nil } -func CreateNewModPackMap(w http.ResponseWriter, resp *interface{}) (modPackMap ModPackMap, err error) { - modPackMap, err = newModPackMap() +func CreateNewModPackMap(w http.ResponseWriter, resp *interface{}) (modPackMap factorio.ModPackMap, err error) { + modPackMap, err = factorio.NewModPackMap() if err != nil { w.WriteHeader(http.StatusInternalServerError) *resp = fmt.Sprintf("Error creating modpackmap aka. list of all modpacks files : %s", err) @@ -33,7 +35,7 @@ func CreateNewModPackMap(w http.ResponseWriter, resp *interface{}) (modPackMap M return } -func ReadModPackRequest(w http.ResponseWriter, r *http.Request, resp *interface{}) (err error, packMap ModPackMap, modPackName string) { +func ReadModPackRequest(w http.ResponseWriter, r *http.Request, resp *interface{}) (err error, packMap factorio.ModPackMap, modPackName string) { vars := mux.Vars(r) modPackName = vars["modpack"] @@ -67,7 +69,7 @@ func ModPackListHandler(w http.ResponseWriter, r *http.Request) { return } - resp = modPackMap.listInstalledModPacks() + resp = modPackMap.ListInstalledModPacks() } func ModPackCreateHandler(w http.ResponseWriter, r *http.Request) { @@ -93,7 +95,7 @@ func ModPackCreateHandler(w http.ResponseWriter, r *http.Request) { return } - err = modPackMap.createModPack(modPackStruct.Name) + err = modPackMap.CreateModPack(modPackStruct.Name) if err != nil { w.WriteHeader(http.StatusInternalServerError) resp = fmt.Sprintf("Error creating modpack file: %s", err) @@ -101,7 +103,7 @@ func ModPackCreateHandler(w http.ResponseWriter, r *http.Request) { return } - resp = modPackMap.listInstalledModPacks() + resp = modPackMap.ListInstalledModPacks() } func ModPackDeleteHandler(w http.ResponseWriter, r *http.Request) { @@ -119,7 +121,7 @@ func ModPackDeleteHandler(w http.ResponseWriter, r *http.Request) { return } - err = modPackMap.deleteModPack(modPackName) + err = modPackMap.DeleteModPack(modPackName) if err != nil { w.WriteHeader(http.StatusInternalServerError) resp = fmt.Sprintf("Error deleting modpack file: %s", err) @@ -146,6 +148,8 @@ func ModPackDownloadHandler(w http.ResponseWriter, r *http.Request) { zipWriter := zip.NewWriter(w) defer zipWriter.Close() + config := bootstrap.GetConfig() + //iterate over folder and create everything in the zip err = filepath.Walk(filepath.Join(config.FactorioModPackDir, modPackName), func(path string, info os.FileInfo, err error) error { if info.IsDir() == false { @@ -205,7 +209,7 @@ func ModPackLoadHandler(w http.ResponseWriter, r *http.Request) { return } - err = modPackMap[modPackName].loadModPack() + err = modPackMap[modPackName].LoadModPack() if err != nil { w.WriteHeader(http.StatusInternalServerError) resp = fmt.Sprintf("Error loading modpack file: %s", err) @@ -213,7 +217,7 @@ func ModPackLoadHandler(w http.ResponseWriter, r *http.Request) { return } - resp = modPackMap[modPackName].Mods.listInstalledMods() + resp = modPackMap[modPackName].Mods.ListInstalledMods() } ////////////////////////////////// @@ -233,7 +237,7 @@ func ModPackModListHandler(w http.ResponseWriter, r *http.Request) { return } - resp = modPackMap[modPackName].Mods.listInstalledMods() + resp = modPackMap[modPackName].Mods.ListInstalledMods() } func ModPackModToggleHandler(w http.ResponseWriter, r *http.Request) { @@ -259,7 +263,7 @@ func ModPackModToggleHandler(w http.ResponseWriter, r *http.Request) { return } - err, resp = packMap[packName].Mods.ModSimpleList.toggleMod(modPackStruct.ModName) + err, resp = packMap[packName].Mods.ModSimpleList.ToggleMod(modPackStruct.ModName) if err != nil { w.WriteHeader(http.StatusInternalServerError) resp = fmt.Sprintf("Error toggling mod inside modPack: %s", err) @@ -291,7 +295,7 @@ func ModPackModDeleteHandler(w http.ResponseWriter, r *http.Request) { return } - err = packMap[packName].Mods.deleteMod(modPackStruct.Name) + err = packMap[packName].Mods.DeleteMod(modPackStruct.Name) if err != nil { w.WriteHeader(http.StatusInternalServerError) resp = fmt.Sprintf("Error deleting mod {%s} in modpack {%s}: %s", modPackStruct.Name, packName, err) @@ -328,7 +332,7 @@ func ModPackModUpdateHandler(w http.ResponseWriter, r *http.Request) { return } - err = packMap[packName].Mods.updateMod(modPackStruct.ModName, modPackStruct.DownloadUrl, modPackStruct.Filename) + err = packMap[packName].Mods.UpdateMod(modPackStruct.ModName, modPackStruct.DownloadUrl, modPackStruct.Filename) if err != nil { w.WriteHeader(http.StatusInternalServerError) @@ -337,7 +341,7 @@ func ModPackModUpdateHandler(w http.ResponseWriter, r *http.Request) { return } - installedMods := packMap[packName].Mods.listInstalledMods().ModsResult + installedMods := packMap[packName].Mods.ListInstalledMods().ModsResult var found = false for _, mod := range installedMods { if mod.Name == modPackStruct.ModName { @@ -368,7 +372,7 @@ func ModPackModDeleteAllHandler(w http.ResponseWriter, r *http.Request) { } // Delete Modpack - err = packMap.deleteModPack(packName) + err = packMap.DeleteModPack(packName) if err != nil { resp = fmt.Sprintf("Error deleting modPackDir: %s", err) log.Println(resp) @@ -376,7 +380,7 @@ func ModPackModDeleteAllHandler(w http.ResponseWriter, r *http.Request) { } // recreate modPack without mods - err = packMap.createEmptyModPack(packName) + err = packMap.CreateEmptyModPack(packName) if err != nil { resp = fmt.Sprintf("Error recreating modPackDir: %s", err) log.Println(resp) @@ -407,7 +411,7 @@ func ModPackModUploadHandler(w http.ResponseWriter, r *http.Request) { err, modPackMap, modPackName := ReadModPackRequest(w, r, &resp) - err = modPackMap[modPackName].Mods.uploadMod(formFile, fileHeader) + err = modPackMap[modPackName].Mods.UploadMod(formFile, fileHeader) if err != nil { resp = fmt.Sprintf("error saving file to modPack: %s", err) log.Println(resp) @@ -415,7 +419,7 @@ func ModPackModUploadHandler(w http.ResponseWriter, r *http.Request) { return } - resp = modPackMap[modPackName].Mods.listInstalledMods() + resp = modPackMap[modPackName].Mods.ListInstalledMods() } func ModPackModPortalInstallHandler(w http.ResponseWriter, r *http.Request) { @@ -444,9 +448,9 @@ func ModPackModPortalInstallHandler(w http.ResponseWriter, r *http.Request) { return } - mods := packMap[packName].Mods + modList := packMap[packName].Mods - err = mods.downloadMod(data.DownloadURL, data.Filename, data.ModName) + err = modList.DownloadMod(data.DownloadURL, data.Filename, data.ModName) if err != nil { resp = fmt.Sprintf("Error downloading a mod: %s", err) log.Println(resp) @@ -454,7 +458,7 @@ func ModPackModPortalInstallHandler(w http.ResponseWriter, r *http.Request) { return } - resp = mods.listInstalledMods() + resp = modList.ListInstalledMods() } func ModPackModPortalInstallMultipleHandler(w http.ResponseWriter, r *http.Request) { @@ -468,8 +472,8 @@ func ModPackModPortalInstallMultipleHandler(w http.ResponseWriter, r *http.Reque w.Header().Set("Content-Type", "application/json;charset=UTF-8") var data []struct { - Name string `json:"name"` - Version Version `json:"version"` + Name string `json:"name"` + Version factorio.Version `json:"version"` } err = ReadFromRequestBody(w, r, &resp, &data) if err != nil { @@ -480,10 +484,9 @@ func ModPackModPortalInstallMultipleHandler(w http.ResponseWriter, r *http.Reque if err != nil { return } - - mods := packMap[packName].Mods + modList := packMap[packName].Mods for _, datum := range data { - details, err, statusCode := modPortalModDetails(datum.Name) + details, err, statusCode := factorio.ModPortalModDetails(datum.Name) if err != nil || statusCode != http.StatusOK { resp = fmt.Sprintf("Error in getting mod details from mod portal: %s", err) log.Println(resp) @@ -493,11 +496,12 @@ func ModPackModPortalInstallMultipleHandler(w http.ResponseWriter, r *http.Reque //find correct mod-version var found = false + for _, release := range details.Releases { if release.Version.Equals(datum.Version) { found = true - err := mods.downloadMod(release.DownloadURL, release.FileName, details.Name) + err := modList.DownloadMod(release.DownloadURL, release.FileName, details.Name) if err != nil { resp = fmt.Sprintf("Error downloading mod {%s}, error: %s", details.Name, err) log.Println(resp) @@ -514,5 +518,5 @@ func ModPackModPortalInstallMultipleHandler(w http.ResponseWriter, r *http.Reque } } - resp = mods.listInstalledMods() + resp = modList.ListInstalledMods() } diff --git a/src/mod_modpack_handler_test.go b/src/api/mod_modpack_handler_test.go similarity index 94% rename from src/mod_modpack_handler_test.go rename to src/api/mod_modpack_handler_test.go index 070368f..55f0acc 100644 --- a/src/mod_modpack_handler_test.go +++ b/src/api/mod_modpack_handler_test.go @@ -1,9 +1,10 @@ -package main +package api import ( "bytes" "encoding/json" "github.com/gorilla/mux" + "github.com/mroote/factorio-server-manager/factorio" "github.com/stretchr/testify/assert" "io" "mime/multipart" @@ -31,14 +32,14 @@ func SetupModPacks(t *testing.T, empty bool, emptyMods bool) { } assert.NoError(t, err, `Error creating "dev_packs/test" directory`) - mods, err := newMods("dev_packs/test") + modList, err := factorio.NewMods("dev_packs/test") assert.NoError(t, err, "error creating mods") if !emptyMods { - err = mods.downloadMod("/download/belt-balancer/5e9f9db4bf9d30000c5303f2", "belt-balancer_2.1.3.zip", "belt-balancer") + err = modList.DownloadMod("/download/belt-balancer/5e9f9db4bf9d30000c5303f2", "belt-balancer_2.1.3.zip", "belt-balancer") assert.NoError(t, err, `Error downloading Mod "belt-balancer"`) - err = mods.downloadMod("/download/train-station-overview/5e8a0a8ee8864f000d0cb022", "train-station-overview_2.0.3.zip", "train-station-overview") + err = modList.DownloadMod("/download/train-station-overview/5e8a0a8ee8864f000d0cb022", "train-station-overview_2.0.3.zip", "train-station-overview") assert.NoError(t, err, `Error downloading Mod "train-station-overview"`) } } @@ -182,16 +183,16 @@ func TestModPackLoadHandler(t *testing.T) { CallRoute(t, method, baseRoute, route, nil, handlerFunc, http.StatusOK, expected) // check if mods are really loaded - packMap, err := newModPackMap() + packMap, err := factorio.NewModPackMap() assert.NoError(t, err, "Error creating modPackMap") - mods, err := newMods("dev") + modList, err := factorio.NewMods("dev") assert.NoError(t, err, "Error creating mods object") packModsJson, err := json.Marshal(packMap["test"].Mods) assert.NoError(t, err, "Error marshalling mods from modPack") - modsJson, err := json.Marshal(mods) + modsJson, err := json.Marshal(modList) assert.NoError(t, err, "Error marshalling mods object") assert.JSONEq(t, string(packModsJson), string(modsJson), "loaded mods and modPack are not identical") @@ -208,16 +209,16 @@ func TestModPackLoadHandler(t *testing.T) { CallRoute(t, method, baseRoute, route, nil, handlerFunc, http.StatusOK, expected) // check if mods are really loaded - packMap, err := newModPackMap() + packMap, err := factorio.NewModPackMap() assert.NoError(t, err, "Error creating modPackMap") - mods, err := newMods("dev") + modList, err := factorio.NewMods("dev") assert.NoError(t, err, "Error creating mods object") packModsJson, err := json.Marshal(packMap["test"].Mods) assert.NoError(t, err, "Error marshalling mods from modPack") - modsJson, err := json.Marshal(mods) + modsJson, err := json.Marshal(modList) assert.NoError(t, err, "Error marshalling mods object") assert.JSONEq(t, string(packModsJson), string(modsJson), "loaded mods and modPack are not identical") @@ -266,7 +267,7 @@ func TestModPackModToggleHandler(t *testing.T) { CallRoute(t, method, baseRoute, route, requestBody, handlerFunc, http.StatusOK, expected) // check if changes happened - packMap, err := newModPackMap() + packMap, err := factorio.NewModPackMap() assert.NoError(t, err, "Error creating modPackMap") found := false @@ -293,7 +294,7 @@ func TestModPackModToggleHandler(t *testing.T) { CallRoute(t, method, baseRoute, route, requestBody, handlerFunc, http.StatusOK, expected) - packMap, err = newModPackMap() + packMap, err = factorio.NewModPackMap() assert.NoError(t, err, "Error creating modPackMap") found = false @@ -338,10 +339,10 @@ func TestModPackModDeleteHandler(t *testing.T) { CallRoute(t, method, baseRoute, route, requestBody, handlerFunc, http.StatusOK, `true`) // check if mod is really not installed anymore - packMap, err := newModPackMap() + packMap, err := factorio.NewModPackMap() assert.NoError(t, err, "Error creating modPackMap") - if packMap["test"].Mods.ModSimpleList.checkModExists("belt-balancer") { + if packMap["test"].Mods.ModSimpleList.CheckModExists("belt-balancer") { t.Fatalf("Mod is still installed, it should be gone by now") } }) @@ -370,7 +371,7 @@ func TestModPackModDeleteAllHandler(t *testing.T) { CallRoute(t, method, baseRoute, route, nil, handlerFunc, http.StatusOK, "true") // check if really empty - packMap, err := newModPackMap() + packMap, err := factorio.NewModPackMap() assert.NoError(t, err, "Error creating modPackMap") if len(packMap["test"].Mods.ModInfoList.Mods) != 0 { @@ -405,10 +406,10 @@ func TestModPackModUpdateHandler(t *testing.T) { defer CleanupModPacks(t) // disable "belt-balancer" mod, so we can test, if it is still deactivated after - packMap, err := newModPackMap() + packMap, err := factorio.NewModPackMap() assert.NoError(t, err, "Error creating modPackMap") - err, _ = packMap["test"].Mods.ModSimpleList.toggleMod("belt-balancer") + err, _ = packMap["test"].Mods.ModSimpleList.ToggleMod("belt-balancer") assert.NoError(t, err, "Error toggling mod") expected := `{"name":"belt-balancer","version":"2.1.2","title":"Belt Balancer","author":"knoxfighter","file_name":"belt-balancer_2.1.2.zip","factorio_version":"0.18.0.0","dependencies":null,"compatibility":true,"enabled":false}` @@ -440,7 +441,7 @@ func TestModPackModUpdateHandler(t *testing.T) { CallRoute(t, method, baseRoute, route, strings.NewReader(requestBody), handlerFunc, http.StatusInternalServerError, "") // check if old mod is still there - packMap, err := newModPackMap() + packMap, err := factorio.NewModPackMap() assert.NoError(t, err, "Error creating modPackMap") var found = false @@ -524,19 +525,19 @@ func TestModPackModUploadHandler(t *testing.T) { } // check if mod is uploaded correctly - packMap, err := newModPackMap() + packMap, err := factorio.NewModPackMap() assert.NoError(t, err, "error creating modPackMap") - expected := ModsResultList{ - ModsResult: []ModsResult{ + expected := factorio.ModsResultList{ + ModsResult: []factorio.ModsResult{ { - ModInfo: ModInfo{ + ModInfo: factorio.ModInfo{ Name: "belt-balancer", Version: "2.1.3", Title: "Belt Balancer", Author: "knoxfighter", FileName: "belt-balancer_2.1.3.zip", - FactorioVersion: Version{0, 18, 0, 0}, + FactorioVersion: factorio.Version{0, 18, 0, 0}, Dependencies: nil, Compatibility: true, }, @@ -545,7 +546,7 @@ func TestModPackModUploadHandler(t *testing.T) { }, } - actual := packMap["test"].Mods.listInstalledMods() + actual := packMap["test"].Mods.ListInstalledMods() assert.Equal(t, expected, actual, `New mod is not correctly installed. expected "%v" - actual "%v"`, expected, actual) }) diff --git a/src/mod_portal_handler.go b/src/api/mod_portal_handler.go similarity index 83% rename from src/mod_portal_handler.go rename to src/api/mod_portal_handler.go index 5439ab5..e723c0d 100644 --- a/src/mod_portal_handler.go +++ b/src/api/mod_portal_handler.go @@ -1,8 +1,9 @@ -package main +package api import ( "fmt" "github.com/gorilla/mux" + "github.com/mroote/factorio-server-manager/factorio" "log" "net/http" ) @@ -18,7 +19,7 @@ func ModPortalListModsHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json;charset=UTF-8") var statusCode int - resp, err, statusCode = modPortalList() + resp, err, statusCode = factorio.ModPortalList() if err != nil { resp = fmt.Sprintf("Error in listing mods from mod portal: %s", err) @@ -45,7 +46,7 @@ func ModPortalModInfoHandler(w http.ResponseWriter, r *http.Request) { modId := vars["mod"] var statusCode int - resp, err, statusCode = modPortalModDetails(modId) + resp, err, statusCode = factorio.ModPortalModDetails(modId) if err != nil { resp = fmt.Sprintf("Error in getting mod details from mod portal: %s", err) @@ -83,7 +84,7 @@ func ModPortalInstallHandler(w http.ResponseWriter, r *http.Request) { return } - err = mods.downloadMod(data.DownloadURL, data.Filename, data.ModName) + err = mods.DownloadMod(data.DownloadURL, data.Filename, data.ModName) if err != nil { resp = fmt.Sprintf("Error downloading a mod: %s", err) log.Println(resp) @@ -91,7 +92,7 @@ func ModPortalInstallHandler(w http.ResponseWriter, r *http.Request) { return } - resp = mods.listInstalledMods() + resp = mods.ListInstalledMods() } func ModPortalLoginHandler(w http.ResponseWriter, r *http.Request) { @@ -113,7 +114,7 @@ func ModPortalLoginHandler(w http.ResponseWriter, r *http.Request) { return } - loginStatus, err, statusCode := factorioLogin(data.Username, data.Password) + loginStatus, err, statusCode := factorio.FactorioLogin(data.Username, data.Password) if err != nil { resp = fmt.Sprintf("Error trying to login into Factorio: %s", err) log.Println(resp) @@ -136,8 +137,8 @@ func ModPortalLoginStatusHandler(w http.ResponseWriter, r *http.Request) { WriteResponse(w, resp) }() - var credentials FactorioCredentials - resp, err = credentials.load() + var credentials factorio.Credentials + resp, err = credentials.Load() if err != nil { resp = fmt.Sprintf("Error getting the factorio credentials: %s", err) @@ -155,8 +156,8 @@ func ModPortalLogoutHandler(w http.ResponseWriter, r *http.Request) { WriteResponse(w, resp) }() - var credentials FactorioCredentials - err = credentials.del() + var credentials factorio.Credentials + err = credentials.Del() if err != nil { resp = fmt.Sprintf("Error on logging out of factorio: %s", err) @@ -179,21 +180,21 @@ func ModPortalInstallMultipleHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json;charset=UTF-8") var data []struct { - Name string `json:"name"` - Version Version `json:"version"` + Name string `json:"name"` + Version factorio.Version `json:"version"` } err = ReadFromRequestBody(w, r, &resp, &data) if err != nil { return } - mods, err := CreateNewMods(w, &resp) + modList, err := CreateNewMods(w, &resp) if err != nil { return } for _, datum := range data { - details, err, statusCode := modPortalModDetails(datum.Name) + details, err, statusCode := factorio.ModPortalModDetails(datum.Name) if err != nil || statusCode != http.StatusOK { resp = fmt.Sprintf("Error in getting mod details from mod portal: %s", err) log.Println(resp) @@ -207,7 +208,7 @@ func ModPortalInstallMultipleHandler(w http.ResponseWriter, r *http.Request) { if release.Version.Equals(datum.Version) { found = true - err := mods.downloadMod(release.DownloadURL, release.FileName, details.Name) + err := modList.DownloadMod(release.DownloadURL, release.FileName, details.Name) if err != nil { resp = fmt.Sprintf("Error downloading mod {%s}, error: %s", details.Name, err) log.Println(resp) @@ -223,5 +224,5 @@ func ModPortalInstallMultipleHandler(w http.ResponseWriter, r *http.Request) { } } - resp = mods.listInstalledMods() + resp = modList.ListInstalledMods() } diff --git a/src/mod_portal_handler_test.go b/src/api/mod_portal_handler_test.go similarity index 99% rename from src/mod_portal_handler_test.go rename to src/api/mod_portal_handler_test.go index 30212ac..d39a40b 100644 --- a/src/mod_portal_handler_test.go +++ b/src/api/mod_portal_handler_test.go @@ -1,4 +1,4 @@ -package main +package api import ( "net/http" diff --git a/src/mods_handler.go b/src/api/mods_handler.go similarity index 84% rename from src/mods_handler.go rename to src/api/mods_handler.go index 9d3a45c..2d83ff9 100644 --- a/src/mods_handler.go +++ b/src/api/mods_handler.go @@ -1,38 +1,22 @@ -package main +package api import ( "archive/zip" "encoding/json" "fmt" + "github.com/mroote/factorio-server-manager/bootstrap" + "github.com/mroote/factorio-server-manager/factorio" "github.com/mroote/factorio-server-manager/lockfile" "io" "log" "net/http" "os" "path/filepath" - "time" ) -type ModPortalStruct struct { - DownloadsCount int `json:"downloads_count"` - Name string `json:"name"` - Owner string `json:"owner"` - Releases []struct { - DownloadURL string `json:"download_url"` - FileName string `json:"file_name"` - InfoJSON struct { - FactorioVersion string `json:"factorio_version"` - } `json:"info_json"` - ReleasedAt time.Time `json:"released_at"` - Sha1 string `json:"sha1"` - Version Version `json:"version"` - } `json:"releases"` - Summary string `json:"summary"` - Title string `json:"title"` -} - -func CreateNewMods(w http.ResponseWriter, resp *interface{}) (mods Mods, err error) { - mods, err = newMods(config.FactorioModsDir) +func CreateNewMods(w http.ResponseWriter, resp *interface{}) (modList factorio.Mods, err error) { + config := bootstrap.GetConfig() + modList, err = factorio.NewMods(config.FactorioModsDir) if err != nil { *resp = fmt.Sprintf("Error creating mods object: %s", err) log.Println(*resp) @@ -69,12 +53,12 @@ func ListInstalledModsHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json;charset=UTF-8") - mods, err := CreateNewMods(w, &resp) + modList, err := CreateNewMods(w, &resp) if err != nil { return } - resp = mods.listInstalledMods().ModsResult + resp = modList.ListInstalledMods().ModsResult } func ModToggleHandler(w http.ResponseWriter, r *http.Request) { @@ -101,7 +85,7 @@ func ModToggleHandler(w http.ResponseWriter, r *http.Request) { return } - err, resp = mods.ModSimpleList.toggleMod(data.Name) + err, resp = mods.ModSimpleList.ToggleMod(data.Name) if err != nil { resp = fmt.Sprintf("Error in toggling mod in simple list: %s", err) log.Println(resp) @@ -130,12 +114,12 @@ func ModDeleteHandler(w http.ResponseWriter, r *http.Request) { return } - mods, err := CreateNewMods(w, &resp) + modList, err := CreateNewMods(w, &resp) if err != nil { return } - err = mods.deleteMod(data.Name) + err = modList.DeleteMod(data.Name) if err != nil { w.WriteHeader(http.StatusInternalServerError) resp = fmt.Sprintf("Error in deleting mod {%s}: %s", data.Name, err) @@ -157,7 +141,7 @@ func ModDeleteAllHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json;charset=UTF-8") //delete mods folder - err = deleteAllMods() + err = factorio.DeleteAllMods() if err != nil { resp = fmt.Sprintf("Error deleting all mods: %s", err) log.Println(resp) @@ -195,14 +179,14 @@ func ModUpdateHandler(w http.ResponseWriter, r *http.Request) { return } - err = mods.updateMod(modData.Name, modData.DownloadUrl, modData.Filename) + err = mods.UpdateMod(modData.Name, modData.DownloadUrl, modData.Filename) if err != nil { resp = fmt.Sprintf("Error updating mod {%s}: %s", modData.Name, err) w.WriteHeader(http.StatusInternalServerError) return } - installedMods := mods.listInstalledMods().ModsResult + installedMods := mods.ListInstalledMods().ModsResult for _, mod := range installedMods { if mod.Name == modData.Name { resp = mod @@ -240,7 +224,7 @@ func ModUploadHandler(w http.ResponseWriter, r *http.Request) { return } - err = mods.uploadMod(formFile, fileHeader) + err = mods.UploadMod(formFile, fileHeader) if err != nil { resp = fmt.Sprintf("error saving file to mods: %s", err) log.Println(resp) @@ -248,7 +232,7 @@ func ModUploadHandler(w http.ResponseWriter, r *http.Request) { return } - resp = mods.listInstalledMods() + resp = mods.ListInstalledMods() } func ModDownloadHandler(w http.ResponseWriter, r *http.Request) { @@ -256,17 +240,17 @@ func ModDownloadHandler(w http.ResponseWriter, r *http.Request) { zipWriter := zip.NewWriter(w) defer zipWriter.Close() - + config := bootstrap.GetConfig() //iterate over folder and create everything in the zip err = filepath.Walk(config.FactorioModsDir, func(path string, info os.FileInfo, err error) error { if info.IsDir() == false { //Lock the file, that we are want to read - err := fileLock.RLock(path) + err := factorio.FileLock.RLock(path) if err != nil { log.Printf("error locking file for reading, something else has locked it") return err } - defer fileLock.RUnlock(path) + defer factorio.FileLock.RUnlock(path) writer, err := zipWriter.Create(info.Name()) if err != nil { @@ -330,9 +314,9 @@ func LoadModsFromSaveHandler(w http.ResponseWriter, r *http.Request) { if err != nil { return } - + config := bootstrap.GetConfig() path := filepath.Join(config.FactorioSavesDir, saveFileStruct.Name) - f, err := OpenArchiveFile(path, "level.dat") + f, err := factorio.OpenArchiveFile(path, "level.dat") if err != nil { w.WriteHeader(http.StatusInternalServerError) resp = fmt.Sprintf("cannot open save level file: %v", err) @@ -342,7 +326,7 @@ func LoadModsFromSaveHandler(w http.ResponseWriter, r *http.Request) { } defer f.Close() - var header SaveHeader + var header factorio.SaveHeader err = header.ReadFrom(f) if err != nil { w.WriteHeader(http.StatusInternalServerError) diff --git a/src/mods_handler_test.go b/src/api/mods_handler_test.go similarity index 88% rename from src/mods_handler_test.go rename to src/api/mods_handler_test.go index 4ae5407..5a54f1e 100644 --- a/src/mods_handler_test.go +++ b/src/api/mods_handler_test.go @@ -1,8 +1,10 @@ -package main +package api import ( "bytes" "github.com/gorilla/mux" + "github.com/mroote/factorio-server-manager/bootstrap" + "github.com/mroote/factorio-server-manager/factorio" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "io" @@ -20,23 +22,27 @@ func TestMain(m *testing.M) { var err error // basic setup stuff - parseFlags() + bootstrap.SetFlags(bootstrap.Flags{ + + }) + + config := bootstrap.GetConfig() config.FactorioModsDir = "dev" config.FactorioModPackDir = "dev_packs" - FactorioServ = new(FactorioServer) - FactorioServ.Version = Version{1, 0, 0, 0} + FactorioServ := new(factorio.Server) + FactorioServ.Version = factorio.Version{1, 0, 0, 0} FactorioServ.BaseModVersion = "1.0.0" // check login status - var cred FactorioCredentials - load, err := cred.load() + var cred factorio.Credentials + load, err := cred.Load() if err != nil { log.Fatalf("Error loading factorio credentials: %s", err) return } if !load { // no credentials found, login... - _, err, _ = factorioLogin(os.Getenv("factorio_username"), os.Getenv("factorio_password")) + _, err, _ = factorio.FactorioLogin(os.Getenv("factorio_username"), os.Getenv("factorio_password")) if err != nil { log.Printf("Error logging in into factorio: %s", err) return @@ -64,18 +70,18 @@ func SetupMods(t *testing.T, empty bool) { return } - mods, err := newMods(config.FactorioModsDir) + mod, err := factorio.NewMods(config.FactorioModsDir) if err != nil { t.Fatalf("couldn't create Mods object: %s", err) } if !empty { - err := mods.downloadMod("/download/belt-balancer/5e9f9db4bf9d30000c5303f2", "belt-balancer_2.1.3.zip", "belt-balancer") + err := mod.DownloadMod("/download/belt-balancer/5e9f9db4bf9d30000c5303f2", "belt-balancer_2.1.3.zip", "belt-balancer") if err != nil { t.Fatalf(`Error downloading Mod "belt-balancer": %s`, err) } - err = mods.downloadMod("/download/train-station-overview/5e8a0a8ee8864f000d0cb022", "train-station-overview_2.0.3.zip", "train-station-overview") + err = mod.DownloadMod("/download/train-station-overview/5e8a0a8ee8864f000d0cb022", "train-station-overview_2.0.3.zip", "train-station-overview") if err != nil { t.Fatalf(`Error downloading Mod "train-station-overview": %s`, err) } @@ -179,12 +185,12 @@ func TestModToggleHandler(t *testing.T) { CallRoute(t, method, route, route, requestBody, handlerFunc, http.StatusOK, expected) // check if changes happenes - mods, err := newMods("dev") + modList, err := factorio.NewMods("dev") if err != nil { t.Fatalf("Error creating Mods object: %s", err) } found := false - for _, mod := range mods.ModSimpleList.Mods { + for _, mod := range modList.ModSimpleList.Mods { if mod.Name == "belt-balancer" { // this mod has to be deactivated now if mod.Enabled { @@ -207,12 +213,12 @@ func TestModToggleHandler(t *testing.T) { CallRoute(t, method, route, route, requestBody, handlerFunc, http.StatusOK, expected) - mods, err = newMods("dev") + modList, err = factorio.NewMods("dev") if err != nil { t.Fatalf("Error creating Mods object: %s", err) } found = false - for _, mod := range mods.ModSimpleList.Mods { + for _, mod := range modList.ModSimpleList.Mods { if mod.Name == "belt-balancer" { // this mod has to be deactivated now if !mod.Enabled { @@ -250,11 +256,11 @@ func TestModDeleteHandler(t *testing.T) { CallRoute(t, method, route, route, requestBody, handlerFunc, http.StatusOK, `"belt-balancer"`) // check if mod is really not installed anymore - mods, err := newMods("dev") + _, err := factorio.NewMods("dev") if err != nil { t.Fatalf("Error creating Mods object: %s", err) } - if mods.ModSimpleList.checkModExists("belt-balancer") { + if factorio.ModSimpleList.CheckModExists("belt-balancer")) { t.Fatalf("Mod is still installed, it should be gone by now") } }) @@ -280,11 +286,11 @@ func TestModDeleteAllHandler(t *testing.T) { CallRoute(t, method, route, route, nil, handlerFunc, http.StatusOK, "null") // check if no mods are there - mods, err := newMods("dev") + modList, err := factorio.NewMods("dev") if err != nil { t.Fatalf("Error creating mods object: %s", err) } - if len(mods.listInstalledMods().ModsResult) != 0 { + if len(modList.ListInstalledMods().ModsResult) != 0 { t.Fatalf("Mods are still there!") } }) @@ -313,11 +319,11 @@ func TestModUpdateHandler(t *testing.T) { defer CleanupMods(t) // disable "belt-balancer" mod, so we can test, if it is still deactivated after - mods, err := newMods("dev") + modList, err := factorio.NewMods("dev") if err != nil { t.Fatalf("Error creating mods object: %s", err) } - err, _ = mods.ModSimpleList.toggleMod("belt-balancer") + err, _ = modList.ModSimpleList.ToggleMod("belt-balancer") if err != nil { t.Fatalf("Error toggling mod: %s", err) } @@ -416,19 +422,19 @@ func TestModUploadHandler(t *testing.T) { } // check if mod is uploaded correctly - mods, err := newMods("dev") + modList, err := factorio.NewMods("dev") assert.NoError(t, err, "error creating mods object") - expected := ModsResultList{ - ModsResult: []ModsResult{ + expected := factorio.ModsResultList{ + ModsResult: []factorio.ModsResult{ { - ModInfo: ModInfo{ + ModInfo: factorio.ModInfo{ Name: "belt-balancer", Version: "2.1.3", Title: "Belt Balancer", Author: "knoxfighter", FileName: "belt-balancer_2.1.3.zip", - FactorioVersion: Version{0, 18, 0, 0}, + FactorioVersion: factorio.Version{0, 18, 0, 0}, Dependencies: nil, Compatibility: true, }, @@ -437,7 +443,7 @@ func TestModUploadHandler(t *testing.T) { }, } - actual := mods.listInstalledMods() + actual := modList.ListInstalledMods() assert.Equal(t, expected, actual, `New mod is not correctly installed. expected "%v" - actual "%v"`, expected, actual) }) diff --git a/src/routes.go b/src/api/routes.go similarity index 97% rename from src/routes.go rename to src/api/routes.go index 6983647..67ef741 100644 --- a/src/routes.go +++ b/src/api/routes.go @@ -1,4 +1,4 @@ -package main +package api import ( "log" @@ -58,9 +58,9 @@ func NewRouter() *mux.Router { Methods("GET"). Name("Websocket"). Handler(AuthorizeHandler(ws)) - ws.Handle("command send", commandSend) - ws.Handle("log subscribe", logSubscribe) - ws.Handle("server status subscribe", serverStatusSubscribe) + ws.Handle("command send", CommandSend) + ws.Handle("log subscribe", LogSubscribe) + ws.Handle("server status subscribe", ServerStatusSubscribe) // Serves the frontend application from the app directory // Uses basic file server to serve index.html and Javascript application @@ -115,6 +115,7 @@ func NewRouter() *mux.Router { // Redirects user to login page if no session is found func AuthorizeHandler(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + Auth := GetAuth() if err := Auth.aaa.Authorize(w, r, true); err != nil { log.Printf("Unauthenticated request %s %s %s", r.Method, r.Host, r.RequestURI) http.Redirect(w, r, "/login", http.StatusSeeOther) diff --git a/src/wsclient.go b/src/api/wsclient.go similarity index 98% rename from src/wsclient.go rename to src/api/wsclient.go index 9e31733..5d94aea 100644 --- a/src/wsclient.go +++ b/src/api/wsclient.go @@ -1,4 +1,4 @@ -package main +package api import ( "github.com/gorilla/websocket" diff --git a/src/wsroutes.go b/src/api/wsroutes.go similarity index 69% rename from src/wsroutes.go rename to src/api/wsroutes.go index 86837b8..4759d3e 100644 --- a/src/wsroutes.go +++ b/src/api/wsroutes.go @@ -1,9 +1,11 @@ -package main +package api import ( + "github.com/mroote/factorio-server-manager/bootstrap" + "github.com/mroote/factorio-server-manager/factorio" "log" "path/filepath" - time "time" + "time" "github.com/hpcloud/tail" ) @@ -18,8 +20,9 @@ func IsClosed(ch <-chan Message) bool { return false } -func logSubscribe(client *Client, data interface{}) { +func LogSubscribe(client *Client, data interface{}) { go func() { + config := bootstrap.GetConfig() logfile := filepath.Join(config.FactorioDir, "factorio-server-console.log") t, err := tail.TailFile(logfile, tail.Config{Follow: true, Poll: true}) if err != nil { @@ -38,12 +41,13 @@ func logSubscribe(client *Client, data interface{}) { }() } -func commandSend(client *Client, data interface{}) { - if FactorioServ.Running { +func CommandSend(client *Client, data interface{}) { + var server, _ = factorio.GetFactorioServer() + if server.Running { go func() { log.Printf("Received command: %v", data) - reqId, err := FactorioServ.Rcon.Write(data.(string)) + reqId, err := server.Rcon.Write(data.(string)) if err != nil { log.Printf("Error sending rcon command: %s", err) return @@ -61,16 +65,16 @@ func commandSend(client *Client, data interface{}) { } } -func serverStatusSubscribe(client *Client, data interface{}) { - +func ServerStatusSubscribe(client *Client, data interface{}) { + var server, _ = factorio.GetFactorioServer() log.Printf("Subcribed to Server Status") go func() { - isRunning := FactorioServ.Running + isRunning := server.Running // always check if status has changed for { - if isRunning != FactorioServ.Running { - isRunning = FactorioServ.Running + if isRunning != server.Running { + isRunning = server.Running log.Printf("Server Status has changed") diff --git a/src/factorio_config.go b/src/factorio/config.go similarity index 62% rename from src/factorio_config.go rename to src/factorio/config.go index 0ce93de..4072035 100644 --- a/src/factorio_config.go +++ b/src/factorio/config.go @@ -1,4 +1,4 @@ -package main +package factorio import ( "log" @@ -6,12 +6,12 @@ import ( "github.com/go-ini/ini" ) -// Loads config.ini file from the factorio config directory -func loadConfig(filename string) (map[string]map[string]string, error) { - log.Printf("Loading config file: %s", filename) +// Loads bootstrap.ini file from the factorio bootstrap directory +func LoadConfig(filename string) (map[string]map[string]string, error) { + log.Printf("Loading bootstrap file: %s", filename) cfg, err := ini.Load(filename) if err != nil { - log.Printf("Error loading config.ini file: %s", err) + log.Printf("Error loading bootstrap.ini file: %s", err) return nil, err } @@ -28,7 +28,7 @@ func loadConfig(filename string) (map[string]map[string]string, error) { result[sectionName] = map[string]string{} result[sectionName] = s.KeysHash() } - log.Printf("Encoding config.ini to JSON") + log.Printf("Encoding bootstrap.ini to JSON") return result, nil } diff --git a/src/factorio_credentials.go b/src/factorio/credentials.go similarity index 75% rename from src/factorio_credentials.go rename to src/factorio/credentials.go index 639bd0f..ede9ad3 100644 --- a/src/factorio_credentials.go +++ b/src/factorio/credentials.go @@ -1,21 +1,22 @@ -package main +package factorio import ( "encoding/json" "errors" + "github.com/mroote/factorio-server-manager/bootstrap" "io/ioutil" "log" "os" ) -type FactorioCredentials struct { +type Credentials struct { Username string `json:"username"` Userkey string `json:"userkey"` } -func (credentials *FactorioCredentials) save() error { +func (credentials *Credentials) Save() error { var err error - + config := bootstrap.GetConfig() credentialsJson, err := json.Marshal(credentials) if err != nil { log.Printf("error mashalling the credentials: %s", err) @@ -31,23 +32,23 @@ func (credentials *FactorioCredentials) save() error { return nil } -func (credentials *FactorioCredentials) load() (bool, error) { +func (credentials *Credentials) Load() (bool, error) { var err error - + config := bootstrap.GetConfig() if _, err := os.Stat(config.FactorioCredentialsFile); os.IsNotExist(err) { return false, nil } fileBytes, err := ioutil.ReadFile(config.FactorioCredentialsFile) if err != nil { - credentials.del() + credentials.Del() log.Printf("error reading CredentialsFile: %s", err) return false, err } err = json.Unmarshal(fileBytes, credentials) if err != nil { - credentials.del() + credentials.Del() log.Printf("error on unmarshal credentials_file: %s", err) return false, err } @@ -55,14 +56,14 @@ func (credentials *FactorioCredentials) load() (bool, error) { if credentials.Userkey != "" && credentials.Username != "" { return true, nil } else { - credentials.del() + credentials.Del() return false, errors.New("incredients incomplete") } } -func (credentials *FactorioCredentials) del() error { +func (credentials *Credentials) Del() error { var err error - + config := bootstrap.GetConfig() err = os.Remove(config.FactorioCredentialsFile) if err != nil { log.Printf("error delete the credentialfile: %s", err) diff --git a/src/gamelog.go b/src/factorio/gamelog.go similarity index 67% rename from src/gamelog.go rename to src/factorio/gamelog.go index a491171..13efa6e 100644 --- a/src/gamelog.go +++ b/src/factorio/gamelog.go @@ -1,14 +1,17 @@ -package main +package factorio import ( + "github.com/mroote/factorio-server-manager/bootstrap" "log" "github.com/hpcloud/tail" ) -func tailLog(filename string) ([]string, error) { +func TailLog(filename string) ([]string, error) { result := []string{} + config := bootstrap.GetConfig() + t, err := tail.TailFile(config.FactorioLog, tail.Config{Follow: false}) if err != nil { log.Printf("Error tailing log %s", err) diff --git a/src/mod_Mods.go b/src/factorio/mod_Mods.go similarity index 88% rename from src/mod_Mods.go rename to src/factorio/mod_Mods.go index bf5d96c..e7191ef 100644 --- a/src/mod_Mods.go +++ b/src/factorio/mod_Mods.go @@ -1,4 +1,4 @@ -package main +package factorio import ( "archive/zip" @@ -26,9 +26,9 @@ type ModsResultList struct { ModsResult []ModsResult `json:"mods"` } -var fileLock lockfile.FileLock = lockfile.NewLock() +var FileLock lockfile.FileLock = lockfile.NewLock() -func newMods(destination string) (Mods, error) { +func NewMods(destination string) (Mods, error) { var err error var mods Mods @@ -47,7 +47,7 @@ func newMods(destination string) (Mods, error) { return mods, nil } -func (mods *Mods) listInstalledMods() ModsResultList { +func (mods *Mods) ListInstalledMods() ModsResultList { result := ModsResultList{make([]ModsResult, 0)} for _, modInfo := range mods.ModInfoList.Mods { @@ -73,7 +73,7 @@ func (mods *Mods) listInstalledMods() ModsResultList { return result } -func (mods *Mods) deleteMod(modName string) error { +func (mods *Mods) DeleteMod(modName string) error { var err error err = mods.ModInfoList.deleteMod(modName) @@ -95,7 +95,7 @@ func (mods *Mods) createMod(modName string, fileName string, fileRc io.Reader) e var err error //check if mod already exists and delete it - if mods.ModSimpleList.checkModExists(modName) { + if mods.ModSimpleList.CheckModExists(modName) { err = mods.ModInfoList.deleteMod(modName) if err != nil { log.Printf("error when deleting mod: %s", err) @@ -118,7 +118,7 @@ func (mods *Mods) createMod(modName string, fileName string, fileRc io.Reader) e } // also add to ModSimpleList if not there yet - if !mods.ModSimpleList.checkModExists(modName) { + if !mods.ModSimpleList.CheckModExists(modName) { err = mods.ModSimpleList.createMod(modName) if err != nil { log.Printf("error creating mod in modSimpleList: %s", err) @@ -129,11 +129,11 @@ func (mods *Mods) createMod(modName string, fileName string, fileRc io.Reader) e return nil } -func (mods *Mods) downloadMod(url string, filename string, modId string) error { +func (mods *Mods) DownloadMod(url string, filename string, modId string) error { var err error - var credentials FactorioCredentials - status, err := credentials.load() + var credentials Credentials + status, err := credentials.Load() if err != nil { log.Printf("error loading credentials: %s", err) return err @@ -175,7 +175,7 @@ func (mods *Mods) downloadMod(url string, filename string, modId string) error { return nil } -func (mods *Mods) uploadMod(file multipart.File, header *multipart.FileHeader) error { +func (mods *Mods) UploadMod(file multipart.File, header *multipart.FileHeader) error { var err error if filepath.Ext(header.Filename) != ".zip" { @@ -211,10 +211,10 @@ func (mods *Mods) uploadMod(file multipart.File, header *multipart.FileHeader) e return nil } -func (mods *Mods) updateMod(modName string, url string, filename string) error { +func (mods *Mods) UpdateMod(modName string, url string, filename string) error { var err error - err = mods.downloadMod(url, filename, modName) + err = mods.DownloadMod(url, filename, modName) if err != nil { log.Printf("updateMod ... error when downloading the new Mod: %s", err) return err diff --git a/src/mod_modInfo.go b/src/factorio/mod_modInfo.go similarity index 93% rename from src/mod_modInfo.go rename to src/factorio/mod_modInfo.go index 6b6a414..31664fd 100644 --- a/src/mod_modInfo.go +++ b/src/factorio/mod_modInfo.go @@ -1,4 +1,4 @@ -package main +package factorio import ( "archive/zip" @@ -50,7 +50,7 @@ func (modInfoList *ModInfoList) listInstalledMods() error { err = filepath.Walk(modInfoList.Destination, func(path string, info os.FileInfo, err error) error { if !info.IsDir() && filepath.Ext(path) == ".zip" { - err = fileLock.RLock(path) + err = FileLock.RLock(path) if err != nil && err == lockfile.ErrorAlreadyLocked { log.Println(err) return nil @@ -58,7 +58,7 @@ func (modInfoList *ModInfoList) listInstalledMods() error { log.Printf("error locking file: %s", err) return err } - defer fileLock.RUnlock(path) + defer FileLock.RUnlock(path) zipFile, err := zip.OpenReader(path) if err != nil { @@ -107,11 +107,13 @@ func (modInfoList *ModInfoList) listInstalledMods() error { break } + server, _ := GetFactorioServer() + if !base.Equals(NilVersion) { - modInfo.Compatibility = FactorioServ.Version.Compare(base, op) + modInfo.Compatibility = server.Version.Compare(base, op) } else { log.Println("error finding basemodDependency. Using FactorioVersion...") - modInfo.Compatibility = !FactorioServ.Version.Less(modInfo.FactorioVersion) + modInfo.Compatibility = !server.Version.Less(modInfo.FactorioVersion) } modInfoList.Mods = append(modInfoList.Mods, modInfo) @@ -136,10 +138,10 @@ func (modInfoList *ModInfoList) deleteMod(modName string) error { if mod.Name == modName { filePath := filepath.Join(modInfoList.Destination, mod.FileName) - fileLock.LockW(filePath) + FileLock.LockW(filePath) //delete mod err = os.Remove(filePath) - fileLock.Unlock(filePath) + FileLock.Unlock(filePath) if err != nil { log.Printf("ModInfoList ... error when deleting mod: %s", err) return err @@ -207,7 +209,7 @@ func (modInfoList *ModInfoList) createMod(modName string, fileName string, modFi } defer newFile.Close() - fileLock.LockW(filePath) + FileLock.LockW(filePath) _, err = io.Copy(newFile, modFile) if err != nil { @@ -221,7 +223,7 @@ func (modInfoList *ModInfoList) createMod(modName string, fileName string, modFi return err } - fileLock.Unlock(filePath) + FileLock.Unlock(filePath) //reload the list err = modInfoList.listInstalledMods() diff --git a/src/mod_modSimple.go b/src/factorio/mod_modSimple.go similarity index 95% rename from src/mod_modSimple.go rename to src/factorio/mod_modSimple.go index 1a935d5..52d39bd 100644 --- a/src/mod_modSimple.go +++ b/src/factorio/mod_modSimple.go @@ -1,4 +1,4 @@ -package main +package factorio import ( "encoding/json" @@ -108,7 +108,7 @@ func (modSimpleList *ModSimpleList) deleteMod(modName string) error { return nil } -func (modSimpleList *ModSimpleList) checkModExists(modName string) bool { +func (modSimpleList *ModSimpleList) CheckModExists(modName string) bool { for _, singleMod := range modSimpleList.Mods { if singleMod.Name == modName { return true @@ -139,7 +139,7 @@ func (modSimpleList *ModSimpleList) createMod(modName string) error { return nil } -func (modSimpleList *ModSimpleList) toggleMod(modName string) (error, bool) { +func (modSimpleList *ModSimpleList) ToggleMod(modName string) (error, bool) { var err error var newEnabled bool diff --git a/src/mod_modpack.go b/src/factorio/mod_modpack.go similarity index 86% rename from src/mod_modpack.go rename to src/factorio/mod_modpack.go index e53daa5..c706282 100644 --- a/src/mod_modpack.go +++ b/src/factorio/mod_modpack.go @@ -1,7 +1,8 @@ -package main +package factorio import ( "errors" + "github.com/mroote/factorio-server-manager/bootstrap" "io" "io/ioutil" "log" @@ -22,7 +23,7 @@ type ModPackResultList struct { ModPacks []ModPackResult `json:"mod_packs"` } -func newModPackMap() (ModPackMap, error) { +func NewModPackMap() (ModPackMap, error) { var err error modPackMap := make(ModPackMap) @@ -39,7 +40,7 @@ func newModPack(modPackFolder string) (*ModPack, error) { var err error var modPack ModPack - modPack.Mods, err = newMods(modPackFolder) + modPack.Mods, err = NewMods(modPackFolder) if err != nil { log.Printf("error on loading mods in mod_pack_dir: %s", err) return &modPack, err @@ -51,6 +52,7 @@ func newModPack(modPackFolder string) (*ModPack, error) { func (modPackMap *ModPackMap) reload() error { var err error newModPackMap := make(ModPackMap) + config := bootstrap.GetConfig() err = filepath.Walk(config.FactorioModPackDir, func(path string, info os.FileInfo, err error) error { if path == config.FactorioModPackDir || !info.IsDir() { @@ -77,13 +79,13 @@ func (modPackMap *ModPackMap) reload() error { return nil } -func (modPackMap *ModPackMap) listInstalledModPacks() ModPackResultList { +func (modPackMap *ModPackMap) ListInstalledModPacks() ModPackResultList { var modPackResultList ModPackResultList for modPackName, modPack := range *modPackMap { var modPackResult ModPackResult modPackResult.Name = modPackName - modPackResult.Mods = modPack.Mods.listInstalledMods() + modPackResult.Mods = modPack.Mods.ListInstalledMods() modPackResultList.ModPacks = append(modPackResultList.ModPacks, modPackResult) } @@ -91,12 +93,12 @@ func (modPackMap *ModPackMap) listInstalledModPacks() ModPackResultList { return modPackResultList } -func (modPackMap *ModPackMap) createModPack(modPackName string) error { +func (modPackMap *ModPackMap) CreateModPack(modPackName string) error { var err error - + config := bootstrap.GetConfig() modPackFolder := filepath.Join(config.FactorioModPackDir, modPackName) - if modPackMap.checkModPackExists(modPackName) == true { + if modPackMap.CheckModPackExists(modPackName) == true { log.Printf("ModPack %s already existis", modPackName) return errors.New("ModPack " + modPackName + " already exists, please choose a different name") } @@ -160,12 +162,12 @@ func (modPackMap *ModPackMap) createModPack(modPackName string) error { return nil } -func (modPackMap *ModPackMap) createEmptyModPack(packName string) error { +func (modPackMap *ModPackMap) CreateEmptyModPack(packName string) error { var err error - + config := bootstrap.GetConfig() modPackFolder := filepath.Join(config.FactorioModPackDir, packName) - if modPackMap.checkModPackExists(packName) == true { + if modPackMap.CheckModPackExists(packName) == true { log.Printf("ModPack %s already existis", packName) return errors.New("ModPack " + packName + " already exists, please choose a different name") } @@ -185,7 +187,7 @@ func (modPackMap *ModPackMap) createEmptyModPack(packName string) error { return nil } -func (modPackMap *ModPackMap) checkModPackExists(modPackName string) bool { +func (modPackMap *ModPackMap) CheckModPackExists(modPackName string) bool { for modPackId := range *modPackMap { if modPackId == modPackName { return true @@ -195,9 +197,9 @@ func (modPackMap *ModPackMap) checkModPackExists(modPackName string) bool { return false } -func (modPackMap *ModPackMap) deleteModPack(modPackName string) error { +func (modPackMap *ModPackMap) DeleteModPack(modPackName string) error { var err error - + config := bootstrap.GetConfig() modPackDir := filepath.Join(config.FactorioModPackDir, modPackName) err = os.RemoveAll(modPackDir) @@ -215,9 +217,9 @@ func (modPackMap *ModPackMap) deleteModPack(modPackName string) error { return nil } -func (modPack *ModPack) loadModPack() error { +func (modPack *ModPack) LoadModPack() error { var err error - + config := bootstrap.GetConfig() //get filemode, so it can be restored fileInfo, err := os.Stat(config.FactorioModsDir) if err != nil { diff --git a/src/mod_portal.go b/src/factorio/mod_portal.go similarity index 73% rename from src/mod_portal.go rename to src/factorio/mod_portal.go index 4aae4b6..5f6aad4 100644 --- a/src/mod_portal.go +++ b/src/factorio/mod_portal.go @@ -1,4 +1,4 @@ -package main +package factorio import ( "encoding/json" @@ -7,10 +7,29 @@ import ( "log" "net/http" "net/url" + "time" ) +type ModPortalStruct struct { + DownloadsCount int `json:"downloads_count"` + Name string `json:"name"` + Owner string `json:"owner"` + Releases []struct { + DownloadURL string `json:"download_url"` + FileName string `json:"file_name"` + InfoJSON struct { + FactorioVersion string `json:"factorio_version"` + } `json:"info_json"` + ReleasedAt time.Time `json:"released_at"` + Sha1 string `json:"sha1"` + Version Version `json:"version"` + } `json:"releases"` + Summary string `json:"summary"` + Title string `json:"title"` +} + // get all mods uploaded to the factorio modPortal -func modPortalList() (interface{}, error, int) { +func ModPortalList() (interface{}, error, int) { req, err := http.NewRequest(http.MethodGet, "https://mods.factorio.com/api/mods?page_size=max", nil) if err != nil { return "error", err, 500 @@ -34,7 +53,7 @@ func modPortalList() (interface{}, error, int) { } // get the details (mod-info, releases, etc.) from a specific mod from the modPortal -func modPortalModDetails(modId string) (ModPortalStruct, error, int) { +func ModPortalModDetails(modId string) (ModPortalStruct, error, int) { var jsonVal ModPortalStruct req, err := http.NewRequest(http.MethodGet, "https://mods.factorio.com/api/mods/"+modId, nil) @@ -59,7 +78,7 @@ func modPortalModDetails(modId string) (ModPortalStruct, error, int) { } //Log the user into factorio, so mods can be downloaded -func factorioLogin(username string, password string) (string, error, int) { +func FactorioLogin(username string, password string) (string, error, int) { var err error resp, err := http.PostForm("https://auth.factorio.com/api-login", @@ -92,12 +111,12 @@ func factorioLogin(username string, password string) (string, error, int) { return err.Error(), err, http.StatusInternalServerError } - credentials := FactorioCredentials{ + credentials := Credentials{ Username: username, Userkey: successResponse[0], } - err = credentials.save() + err = credentials.Save() if err != nil { log.Printf("error saving the credentials. %s", err) return err.Error(), err, http.StatusInternalServerError diff --git a/src/mods.go b/src/factorio/mods.go similarity index 92% rename from src/mods.go rename to src/factorio/mods.go index fe5d798..ebde758 100644 --- a/src/mods.go +++ b/src/factorio/mods.go @@ -1,10 +1,11 @@ -package main +package factorio import ( "archive/zip" "bytes" "encoding/json" "errors" + "github.com/mroote/factorio-server-manager/bootstrap" "io/ioutil" "log" "os" @@ -20,9 +21,9 @@ type LoginSuccessResponse struct { UserKey []string `json:""` } -func deleteAllMods() error { +func DeleteAllMods() error { var err error - + config := bootstrap.GetConfig() modsDirInfo, err := os.Stat(config.FactorioModsDir) if err != nil { log.Printf("error getting stats of FactorioModsDir: %s", err) @@ -46,13 +47,12 @@ func deleteAllMods() error { return nil } -func modStartUp() { - var err error - +func ModStartUp() { + config := bootstrap.GetConfig() //get main-folder info factorioDirInfo, err := os.Stat(config.FactorioDir) if err != nil { - log.Printf("error getting stats from FactorioDir: %s", err) + log.Printf("error getting stats from FactorioDir %s with error %s", config.FactorioDir, err) return } factorioDirPerm := factorioDirInfo.Mode().Perm() @@ -66,7 +66,7 @@ func modStartUp() { //crate mod_pack dir if _, err = os.Stat(config.FactorioModPackDir); os.IsNotExist(err) { log.Println("no ModPackDir found ... creating one ...") - os.Mkdir(config.FactorioModPackDir, factorioDirPerm) + _ = os.Mkdir(config.FactorioModPackDir, factorioDirPerm) } oldModpackDir := filepath.Join(config.FactorioDir, "modpacks") @@ -125,7 +125,7 @@ func modStartUp() { } defer modPackFile.Close() - mods, err := newMods(modPackDir) + mods, err := NewMods(modPackDir) if err != nil { log.Printf("error reading mods: %s", err) return err diff --git a/src/rcon.go b/src/factorio/rcon.go similarity index 61% rename from src/rcon.go rename to src/factorio/rcon.go index 6c0b1d2..2b19ac2 100644 --- a/src/rcon.go +++ b/src/factorio/rcon.go @@ -1,6 +1,7 @@ -package main +package factorio import ( + "github.com/mroote/factorio-server-manager/bootstrap" "log" "strconv" @@ -9,8 +10,10 @@ import ( func connectRC() error { var err error + config := bootstrap.GetConfig() rconAddr := config.ServerIP + ":" + strconv.Itoa(config.FactorioRconPort) - FactorioServ.Rcon, err = rcon.Dial(rconAddr, config.FactorioRconPass) + server, err := GetFactorioServer() + server.Rcon, err = rcon.Dial(rconAddr, config.FactorioRconPass) if err != nil { log.Printf("Cannot create rcon session: %s", err) return err diff --git a/src/factorio_save.go b/src/factorio/save.go similarity index 99% rename from src/factorio_save.go rename to src/factorio/save.go index d61c305..eb487fa 100644 --- a/src/factorio_save.go +++ b/src/factorio/save.go @@ -1,4 +1,4 @@ -package main +package factorio import ( "archive/zip" diff --git a/src/factorio_save_test.go b/src/factorio/save_test.go similarity index 99% rename from src/factorio_save_test.go rename to src/factorio/save_test.go index a0f0d7a..52d5d74 100644 --- a/src/factorio_save_test.go +++ b/src/factorio/save_test.go @@ -1,4 +1,4 @@ -package main +package factorio import ( "testing" diff --git a/src/saves.go b/src/factorio/saves.go similarity index 76% rename from src/saves.go rename to src/factorio/saves.go index 2c59cab..d2ad99c 100644 --- a/src/saves.go +++ b/src/factorio/saves.go @@ -1,8 +1,9 @@ -package main +package factorio import ( "errors" "fmt" + "github.com/mroote/factorio-server-manager/bootstrap" "log" "os" "os/exec" @@ -21,7 +22,7 @@ func (s Save) String() string { } // Lists save files in factorio/saves -func listSaves(saveDir string) (saves []Save, err error) { +func ListSaves(saveDir string) (saves []Save, err error) { saves = []Save{} err = filepath.Walk(saveDir, func(path string, info os.FileInfo, err error) error { if info.IsDir() && info.Name() == "saves" { @@ -37,8 +38,9 @@ func listSaves(saveDir string) (saves []Save, err error) { return } -func findSave(name string) (*Save, error) { - saves, err := listSaves(config.FactorioSavesDir) +func FindSave(name string) (*Save, error) { + config := bootstrap.GetConfig() + saves, err := ListSaves(config.FactorioSavesDir) if err != nil { return nil, fmt.Errorf("error listing saves: %v", err) } @@ -52,16 +54,16 @@ func findSave(name string) (*Save, error) { return nil, errors.New("save not found") } -func (s *Save) remove() error { +func (s *Save) Remove() error { if s.Name == "" { return errors.New("save name cannot be blank") } - + config := bootstrap.GetConfig() return os.Remove(filepath.Join(config.FactorioSavesDir, s.Name)) } // Create savefiles for Factorio -func createSave(filePath string) (string, error) { +func CreateSave(filePath string) (string, error) { err := os.MkdirAll(filepath.Dir(filePath), 0755) if err != nil { log.Printf("Error in creating Factorio save: %s", err) @@ -69,6 +71,7 @@ func createSave(filePath string) (string, error) { } args := []string{"--create", filePath} + config := bootstrap.GetConfig() cmdOutput, err := exec.Command(config.FactorioBinary, args...).Output() if err != nil { log.Printf("Error in creating Factorio save: %s", err) diff --git a/src/factorio_server.go b/src/factorio/server.go similarity index 52% rename from src/factorio_server.go rename to src/factorio/server.go index 1aeac93..5cf21da 100644 --- a/src/factorio_server.go +++ b/src/factorio/server.go @@ -1,24 +1,24 @@ -package main +package factorio import ( "bufio" "encoding/json" - "fmt" + "github.com/mroote/factorio-server-manager/bootstrap" "io" "io/ioutil" "log" - "math/rand" "os" "os/exec" "path/filepath" "regexp" "strconv" "strings" + "sync" "github.com/majormjr/rcon" ) -type FactorioServer struct { +type Server struct { Cmd *exec.Cmd `json:"-"` Savefile string `json:"savefile"` Latency int `json:"latency"` @@ -35,24 +35,22 @@ type FactorioServer struct { LogChan chan []string `json:"-"` } -func randomPort() int { - // Returns random port to use for rcon connection - return rand.Intn(45000-40000) + 40000 -} +var instantiated Server +var once sync.Once -func autostart() { +func (f *Server) autostart() { var err error - if FactorioServ.BindIP == "" { - FactorioServ.BindIP = "0.0.0.0" + if f.BindIP == "" { + f.BindIP = "0.0.0.0" } - if FactorioServ.Port == 0 { - FactorioServ.Port = 34197 + if f.Port == 0 { + f.Port = 34197 } - FactorioServ.Savefile = "Load Latest" + f.Savefile = "Load Latest" - err = FactorioServ.Run() + err = f.Run() if err != nil { log.Printf("Error starting Factorio server: %+v", err) @@ -61,134 +59,146 @@ func autostart() { } -func initFactorio() (f *FactorioServer, err error) { - f = new(FactorioServer) - f.Settings = make(map[string]interface{}) - - if err = os.MkdirAll(config.FactorioConfigDir, 0755); err != nil { - return nil, fmt.Errorf("failed to create config directory: %v", err) - } - - settingsPath := filepath.Join(config.FactorioConfigDir, config.SettingsFile) - var settings *os.File - - if _, err := os.Stat(settingsPath); os.IsNotExist(err) { - // copy example settings to supplied settings file, if not exists - log.Printf("Server settings at %s not found, copying example server settings.\n", settingsPath) - - examplePath := filepath.Join(config.FactorioDir, "data", "server-settings.example.json") - - example, err := os.Open(examplePath) - if err != nil { - return nil, fmt.Errorf("failed to open example server settings: %v", err) - } - defer example.Close() - - settings, err = os.Create(settingsPath) - if err != nil { - return nil, fmt.Errorf("failed to create server settings file: %v", err) - } - defer settings.Close() - - _, err = io.Copy(settings, example) - if err != nil { - return nil, fmt.Errorf("failed to copy example server settings: %v", err) +func GetFactorioServer() (f Server, err error) { + once.Do(func() { + f = Server{} + f.Settings = make(map[string]interface{}) + config := bootstrap.GetConfig() + if err = os.MkdirAll(config.FactorioConfigDir, 0755); err != nil { + log.Printf("failed to create bootstrap directory: %v", err) + return } - err = example.Close() - if err != nil { - return nil, fmt.Errorf("failed to close example server settings: %s", err) - } - } else { - // otherwise, open file normally - settings, err = os.Open(settingsPath) - if err != nil { - return nil, fmt.Errorf("failed to open server settings file: %v", err) - } - defer settings.Close() - } + settingsPath := filepath.Join(config.FactorioConfigDir, config.SettingsFile) + var settings *os.File - // before reading reset offset - if _, err = settings.Seek(0, 0); err != nil { - return nil, fmt.Errorf("error while seeking in settings file: %v", err) - } + if _, err := os.Stat(settingsPath); os.IsNotExist(err) { + // copy example settings to supplied settings file, if not exists + log.Printf("Server settings at %s not found, copying example server settings.\n", settingsPath) - if err = json.NewDecoder(settings).Decode(&f.Settings); err != nil { - return nil, fmt.Errorf("error reading %s: %v", settingsPath, err) - } + examplePath := filepath.Join(config.FactorioDir, "data", "server-settings.example.json") - log.Printf("Loaded Factorio settings from %s\n", settingsPath) + example, err := os.Open(examplePath) + if err != nil { + log.Printf("failed to open example server settings: %v", err) + return + } + defer example.Close() - out := []byte{} - //Load factorio version - if config.glibcCustom == "true" { - out, err = exec.Command(config.glibcLocation, "--library-path", config.glibcLibLoc, config.FactorioBinary, "--version").Output() - } else { - out, err = exec.Command(config.FactorioBinary, "--version").Output() - } + settings, err = os.Create(settingsPath) + if err != nil { + log.Printf("failed to create server settings file: %v", err) + return + } + defer settings.Close() - if err != nil { - log.Printf("error on loading factorio version: %s", err) - return - } + _, err = io.Copy(settings, example) + if err != nil { + log.Printf("failed to copy example server settings: %v", err) + return + } - reg := regexp.MustCompile("Version.*?((\\d+\\.)?(\\d+\\.)?(\\*|\\d+)+)") - found := reg.FindStringSubmatch(string(out)) - err = f.Version.UnmarshalText([]byte(found[1])) - if err != nil { - log.Printf("could not parse version: %v", err) - return - } - - //Load baseMod version - baseModInfoFile := filepath.Join(config.FactorioDir, "data", "base", "info.json") - bmifBa, err := ioutil.ReadFile(baseModInfoFile) - if err != nil { - log.Printf("couldn't open baseMods info.json: %s", err) - return - } - var modInfo ModInfo - err = json.Unmarshal(bmifBa, &modInfo) - if err != nil { - log.Printf("error unmarshalling baseMods info.json to a modInfo: %s", err) - return - } - - f.BaseModVersion = modInfo.Version - - // load admins from additional file - if (f.Version.Greater(Version{0, 17, 0})) { - if _, err := os.Stat(filepath.Join(config.FactorioConfigDir, config.FactorioAdminFile)); os.IsNotExist(err) { - //save empty admins-file - ioutil.WriteFile(filepath.Join(config.FactorioConfigDir, config.FactorioAdminFile), []byte("[]"), 0664) + err = example.Close() + if err != nil { + log.Printf("failed to close example server settings: %s", err) + return + } } else { - data, err := ioutil.ReadFile(filepath.Join(config.FactorioConfigDir, config.FactorioAdminFile)) + // otherwise, open file normally + settings, err = os.Open(settingsPath) if err != nil { - log.Printf("Error loading FactorioAdminFile: %s", err) - return f, err + log.Printf("failed to open server settings file: %v", err) + return } - - var jsonData interface{} - err = json.Unmarshal(data, &jsonData) - if err != nil { - log.Printf("Error unmarshalling FactorioAdminFile: %s", err) - return f, err - } - - f.Settings["admins"] = jsonData + defer settings.Close() } - } - if config.autostart == "true" { - go autostart() - } + // before reading reset offset + if _, err = settings.Seek(0, 0); err != nil { + log.Printf("error while seeking in settings file: %v", err) + return + } - return + if err = json.NewDecoder(settings).Decode(&f.Settings); err != nil { + log.Printf("error reading %s: %v", settingsPath, err) + return + } + + log.Printf("Loaded Factorio settings from %s\n", settingsPath) + + out := []byte{} + //Load factorio version + if config.GlibcCustom == "true" { + out, err = exec.Command(config.GlibcLocation, "--library-path", config.GlibcLibLoc, config.FactorioBinary, "--version").Output() + } else { + out, err = exec.Command(config.FactorioBinary, "--version").Output() + } + + if err != nil { + log.Printf("error on loading factorio version: %s", err) + return + } + + reg := regexp.MustCompile("Version.*?((\\d+\\.)?(\\d+\\.)?(\\*|\\d+)+)") + found := reg.FindStringSubmatch(string(out)) + err = f.Version.UnmarshalText([]byte(found[1])) + if err != nil { + log.Printf("could not parse version: %v", err) + return + } + + //Load baseMod version + baseModInfoFile := filepath.Join(config.FactorioDir, "data", "base", "info.json") + bmifBa, err := ioutil.ReadFile(baseModInfoFile) + if err != nil { + log.Printf("couldn't open baseMods info.json: %s", err) + return + } + var modInfo ModInfo + err = json.Unmarshal(bmifBa, &modInfo) + if err != nil { + log.Printf("error unmarshalling baseMods info.json to a modInfo: %s", err) + return + } + + f.BaseModVersion = modInfo.Version + + // load admins from additional file + if (f.Version.Greater(Version{0, 17, 0})) { + if _, err := os.Stat(filepath.Join(config.FactorioConfigDir, config.FactorioAdminFile)); os.IsNotExist(err) { + //save empty admins-file + _ = ioutil.WriteFile(filepath.Join(config.FactorioConfigDir, config.FactorioAdminFile), []byte("[]"), 0664) + } else { + data, err := ioutil.ReadFile(filepath.Join(config.FactorioConfigDir, config.FactorioAdminFile)) + if err != nil { + log.Printf("Error loading FactorioAdminFile: %s", err) + return + } + + var jsonData interface{} + err = json.Unmarshal(data, &jsonData) + if err != nil { + log.Printf("Error unmarshalling FactorioAdminFile: %s", err) + return + } + + f.Settings["admins"] = jsonData + } + } + + instantiated = f + + if config.Autostart == "true" { + go instantiated.autostart() + } + }) + + return instantiated, nil } -func (f *FactorioServer) Run() error { +func (f *Server) Run() error { var err error - + config := bootstrap.GetConfig() data, err := json.MarshalIndent(f.Settings, "", " ") if err != nil { log.Println("Failed to marshal FactorioServerSettings: ", err) @@ -200,9 +210,9 @@ func (f *FactorioServer) Run() error { //The factorio server refenences its executable-path, since we execute the ld.so file and pass the factorio binary as a parameter //the game would use the path to the ld.so file as it's executable path and crash, to prevent this the parameter "--executable-path" is added - if config.glibcCustom == "true" { - log.Println("Custom glibc selected, glibc.so location:", config.glibcLocation, " lib location:", config.glibcLibLoc) - args = append(args, "--library-path", config.glibcLibLoc, config.FactorioBinary, "--executable-path", config.FactorioBinary) + if config.GlibcCustom == "true" { + log.Println("Custom glibc selected, glibc.so location:", config.GlibcLocation, " lib location:", config.GlibcLibLoc) + args = append(args, "--library-path", config.GlibcLibLoc, config.FactorioBinary, "--executable-path", config.FactorioBinary) } args = append(args, @@ -222,9 +232,9 @@ func (f *FactorioServer) Run() error { args = append(args, "--start-server", filepath.Join(config.FactorioSavesDir, f.Savefile)) } - if config.glibcCustom == "true" { - log.Println("Starting server with command: ", config.glibcLocation, args) - f.Cmd = exec.Command(config.glibcLocation, args...) + if config.GlibcCustom == "true" { + log.Println("Starting server with command: ", config.GlibcLocation, args) + f.Cmd = exec.Command(config.GlibcLocation, args...) } else { log.Println("Starting server with command: ", config.FactorioBinary, args) f.Cmd = exec.Command(config.FactorioBinary, args...) @@ -268,7 +278,7 @@ func (f *FactorioServer) Run() error { return nil } -func (f *FactorioServer) parseRunningCommand(std io.ReadCloser) (err error) { +func (f *Server) parseRunningCommand(std io.ReadCloser) (err error) { stdScanner := bufio.NewScanner(std) for stdScanner.Scan() { log.Printf("Factorio Server: %s", stdScanner.Text()) @@ -308,7 +318,8 @@ func (f *FactorioServer) parseRunningCommand(std io.ReadCloser) (err error) { return nil } -func (f *FactorioServer) writeLog(logline string) error { +func (f *Server) writeLog(logline string) error { + config := bootstrap.GetConfig() logfileName := filepath.Join(config.FactorioDir, "factorio-server-console.log") file, err := os.OpenFile(logfileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) if err != nil { @@ -327,7 +338,7 @@ func (f *FactorioServer) writeLog(logline string) error { return nil } -func (f *FactorioServer) checkLogError(logline []string) error { +func (f *Server) checkLogError(logline []string) error { // TODO Handle errors generated by running Factorio Server log.Println(logline) diff --git a/src/factorio_server_linux.go b/src/factorio/server_linux.go similarity index 91% rename from src/factorio_server_linux.go rename to src/factorio/server_linux.go index 98e8980..5c41a1b 100644 --- a/src/factorio_server_linux.go +++ b/src/factorio/server_linux.go @@ -1,7 +1,7 @@ // use this file only when compiling not windows (all unix systems) // +build !windows -package main +package factorio import ( "log" @@ -10,7 +10,7 @@ import ( // Stubs for windows-only functions -func (f *FactorioServer) Kill() error { +func (f *Server) Kill() error { err := f.Cmd.Process.Signal(os.Kill) if err != nil { if err.Error() == "os: process already finished" { @@ -31,7 +31,7 @@ func (f *FactorioServer) Kill() error { return nil } -func (f *FactorioServer) Stop() error { +func (f *Server) Stop() error { err := f.Cmd.Process.Signal(os.Interrupt) if err != nil { if err.Error() == "os: process already finished" { diff --git a/src/factorio_server_windows.go b/src/factorio/server_windows.go similarity index 95% rename from src/factorio_server_windows.go rename to src/factorio/server_windows.go index 7e5a38d..7ef163e 100644 --- a/src/factorio_server_windows.go +++ b/src/factorio/server_windows.go @@ -1,4 +1,4 @@ -package main +package factorio import ( "log" @@ -42,7 +42,7 @@ func setCtrlHandlingIsDisabledForThisProcess(disabled bool) { } } -func (f *FactorioServer) Kill() error { +func (f *Server) Kill() error { err := f.Cmd.Process.Signal(os.Kill) if err != nil { if err.Error() == "os: process already finished" { @@ -58,7 +58,7 @@ func (f *FactorioServer) Kill() error { return nil } -func (f *FactorioServer) Stop() error { +func (f *Server) Stop() error { // Disable our own handling of CTRL+C, so we don't close when we send it to the console. setCtrlHandlingIsDisabledForThisProcess(true) diff --git a/src/version.go b/src/factorio/version.go similarity index 99% rename from src/version.go rename to src/factorio/version.go index 927f398..ba69e4b 100644 --- a/src/version.go +++ b/src/factorio/version.go @@ -1,4 +1,4 @@ -package main +package factorio import ( "encoding/binary" diff --git a/src/main.go b/src/main.go index 61de59a..76ff913 100644 --- a/src/main.go +++ b/src/main.go @@ -1,140 +1,69 @@ package main import ( - "encoding/json" "flag" - "fmt" + "github.com/mroote/factorio-server-manager/api" + "github.com/mroote/factorio-server-manager/bootstrap" + "github.com/mroote/factorio-server-manager/factorio" "log" "net/http" - "os" - "path/filepath" - "runtime" ) -type Config struct { - FactorioDir string `json:"factorio_dir"` - FactorioSavesDir string `json:"saves_dir"` - FactorioModsDir string `json:"mods_dir"` - FactorioModPackDir string `json:"mod_pack_dir"` - FactorioConfigFile string `json:"config_file"` - FactorioConfigDir string `json:"config_directory"` - FactorioLog string `json:"logfile"` - FactorioBinary string `json:"factorio_binary"` - FactorioRconPort int `json:"rcon_port"` - FactorioRconPass string `json:"rcon_pass"` - FactorioCredentialsFile string `json:"factorio_credentials_file"` - FactorioIP string `json:"factorio_ip"` - FactorioAdminFile string `json:"-"` - ServerIP string `json:"server_ip"` - ServerPort string `json:"server_port"` - MaxUploadSize int64 `json:"max_upload_size"` - Username string `json:"username"` - Password string `json:"password"` - DatabaseFile string `json:"database_file"` - CookieEncryptionKey string `json:"cookie_encryption_key"` - SettingsFile string `json:"settings_file"` - LogFile string `json:"log_file"` - ConfFile string - glibcCustom string - glibcLocation string - glibcLibLoc string - autostart string -} +func main() { + // parse command flags and pass them to bootstrap + flags := parseFlags() + bootstrap.SetFlags(flags) -var ( - config Config - FactorioServ *FactorioServer - Auth *AuthHTTP -) + // get the all configs based on the flags + config := bootstrap.GetConfig() -func failOnError(err error, msg string) { + // setup the required files for the mods + factorio.ModStartUp() + + // Initialize Factorio Server struct + _, err := factorio.GetFactorioServer() if err != nil { - log.Printf("%s: %s", msg, err) - panic(fmt.Sprintf("%s: %s", msg, err)) + log.Printf("Error occurred during Server initializaion: %v\n", err) + return } + + // Initialize authentication system + api.GetAuth() + + // Initialize HTTP router + router := api.NewRouter() + log.Printf("Starting server on: %s:%s", config.ServerIP, config.ServerPort) + log.Fatal(http.ListenAndServe(config.ServerIP+":"+config.ServerPort, router)) + } -// Loads server configuration files -// JSON config file contains default values, -// config file will overwrite any provided flags -func loadServerConfig(f string) { - file, err := os.Open(f) - failOnError(err, "Error loading config file.") - - decoder := json.NewDecoder(file) - err = decoder.Decode(&config) - failOnError(err, "Error decoding JSON config file.") - - config.FactorioRconPort = randomPort() -} - -func parseFlags() { - confFile := flag.String("conf", "./conf.json", "Specify location of Factorio Server Manager config file.") +func parseFlags() bootstrap.Flags { + confFile := flag.String("conf", "./conf.json", "Specify location of Factorio Server Manager bootstrap file.") factorioDir := flag.String("dir", "./", "Specify location of Factorio directory.") serverIP := flag.String("host", "0.0.0.0", "Specify IP for webserver to listen on.") factorioIP := flag.String("game-bind-address", "0.0.0.0", "Specify IP for Fcatorio gamer server to listen on.") factorioPort := flag.String("port", "8080", "Specify a port for the server.") - factorioConfigFile := flag.String("config", "config/config.ini", "Specify location of Factorio config.ini file") + factorioConfigFile := flag.String("bootstrap", "bootstrap/bootstrap.ini", "Specify location of Factorio bootstrap.ini file") factorioMaxUpload := flag.Int64("max-upload", 1024*1024*20, "Maximum filesize for uploaded files (default 20MB).") factorioBinary := flag.String("bin", "bin/x64/factorio", "Location of Factorio Server binary file") glibcCustom := flag.String("glibc-custom", "false", "By default false, if custom glibc is required set this to true and add glibc-loc and glibc-lib-loc parameters") glibcLocation := flag.String("glibc-loc", "/opt/glibc-2.18/lib/ld-2.18.so", "Location glibc ld.so file if needed (ex. /opt/glibc-2.18/lib/ld-2.18.so)") glibcLibLoc := flag.String("glibc-lib-loc", "/opt/glibc-2.18/lib", "Location of glibc lib folder (ex. /opt/glibc-2.18/lib)") autostart := flag.String("autostart", "false", "Autostart factorio server on bootup of FSM, default false [true/false]") - flag.Parse() - config.autostart = *autostart - config.glibcCustom = *glibcCustom - config.glibcLocation = *glibcLocation - config.glibcLibLoc = *glibcLibLoc - config.ConfFile = *confFile - config.FactorioDir = *factorioDir - config.ServerIP = *serverIP - config.FactorioIP = *factorioIP - config.ServerPort = *factorioPort - config.FactorioSavesDir = filepath.Join(config.FactorioDir, "saves") - config.FactorioModsDir = filepath.Join(config.FactorioDir, "mods") - config.FactorioModPackDir = "./mod_packs" - config.FactorioConfigDir = filepath.Join(config.FactorioDir, "config") - config.FactorioConfigFile = filepath.Join(config.FactorioDir, *factorioConfigFile) - config.FactorioBinary = filepath.Join(config.FactorioDir, *factorioBinary) - config.FactorioCredentialsFile = "./factorio.auth" - config.FactorioAdminFile = "server-adminlist.json" - config.MaxUploadSize = *factorioMaxUpload - if runtime.GOOS == "windows" { - appdata := os.Getenv("APPDATA") - config.FactorioLog = filepath.Join(appdata, "Factorio", "factorio-current.log") - } else { - config.FactorioLog = filepath.Join(config.FactorioDir, "factorio-current.log") + return bootstrap.Flags{ + ConfFile: confFile, + FactorioDir: factorioDir, + ServerIP: serverIP, + FactorioIP: factorioIP, + FactorioPort: factorioPort, + FactorioConfigFile: factorioConfigFile, + FactorioMaxUpload: factorioMaxUpload, + FactorioBinary: factorioBinary, + GlibcCustom: glibcCustom, + GlibcLocation: glibcLocation, + GlibcLibLoc: glibcLibLoc, + Autostart: autostart, } } - -func main() { - var err error - - // Parse configuration flags - parseFlags() - // Load server config from file - loadServerConfig(config.ConfFile) - // create mod-stuff - modStartUp() - - // Initialize Factorio Server struct - FactorioServ, err = initFactorio() - if err != nil { - log.Printf("Error occurred during FactorioServer initializaion: %v\n", err) - return - } - - // Initialize authentication system - Auth = initAuth() - Auth.CreateAuth(config.DatabaseFile, config.CookieEncryptionKey) - Auth.CreateOrUpdateUser(config.Username, config.Password, "admin", "") - - // Initialize HTTP router - router := NewRouter() - log.Printf("Starting server on: %s:%s", config.ServerIP, config.ServerPort) - log.Fatal(http.ListenAndServe(config.ServerIP+":"+config.ServerPort, router)) - -}