This commit is contained in:
Jan Naahs
2020-09-28 23:54:43 +02:00
parent 9f0686a09e
commit ad9ffadc74
29 changed files with 533 additions and 544 deletions

View File

@ -1,8 +1,10 @@
package main package api
import ( import (
"github.com/mroote/factorio-server-manager/bootstrap"
"log" "log"
"os" "os"
"sync"
"github.com/apexskier/httpauth" "github.com/apexskier/httpauth"
) )
@ -19,8 +21,18 @@ type User struct {
Email string `json:"email"` Email string `json:"email"`
} }
func initAuth() *AuthHTTP { var once sync.Once
return &AuthHTTP{} 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 { func (auth *AuthHTTP) CreateAuth(backendFile string, cookieKey string) error {

View File

@ -1,9 +1,11 @@
package main package api
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/mroote/factorio-server-manager/bootstrap"
"github.com/mroote/factorio-server-manager/factorio"
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
@ -52,14 +54,14 @@ func ReadRequestBody(w http.ResponseWriter, r *http.Request, resp *interface{})
// Lists all save files in the factorio/saves directory // Lists all save files in the factorio/saves directory
func ListSaves(w http.ResponseWriter, r *http.Request) { func ListSaves(w http.ResponseWriter, r *http.Request) {
var resp interface{} var resp interface{}
config := bootstrap.GetConfig()
defer func() { defer func() {
WriteResponse(w, resp) WriteResponse(w, resp)
}() }()
w.Header().Set("Content-Type", "application/json;charset=UTF-8") w.Header().Set("Content-Type", "application/json;charset=UTF-8")
savesList, err := listSaves(config.FactorioSavesDir) savesList, err := factorio.ListSaves(config.FactorioSavesDir)
if err != nil { if err != nil {
resp = fmt.Sprintf("Error listing save files: %s", err) resp = fmt.Sprintf("Error listing save files: %s", err)
log.Println(resp) log.Println(resp)
@ -67,7 +69,7 @@ func ListSaves(w http.ResponseWriter, r *http.Request) {
return return
} }
loadLatest := Save{Name: "Load Latest"} loadLatest := factorio.Save{Name: "Load Latest"}
savesList = append(savesList, loadLatest) savesList = append(savesList, loadLatest)
resp = savesList resp = savesList
@ -75,7 +77,7 @@ func ListSaves(w http.ResponseWriter, r *http.Request) {
func DLSave(w http.ResponseWriter, r *http.Request) { func DLSave(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Type", "application/octet-stream")
config := bootstrap.GetConfig()
vars := mux.Vars(r) vars := mux.Vars(r)
save := vars["save"] save := vars["save"]
saveName := filepath.Join(config.FactorioSavesDir, save) saveName := filepath.Join(config.FactorioSavesDir, save)
@ -96,6 +98,7 @@ func UploadSave(w http.ResponseWriter, r *http.Request) {
log.Println("Uploading save file") log.Println("Uploading save file")
r.ParseMultipartForm(32 << 20) r.ParseMultipartForm(32 << 20)
config := bootstrap.GetConfig()
for _, saveFile := range r.MultipartForm.File["savefile"] { for _, saveFile := range r.MultipartForm.File["savefile"] {
ext := filepath.Ext(saveFile.Filename) ext := filepath.Ext(saveFile.Filename)
@ -149,7 +152,7 @@ func RemoveSave(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
name := vars["save"] name := vars["save"]
save, err := findSave(name) save, err := factorio.FindSave(name)
if err != nil { if err != nil {
resp = fmt.Sprintf("Error finding save {%s}: %s", name, err) resp = fmt.Sprintf("Error finding save {%s}: %s", name, err)
log.Println(resp) log.Println(resp)
@ -157,7 +160,7 @@ func RemoveSave(w http.ResponseWriter, r *http.Request) {
return return
} }
err = save.remove() err = save.Remove()
if err != nil { if err != nil {
resp = fmt.Sprintf("Error removing save {%s}: %s", name, err) resp = fmt.Sprintf("Error removing save {%s}: %s", name, err)
log.Println(resp) log.Println(resp)
@ -188,9 +191,9 @@ func CreateSaveHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return return
} }
config := bootstrap.GetConfig()
saveFile := filepath.Join(config.FactorioSavesDir, saveName) saveFile := filepath.Join(config.FactorioSavesDir, saveName)
cmdOut, err := createSave(saveFile) cmdOut, err := factorio.CreateSave(saveFile)
if err != nil { if err != nil {
resp = fmt.Sprintf("Error creating save {%s}: %s", saveName, err) resp = fmt.Sprintf("Error creating save {%s}: %s", saveName, err)
log.Println(resp) 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") w.Header().Set("Content-Type", "application/json;charset=UTF-8")
config := bootstrap.GetConfig()
resp, err = tailLog(config.FactorioLog) resp, err = factorio.TailLog(config.FactorioLog)
if err != nil { if err != nil {
resp = fmt.Sprintf("Could not tail %s: %s", config.FactorioLog, err) resp = fmt.Sprintf("Could not tail %s: %s", config.FactorioLog, err)
return return
@ -229,8 +232,8 @@ func LoadConfig(w http.ResponseWriter, r *http.Request) {
}() }()
w.Header().Set("Content-Type", "application/json;charset=UTF-8") w.Header().Set("Content-Type", "application/json;charset=UTF-8")
config := bootstrap.GetConfig()
configContents, err := loadConfig(config.FactorioConfigFile) configContents, err := factorio.LoadConfig(config.FactorioConfigFile)
if err != nil { if err != nil {
resp = fmt.Sprintf("Could not retrieve config.ini: %s", err) resp = fmt.Sprintf("Could not retrieve config.ini: %s", err)
log.Println(resp) log.Println(resp)
@ -246,14 +249,14 @@ func LoadConfig(w http.ResponseWriter, r *http.Request) {
func StartServer(w http.ResponseWriter, r *http.Request) { func StartServer(w http.ResponseWriter, r *http.Request) {
var err error var err error
var resp interface{} var resp interface{}
var server, _ = factorio.GetFactorioServer()
defer func() { defer func() {
WriteResponse(w, resp) WriteResponse(w, resp)
}() }()
w.Header().Set("Content-Type", "application/json;charset=UTF-8") w.Header().Set("Content-Type", "application/json;charset=UTF-8")
if FactorioServ.Running { if server.Running {
resp = "Factorio server is already running" resp = "Factorio server is already running"
w.WriteHeader(http.StatusConflict) w.WriteHeader(http.StatusConflict)
return return
@ -268,7 +271,7 @@ func StartServer(w http.ResponseWriter, r *http.Request) {
log.Printf("Starting Factorio server with settings: %v", string(body)) log.Printf("Starting Factorio server with settings: %v", string(body))
err = json.Unmarshal(body, &FactorioServ) err = json.Unmarshal(body, &server)
if err != nil { if err != nil {
resp = fmt.Sprintf("Error unmarshalling server settings JSON: %s", err) resp = fmt.Sprintf("Error unmarshalling server settings JSON: %s", err)
log.Println(resp) 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. // 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" resp = "Error starting Factorio server: No save file provided"
log.Println(resp) log.Println(resp)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
@ -285,7 +288,7 @@ func StartServer(w http.ResponseWriter, r *http.Request) {
} }
go func() { go func() {
err = FactorioServ.Run() err = server.Run()
if err != nil { if err != nil {
log.Printf("Error starting Factorio server: %+v", err) log.Printf("Error starting Factorio server: %+v", err)
return return
@ -295,7 +298,7 @@ func StartServer(w http.ResponseWriter, r *http.Request) {
timeout := 0 timeout := 0
for timeout <= 3 { for timeout <= 3 {
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
if FactorioServ.Running { if server.Running {
break break
} else { } else {
log.Printf("Did not detect running Factorio server attempt: %+v", timeout) log.Printf("Did not detect running Factorio server attempt: %+v", timeout)
@ -304,14 +307,14 @@ func StartServer(w http.ResponseWriter, r *http.Request) {
timeout++ timeout++
} }
if FactorioServ.Running == false { if server.Running == false {
resp = fmt.Sprintf("Error starting Factorio server: %s", err) resp = fmt.Sprintf("Error starting Factorio server: %s", err)
log.Println(resp) log.Println(resp)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
return 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) 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") w.Header().Set("Content-Type", "application/json;charset=UTF-8")
var server, _ = factorio.GetFactorioServer()
if FactorioServ.Running { if server.Running {
err := FactorioServ.Stop() err := server.Stop()
if err != nil { if err != nil {
resp = fmt.Sprintf("Error stopping factorio server: %s", err) resp = fmt.Sprintf("Error stopping factorio server: %s", err)
log.Println(resp) 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") w.Header().Set("Content-Type", "application/json;charset=UTF-8")
var server, _ = factorio.GetFactorioServer()
if FactorioServ.Running { if server.Running {
err := FactorioServ.Kill() err := server.Kill()
if err != nil { if err != nil {
resp = fmt.Sprintf("Error killing factorio server: %s", err) resp = fmt.Sprintf("Error killing factorio server: %s", err)
log.Println(resp) 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") w.Header().Set("Content-Type", "application/json;charset=UTF-8")
var server, _ = factorio.GetFactorioServer()
if FactorioServ.Running { if server.Running {
resp["status"] = "running" resp["status"] = "running"
resp["port"] = strconv.Itoa(FactorioServ.Port) resp["port"] = strconv.Itoa(server.Port)
resp["savefile"] = FactorioServ.Savefile resp["savefile"] = server.Savefile
resp["address"] = FactorioServ.BindIP resp["address"] = server.BindIP
} else { } else {
resp["status"] = "stopped" 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") w.Header().Set("Content-Type", "application/json;charset=UTF-8")
var server, _ = factorio.GetFactorioServer()
resp["version"] = FactorioServ.Version.String() resp["version"] = server.Version.String()
resp["base_mod_version"] = FactorioServ.BaseModVersion resp["base_mod_version"] = server.BaseModVersion
} }
// Unmarshall the User object from the given bytearray // 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) log.Printf("Logging in user: %s", user.Username)
Auth := GetAuth()
err = Auth.aaa.Login(w, r, user.Username, user.Password, "/") err = Auth.aaa.Login(w, r, user.Username, user.Password, "/")
if err != nil { if err != nil {
resp = fmt.Sprintf("Error loggin in user: %s, error: %s", user.Username, err) 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") w.Header().Set("Content-Type", "application/json;charset=UTF-8")
Auth := GetAuth()
if err = Auth.aaa.Logout(w, r); err != nil { if err = Auth.aaa.Logout(w, r); err != nil {
log.Printf("Error logging out current user") log.Printf("Error logging out current user")
w.WriteHeader(http.StatusInternalServerError) 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") w.Header().Set("Content-Type", "application/json;charset=UTF-8")
Auth := GetAuth()
user, err := Auth.aaa.CurrentUser(w, r) user, err := Auth.aaa.CurrentUser(w, r)
if err != nil { if err != nil {
resp = fmt.Sprintf("Error getting user status: %s, error: %s", user.Username, err) 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") w.Header().Set("Content-Type", "application/json;charset=UTF-8")
Auth := GetAuth()
users, err := Auth.listUsers() users, err := Auth.listUsers()
if err != nil { if err != nil {
resp = fmt.Sprintf("Error listing users: %s", err) resp = fmt.Sprintf("Error listing users: %s", err)
@ -524,7 +527,7 @@ func AddUser(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
return return
} }
Auth := GetAuth()
err = Auth.addUser(user.Username, user.Password, user.Email, user.Role) err = Auth.addUser(user.Username, user.Password, user.Email, user.Role)
if err != nil { if err != nil {
resp = fmt.Sprintf("Error in adding user {%s}: %s", user.Username, err) 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 { if err != nil {
return return
} }
Auth := GetAuth()
err = Auth.removeUser(user.Username) err = Auth.removeUser(user.Username)
if err != nil { if err != nil {
resp = fmt.Sprintf("Error in removing user {%s}, error: %s", user.Username, err) 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") w.Header().Set("Content-Type", "application/json;charset=UTF-8")
var server, _ = factorio.GetFactorioServer()
resp = FactorioServ.Settings resp = server.Settings
log.Printf("Sent server settings response") log.Printf("Sent server settings response")
} }
@ -594,8 +597,8 @@ func UpdateServerSettings(w http.ResponseWriter, r *http.Request) {
return return
} }
log.Printf("Received settings JSON: %s", body) log.Printf("Received settings JSON: %s", body)
var server, _ = factorio.GetFactorioServer()
err = json.Unmarshal(body, &FactorioServ.Settings) err = json.Unmarshal(body, &server.Settings)
if err != nil { if err != nil {
resp = fmt.Sprintf("Error unmarhaling server settings JSON: %s", err) resp = fmt.Sprintf("Error unmarhaling server settings JSON: %s", err)
log.Println(resp) log.Println(resp)
@ -603,14 +606,14 @@ func UpdateServerSettings(w http.ResponseWriter, r *http.Request) {
return return
} }
settings, err := json.MarshalIndent(&FactorioServ.Settings, "", " ") settings, err := json.MarshalIndent(&server.Settings, "", " ")
if err != nil { if err != nil {
resp = fmt.Sprintf("Failed to marshal server settings: %s", err) resp = fmt.Sprintf("Failed to marshal server settings: %s", err)
log.Println(resp) log.Println(resp)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
return return
} }
config := bootstrap.GetConfig()
err = ioutil.WriteFile(filepath.Join(config.FactorioConfigDir, config.SettingsFile), settings, 0644) err = ioutil.WriteFile(filepath.Join(config.FactorioConfigDir, config.SettingsFile), settings, 0644)
if err != nil { if err != nil {
resp = fmt.Sprintf("Failed to save server settings: %v\n", err) 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") 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 // save admins to adminJson
admins, err := json.MarshalIndent(FactorioServ.Settings["admins"], "", " ") admins, err := json.MarshalIndent(server.Settings["admins"], "", " ")
if err != nil { if err != nil {
resp = fmt.Sprintf("Failed to marshal admins-Setting: %s", err) resp = fmt.Sprintf("Failed to marshal admins-Setting: %s", err)
log.Println(resp) log.Println(resp)

View File

@ -1,10 +1,12 @@
package main package api
import ( import (
"archive/zip" "archive/zip"
"errors" "errors"
"fmt" "fmt"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/mroote/factorio-server-manager/bootstrap"
"github.com/mroote/factorio-server-manager/factorio"
"io" "io"
"log" "log"
"net/http" "net/http"
@ -12,8 +14,8 @@ import (
"path/filepath" "path/filepath"
) )
func CheckModPackExists(modPackMap ModPackMap, modPackName string, w http.ResponseWriter, resp interface{}) error { func CheckModPackExists(modPackMap factorio.ModPackMap, modPackName string, w http.ResponseWriter, resp interface{}) error {
exists := modPackMap.checkModPackExists(modPackName) exists := modPackMap.CheckModPackExists(modPackName)
if !exists { if !exists {
resp = fmt.Sprintf("requested modPack {%s} does not exist", modPackName) resp = fmt.Sprintf("requested modPack {%s} does not exist", modPackName)
log.Println(resp) log.Println(resp)
@ -23,8 +25,8 @@ func CheckModPackExists(modPackMap ModPackMap, modPackName string, w http.Respon
return nil return nil
} }
func CreateNewModPackMap(w http.ResponseWriter, resp *interface{}) (modPackMap ModPackMap, err error) { func CreateNewModPackMap(w http.ResponseWriter, resp *interface{}) (modPackMap factorio.ModPackMap, err error) {
modPackMap, err = newModPackMap() modPackMap, err = factorio.NewModPackMap()
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
*resp = fmt.Sprintf("Error creating modpackmap aka. list of all modpacks files : %s", err) *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 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) vars := mux.Vars(r)
modPackName = vars["modpack"] modPackName = vars["modpack"]
@ -67,7 +69,7 @@ func ModPackListHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
resp = modPackMap.listInstalledModPacks() resp = modPackMap.ListInstalledModPacks()
} }
func ModPackCreateHandler(w http.ResponseWriter, r *http.Request) { func ModPackCreateHandler(w http.ResponseWriter, r *http.Request) {
@ -93,7 +95,7 @@ func ModPackCreateHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
err = modPackMap.createModPack(modPackStruct.Name) err = modPackMap.CreateModPack(modPackStruct.Name)
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
resp = fmt.Sprintf("Error creating modpack file: %s", err) resp = fmt.Sprintf("Error creating modpack file: %s", err)
@ -101,7 +103,7 @@ func ModPackCreateHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
resp = modPackMap.listInstalledModPacks() resp = modPackMap.ListInstalledModPacks()
} }
func ModPackDeleteHandler(w http.ResponseWriter, r *http.Request) { func ModPackDeleteHandler(w http.ResponseWriter, r *http.Request) {
@ -119,7 +121,7 @@ func ModPackDeleteHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
err = modPackMap.deleteModPack(modPackName) err = modPackMap.DeleteModPack(modPackName)
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
resp = fmt.Sprintf("Error deleting modpack file: %s", err) 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) zipWriter := zip.NewWriter(w)
defer zipWriter.Close() defer zipWriter.Close()
config := bootstrap.GetConfig()
//iterate over folder and create everything in the zip //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 { err = filepath.Walk(filepath.Join(config.FactorioModPackDir, modPackName), func(path string, info os.FileInfo, err error) error {
if info.IsDir() == false { if info.IsDir() == false {
@ -205,7 +209,7 @@ func ModPackLoadHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
err = modPackMap[modPackName].loadModPack() err = modPackMap[modPackName].LoadModPack()
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
resp = fmt.Sprintf("Error loading modpack file: %s", err) resp = fmt.Sprintf("Error loading modpack file: %s", err)
@ -213,7 +217,7 @@ func ModPackLoadHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
resp = modPackMap[modPackName].Mods.listInstalledMods() resp = modPackMap[modPackName].Mods.ListInstalledMods()
} }
////////////////////////////////// //////////////////////////////////
@ -233,7 +237,7 @@ func ModPackModListHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
resp = modPackMap[modPackName].Mods.listInstalledMods() resp = modPackMap[modPackName].Mods.ListInstalledMods()
} }
func ModPackModToggleHandler(w http.ResponseWriter, r *http.Request) { func ModPackModToggleHandler(w http.ResponseWriter, r *http.Request) {
@ -259,7 +263,7 @@ func ModPackModToggleHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
err, resp = packMap[packName].Mods.ModSimpleList.toggleMod(modPackStruct.ModName) err, resp = packMap[packName].Mods.ModSimpleList.ToggleMod(modPackStruct.ModName)
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
resp = fmt.Sprintf("Error toggling mod inside modPack: %s", err) resp = fmt.Sprintf("Error toggling mod inside modPack: %s", err)
@ -291,7 +295,7 @@ func ModPackModDeleteHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
err = packMap[packName].Mods.deleteMod(modPackStruct.Name) err = packMap[packName].Mods.DeleteMod(modPackStruct.Name)
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
resp = fmt.Sprintf("Error deleting mod {%s} in modpack {%s}: %s", modPackStruct.Name, packName, err) 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 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 { if err != nil {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
@ -337,7 +341,7 @@ func ModPackModUpdateHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
installedMods := packMap[packName].Mods.listInstalledMods().ModsResult installedMods := packMap[packName].Mods.ListInstalledMods().ModsResult
var found = false var found = false
for _, mod := range installedMods { for _, mod := range installedMods {
if mod.Name == modPackStruct.ModName { if mod.Name == modPackStruct.ModName {
@ -368,7 +372,7 @@ func ModPackModDeleteAllHandler(w http.ResponseWriter, r *http.Request) {
} }
// Delete Modpack // Delete Modpack
err = packMap.deleteModPack(packName) err = packMap.DeleteModPack(packName)
if err != nil { if err != nil {
resp = fmt.Sprintf("Error deleting modPackDir: %s", err) resp = fmt.Sprintf("Error deleting modPackDir: %s", err)
log.Println(resp) log.Println(resp)
@ -376,7 +380,7 @@ func ModPackModDeleteAllHandler(w http.ResponseWriter, r *http.Request) {
} }
// recreate modPack without mods // recreate modPack without mods
err = packMap.createEmptyModPack(packName) err = packMap.CreateEmptyModPack(packName)
if err != nil { if err != nil {
resp = fmt.Sprintf("Error recreating modPackDir: %s", err) resp = fmt.Sprintf("Error recreating modPackDir: %s", err)
log.Println(resp) 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 := ReadModPackRequest(w, r, &resp)
err = modPackMap[modPackName].Mods.uploadMod(formFile, fileHeader) err = modPackMap[modPackName].Mods.UploadMod(formFile, fileHeader)
if err != nil { if err != nil {
resp = fmt.Sprintf("error saving file to modPack: %s", err) resp = fmt.Sprintf("error saving file to modPack: %s", err)
log.Println(resp) log.Println(resp)
@ -415,7 +419,7 @@ func ModPackModUploadHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
resp = modPackMap[modPackName].Mods.listInstalledMods() resp = modPackMap[modPackName].Mods.ListInstalledMods()
} }
func ModPackModPortalInstallHandler(w http.ResponseWriter, r *http.Request) { func ModPackModPortalInstallHandler(w http.ResponseWriter, r *http.Request) {
@ -444,9 +448,9 @@ func ModPackModPortalInstallHandler(w http.ResponseWriter, r *http.Request) {
return 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 { if err != nil {
resp = fmt.Sprintf("Error downloading a mod: %s", err) resp = fmt.Sprintf("Error downloading a mod: %s", err)
log.Println(resp) log.Println(resp)
@ -454,7 +458,7 @@ func ModPackModPortalInstallHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
resp = mods.listInstalledMods() resp = modList.ListInstalledMods()
} }
func ModPackModPortalInstallMultipleHandler(w http.ResponseWriter, r *http.Request) { 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") w.Header().Set("Content-Type", "application/json;charset=UTF-8")
var data []struct { var data []struct {
Name string `json:"name"` Name string `json:"name"`
Version Version `json:"version"` Version factorio.Version `json:"version"`
} }
err = ReadFromRequestBody(w, r, &resp, &data) err = ReadFromRequestBody(w, r, &resp, &data)
if err != nil { if err != nil {
@ -480,10 +484,9 @@ func ModPackModPortalInstallMultipleHandler(w http.ResponseWriter, r *http.Reque
if err != nil { if err != nil {
return return
} }
modList := packMap[packName].Mods
mods := packMap[packName].Mods
for _, datum := range data { for _, datum := range data {
details, err, statusCode := modPortalModDetails(datum.Name) details, err, statusCode := factorio.ModPortalModDetails(datum.Name)
if err != nil || statusCode != http.StatusOK { if err != nil || statusCode != http.StatusOK {
resp = fmt.Sprintf("Error in getting mod details from mod portal: %s", err) resp = fmt.Sprintf("Error in getting mod details from mod portal: %s", err)
log.Println(resp) log.Println(resp)
@ -493,11 +496,12 @@ func ModPackModPortalInstallMultipleHandler(w http.ResponseWriter, r *http.Reque
//find correct mod-version //find correct mod-version
var found = false var found = false
for _, release := range details.Releases { for _, release := range details.Releases {
if release.Version.Equals(datum.Version) { if release.Version.Equals(datum.Version) {
found = true found = true
err := mods.downloadMod(release.DownloadURL, release.FileName, details.Name) err := modList.DownloadMod(release.DownloadURL, release.FileName, details.Name)
if err != nil { if err != nil {
resp = fmt.Sprintf("Error downloading mod {%s}, error: %s", details.Name, err) resp = fmt.Sprintf("Error downloading mod {%s}, error: %s", details.Name, err)
log.Println(resp) log.Println(resp)
@ -514,5 +518,5 @@ func ModPackModPortalInstallMultipleHandler(w http.ResponseWriter, r *http.Reque
} }
} }
resp = mods.listInstalledMods() resp = modList.ListInstalledMods()
} }

View File

@ -1,9 +1,10 @@
package main package api
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/mroote/factorio-server-manager/factorio"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"io" "io"
"mime/multipart" "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`) 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") assert.NoError(t, err, "error creating mods")
if !emptyMods { 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"`) 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"`) 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) CallRoute(t, method, baseRoute, route, nil, handlerFunc, http.StatusOK, expected)
// check if mods are really loaded // check if mods are really loaded
packMap, err := newModPackMap() packMap, err := factorio.NewModPackMap()
assert.NoError(t, err, "Error creating modPackMap") assert.NoError(t, err, "Error creating modPackMap")
mods, err := newMods("dev") modList, err := factorio.NewMods("dev")
assert.NoError(t, err, "Error creating mods object") assert.NoError(t, err, "Error creating mods object")
packModsJson, err := json.Marshal(packMap["test"].Mods) packModsJson, err := json.Marshal(packMap["test"].Mods)
assert.NoError(t, err, "Error marshalling mods from modPack") 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.NoError(t, err, "Error marshalling mods object")
assert.JSONEq(t, string(packModsJson), string(modsJson), "loaded mods and modPack are not identical") 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) CallRoute(t, method, baseRoute, route, nil, handlerFunc, http.StatusOK, expected)
// check if mods are really loaded // check if mods are really loaded
packMap, err := newModPackMap() packMap, err := factorio.NewModPackMap()
assert.NoError(t, err, "Error creating modPackMap") assert.NoError(t, err, "Error creating modPackMap")
mods, err := newMods("dev") modList, err := factorio.NewMods("dev")
assert.NoError(t, err, "Error creating mods object") assert.NoError(t, err, "Error creating mods object")
packModsJson, err := json.Marshal(packMap["test"].Mods) packModsJson, err := json.Marshal(packMap["test"].Mods)
assert.NoError(t, err, "Error marshalling mods from modPack") 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.NoError(t, err, "Error marshalling mods object")
assert.JSONEq(t, string(packModsJson), string(modsJson), "loaded mods and modPack are not identical") 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) CallRoute(t, method, baseRoute, route, requestBody, handlerFunc, http.StatusOK, expected)
// check if changes happened // check if changes happened
packMap, err := newModPackMap() packMap, err := factorio.NewModPackMap()
assert.NoError(t, err, "Error creating modPackMap") assert.NoError(t, err, "Error creating modPackMap")
found := false found := false
@ -293,7 +294,7 @@ func TestModPackModToggleHandler(t *testing.T) {
CallRoute(t, method, baseRoute, route, requestBody, handlerFunc, http.StatusOK, expected) CallRoute(t, method, baseRoute, route, requestBody, handlerFunc, http.StatusOK, expected)
packMap, err = newModPackMap() packMap, err = factorio.NewModPackMap()
assert.NoError(t, err, "Error creating modPackMap") assert.NoError(t, err, "Error creating modPackMap")
found = false found = false
@ -338,10 +339,10 @@ func TestModPackModDeleteHandler(t *testing.T) {
CallRoute(t, method, baseRoute, route, requestBody, handlerFunc, http.StatusOK, `true`) CallRoute(t, method, baseRoute, route, requestBody, handlerFunc, http.StatusOK, `true`)
// check if mod is really not installed anymore // check if mod is really not installed anymore
packMap, err := newModPackMap() packMap, err := factorio.NewModPackMap()
assert.NoError(t, err, "Error creating modPackMap") 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") 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") CallRoute(t, method, baseRoute, route, nil, handlerFunc, http.StatusOK, "true")
// check if really empty // check if really empty
packMap, err := newModPackMap() packMap, err := factorio.NewModPackMap()
assert.NoError(t, err, "Error creating modPackMap") assert.NoError(t, err, "Error creating modPackMap")
if len(packMap["test"].Mods.ModInfoList.Mods) != 0 { if len(packMap["test"].Mods.ModInfoList.Mods) != 0 {
@ -405,10 +406,10 @@ func TestModPackModUpdateHandler(t *testing.T) {
defer CleanupModPacks(t) defer CleanupModPacks(t)
// disable "belt-balancer" mod, so we can test, if it is still deactivated after // 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") 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") 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}` 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, "") CallRoute(t, method, baseRoute, route, strings.NewReader(requestBody), handlerFunc, http.StatusInternalServerError, "")
// check if old mod is still there // check if old mod is still there
packMap, err := newModPackMap() packMap, err := factorio.NewModPackMap()
assert.NoError(t, err, "Error creating modPackMap") assert.NoError(t, err, "Error creating modPackMap")
var found = false var found = false
@ -524,19 +525,19 @@ func TestModPackModUploadHandler(t *testing.T) {
} }
// check if mod is uploaded correctly // check if mod is uploaded correctly
packMap, err := newModPackMap() packMap, err := factorio.NewModPackMap()
assert.NoError(t, err, "error creating modPackMap") assert.NoError(t, err, "error creating modPackMap")
expected := ModsResultList{ expected := factorio.ModsResultList{
ModsResult: []ModsResult{ ModsResult: []factorio.ModsResult{
{ {
ModInfo: ModInfo{ ModInfo: factorio.ModInfo{
Name: "belt-balancer", Name: "belt-balancer",
Version: "2.1.3", Version: "2.1.3",
Title: "Belt Balancer", Title: "Belt Balancer",
Author: "knoxfighter", Author: "knoxfighter",
FileName: "belt-balancer_2.1.3.zip", FileName: "belt-balancer_2.1.3.zip",
FactorioVersion: Version{0, 18, 0, 0}, FactorioVersion: factorio.Version{0, 18, 0, 0},
Dependencies: nil, Dependencies: nil,
Compatibility: true, 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) assert.Equal(t, expected, actual, `New mod is not correctly installed. expected "%v" - actual "%v"`, expected, actual)
}) })

View File

@ -1,8 +1,9 @@
package main package api
import ( import (
"fmt" "fmt"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/mroote/factorio-server-manager/factorio"
"log" "log"
"net/http" "net/http"
) )
@ -18,7 +19,7 @@ func ModPortalListModsHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json;charset=UTF-8") w.Header().Set("Content-Type", "application/json;charset=UTF-8")
var statusCode int var statusCode int
resp, err, statusCode = modPortalList() resp, err, statusCode = factorio.ModPortalList()
if err != nil { if err != nil {
resp = fmt.Sprintf("Error in listing mods from mod portal: %s", err) 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"] modId := vars["mod"]
var statusCode int var statusCode int
resp, err, statusCode = modPortalModDetails(modId) resp, err, statusCode = factorio.ModPortalModDetails(modId)
if err != nil { if err != nil {
resp = fmt.Sprintf("Error in getting mod details from mod portal: %s", err) 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 return
} }
err = mods.downloadMod(data.DownloadURL, data.Filename, data.ModName) err = mods.DownloadMod(data.DownloadURL, data.Filename, data.ModName)
if err != nil { if err != nil {
resp = fmt.Sprintf("Error downloading a mod: %s", err) resp = fmt.Sprintf("Error downloading a mod: %s", err)
log.Println(resp) log.Println(resp)
@ -91,7 +92,7 @@ func ModPortalInstallHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
resp = mods.listInstalledMods() resp = mods.ListInstalledMods()
} }
func ModPortalLoginHandler(w http.ResponseWriter, r *http.Request) { func ModPortalLoginHandler(w http.ResponseWriter, r *http.Request) {
@ -113,7 +114,7 @@ func ModPortalLoginHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
loginStatus, err, statusCode := factorioLogin(data.Username, data.Password) loginStatus, err, statusCode := factorio.FactorioLogin(data.Username, data.Password)
if err != nil { if err != nil {
resp = fmt.Sprintf("Error trying to login into Factorio: %s", err) resp = fmt.Sprintf("Error trying to login into Factorio: %s", err)
log.Println(resp) log.Println(resp)
@ -136,8 +137,8 @@ func ModPortalLoginStatusHandler(w http.ResponseWriter, r *http.Request) {
WriteResponse(w, resp) WriteResponse(w, resp)
}() }()
var credentials FactorioCredentials var credentials factorio.Credentials
resp, err = credentials.load() resp, err = credentials.Load()
if err != nil { if err != nil {
resp = fmt.Sprintf("Error getting the factorio credentials: %s", err) 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) WriteResponse(w, resp)
}() }()
var credentials FactorioCredentials var credentials factorio.Credentials
err = credentials.del() err = credentials.Del()
if err != nil { if err != nil {
resp = fmt.Sprintf("Error on logging out of factorio: %s", err) 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") w.Header().Set("Content-Type", "application/json;charset=UTF-8")
var data []struct { var data []struct {
Name string `json:"name"` Name string `json:"name"`
Version Version `json:"version"` Version factorio.Version `json:"version"`
} }
err = ReadFromRequestBody(w, r, &resp, &data) err = ReadFromRequestBody(w, r, &resp, &data)
if err != nil { if err != nil {
return return
} }
mods, err := CreateNewMods(w, &resp) modList, err := CreateNewMods(w, &resp)
if err != nil { if err != nil {
return return
} }
for _, datum := range data { for _, datum := range data {
details, err, statusCode := modPortalModDetails(datum.Name) details, err, statusCode := factorio.ModPortalModDetails(datum.Name)
if err != nil || statusCode != http.StatusOK { if err != nil || statusCode != http.StatusOK {
resp = fmt.Sprintf("Error in getting mod details from mod portal: %s", err) resp = fmt.Sprintf("Error in getting mod details from mod portal: %s", err)
log.Println(resp) log.Println(resp)
@ -207,7 +208,7 @@ func ModPortalInstallMultipleHandler(w http.ResponseWriter, r *http.Request) {
if release.Version.Equals(datum.Version) { if release.Version.Equals(datum.Version) {
found = true found = true
err := mods.downloadMod(release.DownloadURL, release.FileName, details.Name) err := modList.DownloadMod(release.DownloadURL, release.FileName, details.Name)
if err != nil { if err != nil {
resp = fmt.Sprintf("Error downloading mod {%s}, error: %s", details.Name, err) resp = fmt.Sprintf("Error downloading mod {%s}, error: %s", details.Name, err)
log.Println(resp) log.Println(resp)
@ -223,5 +224,5 @@ func ModPortalInstallMultipleHandler(w http.ResponseWriter, r *http.Request) {
} }
} }
resp = mods.listInstalledMods() resp = modList.ListInstalledMods()
} }

View File

@ -1,4 +1,4 @@
package main package api
import ( import (
"net/http" "net/http"

View File

@ -1,38 +1,22 @@
package main package api
import ( import (
"archive/zip" "archive/zip"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/mroote/factorio-server-manager/bootstrap"
"github.com/mroote/factorio-server-manager/factorio"
"github.com/mroote/factorio-server-manager/lockfile" "github.com/mroote/factorio-server-manager/lockfile"
"io" "io"
"log" "log"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"time"
) )
type ModPortalStruct struct { func CreateNewMods(w http.ResponseWriter, resp *interface{}) (modList factorio.Mods, err error) {
DownloadsCount int `json:"downloads_count"` config := bootstrap.GetConfig()
Name string `json:"name"` modList, err = factorio.NewMods(config.FactorioModsDir)
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)
if err != nil { if err != nil {
*resp = fmt.Sprintf("Error creating mods object: %s", err) *resp = fmt.Sprintf("Error creating mods object: %s", err)
log.Println(*resp) 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") w.Header().Set("Content-Type", "application/json;charset=UTF-8")
mods, err := CreateNewMods(w, &resp) modList, err := CreateNewMods(w, &resp)
if err != nil { if err != nil {
return return
} }
resp = mods.listInstalledMods().ModsResult resp = modList.ListInstalledMods().ModsResult
} }
func ModToggleHandler(w http.ResponseWriter, r *http.Request) { func ModToggleHandler(w http.ResponseWriter, r *http.Request) {
@ -101,7 +85,7 @@ func ModToggleHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
err, resp = mods.ModSimpleList.toggleMod(data.Name) err, resp = mods.ModSimpleList.ToggleMod(data.Name)
if err != nil { if err != nil {
resp = fmt.Sprintf("Error in toggling mod in simple list: %s", err) resp = fmt.Sprintf("Error in toggling mod in simple list: %s", err)
log.Println(resp) log.Println(resp)
@ -130,12 +114,12 @@ func ModDeleteHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
mods, err := CreateNewMods(w, &resp) modList, err := CreateNewMods(w, &resp)
if err != nil { if err != nil {
return return
} }
err = mods.deleteMod(data.Name) err = modList.DeleteMod(data.Name)
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
resp = fmt.Sprintf("Error in deleting mod {%s}: %s", data.Name, err) 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") w.Header().Set("Content-Type", "application/json;charset=UTF-8")
//delete mods folder //delete mods folder
err = deleteAllMods() err = factorio.DeleteAllMods()
if err != nil { if err != nil {
resp = fmt.Sprintf("Error deleting all mods: %s", err) resp = fmt.Sprintf("Error deleting all mods: %s", err)
log.Println(resp) log.Println(resp)
@ -195,14 +179,14 @@ func ModUpdateHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
err = mods.updateMod(modData.Name, modData.DownloadUrl, modData.Filename) err = mods.UpdateMod(modData.Name, modData.DownloadUrl, modData.Filename)
if err != nil { if err != nil {
resp = fmt.Sprintf("Error updating mod {%s}: %s", modData.Name, err) resp = fmt.Sprintf("Error updating mod {%s}: %s", modData.Name, err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
return return
} }
installedMods := mods.listInstalledMods().ModsResult installedMods := mods.ListInstalledMods().ModsResult
for _, mod := range installedMods { for _, mod := range installedMods {
if mod.Name == modData.Name { if mod.Name == modData.Name {
resp = mod resp = mod
@ -240,7 +224,7 @@ func ModUploadHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
err = mods.uploadMod(formFile, fileHeader) err = mods.UploadMod(formFile, fileHeader)
if err != nil { if err != nil {
resp = fmt.Sprintf("error saving file to mods: %s", err) resp = fmt.Sprintf("error saving file to mods: %s", err)
log.Println(resp) log.Println(resp)
@ -248,7 +232,7 @@ func ModUploadHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
resp = mods.listInstalledMods() resp = mods.ListInstalledMods()
} }
func ModDownloadHandler(w http.ResponseWriter, r *http.Request) { func ModDownloadHandler(w http.ResponseWriter, r *http.Request) {
@ -256,17 +240,17 @@ func ModDownloadHandler(w http.ResponseWriter, r *http.Request) {
zipWriter := zip.NewWriter(w) zipWriter := zip.NewWriter(w)
defer zipWriter.Close() defer zipWriter.Close()
config := bootstrap.GetConfig()
//iterate over folder and create everything in the zip //iterate over folder and create everything in the zip
err = filepath.Walk(config.FactorioModsDir, func(path string, info os.FileInfo, err error) error { err = filepath.Walk(config.FactorioModsDir, func(path string, info os.FileInfo, err error) error {
if info.IsDir() == false { if info.IsDir() == false {
//Lock the file, that we are want to read //Lock the file, that we are want to read
err := fileLock.RLock(path) err := factorio.FileLock.RLock(path)
if err != nil { if err != nil {
log.Printf("error locking file for reading, something else has locked it") log.Printf("error locking file for reading, something else has locked it")
return err return err
} }
defer fileLock.RUnlock(path) defer factorio.FileLock.RUnlock(path)
writer, err := zipWriter.Create(info.Name()) writer, err := zipWriter.Create(info.Name())
if err != nil { if err != nil {
@ -330,9 +314,9 @@ func LoadModsFromSaveHandler(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
return return
} }
config := bootstrap.GetConfig()
path := filepath.Join(config.FactorioSavesDir, saveFileStruct.Name) path := filepath.Join(config.FactorioSavesDir, saveFileStruct.Name)
f, err := OpenArchiveFile(path, "level.dat") f, err := factorio.OpenArchiveFile(path, "level.dat")
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
resp = fmt.Sprintf("cannot open save level file: %v", err) 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() defer f.Close()
var header SaveHeader var header factorio.SaveHeader
err = header.ReadFrom(f) err = header.ReadFrom(f)
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)

View File

@ -1,8 +1,10 @@
package main package api
import ( import (
"bytes" "bytes"
"github.com/gorilla/mux" "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/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"io" "io"
@ -20,23 +22,27 @@ func TestMain(m *testing.M) {
var err error var err error
// basic setup stuff // basic setup stuff
parseFlags() bootstrap.SetFlags(bootstrap.Flags{
})
config := bootstrap.GetConfig()
config.FactorioModsDir = "dev" config.FactorioModsDir = "dev"
config.FactorioModPackDir = "dev_packs" config.FactorioModPackDir = "dev_packs"
FactorioServ = new(FactorioServer) FactorioServ := new(factorio.Server)
FactorioServ.Version = Version{1, 0, 0, 0} FactorioServ.Version = factorio.Version{1, 0, 0, 0}
FactorioServ.BaseModVersion = "1.0.0" FactorioServ.BaseModVersion = "1.0.0"
// check login status // check login status
var cred FactorioCredentials var cred factorio.Credentials
load, err := cred.load() load, err := cred.Load()
if err != nil { if err != nil {
log.Fatalf("Error loading factorio credentials: %s", err) log.Fatalf("Error loading factorio credentials: %s", err)
return return
} }
if !load { if !load {
// no credentials found, login... // 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 { if err != nil {
log.Printf("Error logging in into factorio: %s", err) log.Printf("Error logging in into factorio: %s", err)
return return
@ -64,18 +70,18 @@ func SetupMods(t *testing.T, empty bool) {
return return
} }
mods, err := newMods(config.FactorioModsDir) mod, err := factorio.NewMods(config.FactorioModsDir)
if err != nil { if err != nil {
t.Fatalf("couldn't create Mods object: %s", err) t.Fatalf("couldn't create Mods object: %s", err)
} }
if !empty { 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 { if err != nil {
t.Fatalf(`Error downloading Mod "belt-balancer": %s`, err) 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 { if err != nil {
t.Fatalf(`Error downloading Mod "train-station-overview": %s`, err) 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) CallRoute(t, method, route, route, requestBody, handlerFunc, http.StatusOK, expected)
// check if changes happenes // check if changes happenes
mods, err := newMods("dev") modList, err := factorio.NewMods("dev")
if err != nil { if err != nil {
t.Fatalf("Error creating Mods object: %s", err) t.Fatalf("Error creating Mods object: %s", err)
} }
found := false found := false
for _, mod := range mods.ModSimpleList.Mods { for _, mod := range modList.ModSimpleList.Mods {
if mod.Name == "belt-balancer" { if mod.Name == "belt-balancer" {
// this mod has to be deactivated now // this mod has to be deactivated now
if mod.Enabled { if mod.Enabled {
@ -207,12 +213,12 @@ func TestModToggleHandler(t *testing.T) {
CallRoute(t, method, route, route, requestBody, handlerFunc, http.StatusOK, expected) CallRoute(t, method, route, route, requestBody, handlerFunc, http.StatusOK, expected)
mods, err = newMods("dev") modList, err = factorio.NewMods("dev")
if err != nil { if err != nil {
t.Fatalf("Error creating Mods object: %s", err) t.Fatalf("Error creating Mods object: %s", err)
} }
found = false found = false
for _, mod := range mods.ModSimpleList.Mods { for _, mod := range modList.ModSimpleList.Mods {
if mod.Name == "belt-balancer" { if mod.Name == "belt-balancer" {
// this mod has to be deactivated now // this mod has to be deactivated now
if !mod.Enabled { if !mod.Enabled {
@ -250,11 +256,11 @@ func TestModDeleteHandler(t *testing.T) {
CallRoute(t, method, route, route, requestBody, handlerFunc, http.StatusOK, `"belt-balancer"`) CallRoute(t, method, route, route, requestBody, handlerFunc, http.StatusOK, `"belt-balancer"`)
// check if mod is really not installed anymore // check if mod is really not installed anymore
mods, err := newMods("dev") _, err := factorio.NewMods("dev")
if err != nil { if err != nil {
t.Fatalf("Error creating Mods object: %s", err) 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") 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") CallRoute(t, method, route, route, nil, handlerFunc, http.StatusOK, "null")
// check if no mods are there // check if no mods are there
mods, err := newMods("dev") modList, err := factorio.NewMods("dev")
if err != nil { if err != nil {
t.Fatalf("Error creating mods object: %s", err) 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!") t.Fatalf("Mods are still there!")
} }
}) })
@ -313,11 +319,11 @@ func TestModUpdateHandler(t *testing.T) {
defer CleanupMods(t) defer CleanupMods(t)
// disable "belt-balancer" mod, so we can test, if it is still deactivated after // 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 { if err != nil {
t.Fatalf("Error creating mods object: %s", err) t.Fatalf("Error creating mods object: %s", err)
} }
err, _ = mods.ModSimpleList.toggleMod("belt-balancer") err, _ = modList.ModSimpleList.ToggleMod("belt-balancer")
if err != nil { if err != nil {
t.Fatalf("Error toggling mod: %s", err) t.Fatalf("Error toggling mod: %s", err)
} }
@ -416,19 +422,19 @@ func TestModUploadHandler(t *testing.T) {
} }
// check if mod is uploaded correctly // check if mod is uploaded correctly
mods, err := newMods("dev") modList, err := factorio.NewMods("dev")
assert.NoError(t, err, "error creating mods object") assert.NoError(t, err, "error creating mods object")
expected := ModsResultList{ expected := factorio.ModsResultList{
ModsResult: []ModsResult{ ModsResult: []factorio.ModsResult{
{ {
ModInfo: ModInfo{ ModInfo: factorio.ModInfo{
Name: "belt-balancer", Name: "belt-balancer",
Version: "2.1.3", Version: "2.1.3",
Title: "Belt Balancer", Title: "Belt Balancer",
Author: "knoxfighter", Author: "knoxfighter",
FileName: "belt-balancer_2.1.3.zip", FileName: "belt-balancer_2.1.3.zip",
FactorioVersion: Version{0, 18, 0, 0}, FactorioVersion: factorio.Version{0, 18, 0, 0},
Dependencies: nil, Dependencies: nil,
Compatibility: true, 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) assert.Equal(t, expected, actual, `New mod is not correctly installed. expected "%v" - actual "%v"`, expected, actual)
}) })

View File

@ -1,4 +1,4 @@
package main package api
import ( import (
"log" "log"
@ -58,9 +58,9 @@ func NewRouter() *mux.Router {
Methods("GET"). Methods("GET").
Name("Websocket"). Name("Websocket").
Handler(AuthorizeHandler(ws)) Handler(AuthorizeHandler(ws))
ws.Handle("command send", commandSend) ws.Handle("command send", CommandSend)
ws.Handle("log subscribe", logSubscribe) ws.Handle("log subscribe", LogSubscribe)
ws.Handle("server status subscribe", serverStatusSubscribe) ws.Handle("server status subscribe", ServerStatusSubscribe)
// Serves the frontend application from the app directory // Serves the frontend application from the app directory
// Uses basic file server to serve index.html and Javascript application // 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 // Redirects user to login page if no session is found
func AuthorizeHandler(h http.Handler) http.Handler { func AuthorizeHandler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Auth := GetAuth()
if err := Auth.aaa.Authorize(w, r, true); err != nil { if err := Auth.aaa.Authorize(w, r, true); err != nil {
log.Printf("Unauthenticated request %s %s %s", r.Method, r.Host, r.RequestURI) log.Printf("Unauthenticated request %s %s %s", r.Method, r.Host, r.RequestURI)
http.Redirect(w, r, "/login", http.StatusSeeOther) http.Redirect(w, r, "/login", http.StatusSeeOther)

View File

@ -1,4 +1,4 @@
package main package api
import ( import (
"github.com/gorilla/websocket" "github.com/gorilla/websocket"

View File

@ -1,9 +1,11 @@
package main package api
import ( import (
"github.com/mroote/factorio-server-manager/bootstrap"
"github.com/mroote/factorio-server-manager/factorio"
"log" "log"
"path/filepath" "path/filepath"
time "time" "time"
"github.com/hpcloud/tail" "github.com/hpcloud/tail"
) )
@ -18,8 +20,9 @@ func IsClosed(ch <-chan Message) bool {
return false return false
} }
func logSubscribe(client *Client, data interface{}) { func LogSubscribe(client *Client, data interface{}) {
go func() { go func() {
config := bootstrap.GetConfig()
logfile := filepath.Join(config.FactorioDir, "factorio-server-console.log") logfile := filepath.Join(config.FactorioDir, "factorio-server-console.log")
t, err := tail.TailFile(logfile, tail.Config{Follow: true, Poll: true}) t, err := tail.TailFile(logfile, tail.Config{Follow: true, Poll: true})
if err != nil { if err != nil {
@ -38,12 +41,13 @@ func logSubscribe(client *Client, data interface{}) {
}() }()
} }
func commandSend(client *Client, data interface{}) { func CommandSend(client *Client, data interface{}) {
if FactorioServ.Running { var server, _ = factorio.GetFactorioServer()
if server.Running {
go func() { go func() {
log.Printf("Received command: %v", data) log.Printf("Received command: %v", data)
reqId, err := FactorioServ.Rcon.Write(data.(string)) reqId, err := server.Rcon.Write(data.(string))
if err != nil { if err != nil {
log.Printf("Error sending rcon command: %s", err) log.Printf("Error sending rcon command: %s", err)
return 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") log.Printf("Subcribed to Server Status")
go func() { go func() {
isRunning := FactorioServ.Running isRunning := server.Running
// always check if status has changed // always check if status has changed
for { for {
if isRunning != FactorioServ.Running { if isRunning != server.Running {
isRunning = FactorioServ.Running isRunning = server.Running
log.Printf("Server Status has changed") log.Printf("Server Status has changed")

View File

@ -1,4 +1,4 @@
package main package factorio
import ( import (
"log" "log"
@ -6,12 +6,12 @@ import (
"github.com/go-ini/ini" "github.com/go-ini/ini"
) )
// Loads config.ini file from the factorio config directory // Loads bootstrap.ini file from the factorio bootstrap directory
func loadConfig(filename string) (map[string]map[string]string, error) { func LoadConfig(filename string) (map[string]map[string]string, error) {
log.Printf("Loading config file: %s", filename) log.Printf("Loading bootstrap file: %s", filename)
cfg, err := ini.Load(filename) cfg, err := ini.Load(filename)
if err != nil { if err != nil {
log.Printf("Error loading config.ini file: %s", err) log.Printf("Error loading bootstrap.ini file: %s", err)
return nil, 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] = map[string]string{}
result[sectionName] = s.KeysHash() result[sectionName] = s.KeysHash()
} }
log.Printf("Encoding config.ini to JSON") log.Printf("Encoding bootstrap.ini to JSON")
return result, nil return result, nil
} }

View File

@ -1,21 +1,22 @@
package main package factorio
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"github.com/mroote/factorio-server-manager/bootstrap"
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
) )
type FactorioCredentials struct { type Credentials struct {
Username string `json:"username"` Username string `json:"username"`
Userkey string `json:"userkey"` Userkey string `json:"userkey"`
} }
func (credentials *FactorioCredentials) save() error { func (credentials *Credentials) Save() error {
var err error var err error
config := bootstrap.GetConfig()
credentialsJson, err := json.Marshal(credentials) credentialsJson, err := json.Marshal(credentials)
if err != nil { if err != nil {
log.Printf("error mashalling the credentials: %s", err) log.Printf("error mashalling the credentials: %s", err)
@ -31,23 +32,23 @@ func (credentials *FactorioCredentials) save() error {
return nil return nil
} }
func (credentials *FactorioCredentials) load() (bool, error) { func (credentials *Credentials) Load() (bool, error) {
var err error var err error
config := bootstrap.GetConfig()
if _, err := os.Stat(config.FactorioCredentialsFile); os.IsNotExist(err) { if _, err := os.Stat(config.FactorioCredentialsFile); os.IsNotExist(err) {
return false, nil return false, nil
} }
fileBytes, err := ioutil.ReadFile(config.FactorioCredentialsFile) fileBytes, err := ioutil.ReadFile(config.FactorioCredentialsFile)
if err != nil { if err != nil {
credentials.del() credentials.Del()
log.Printf("error reading CredentialsFile: %s", err) log.Printf("error reading CredentialsFile: %s", err)
return false, err return false, err
} }
err = json.Unmarshal(fileBytes, credentials) err = json.Unmarshal(fileBytes, credentials)
if err != nil { if err != nil {
credentials.del() credentials.Del()
log.Printf("error on unmarshal credentials_file: %s", err) log.Printf("error on unmarshal credentials_file: %s", err)
return false, err return false, err
} }
@ -55,14 +56,14 @@ func (credentials *FactorioCredentials) load() (bool, error) {
if credentials.Userkey != "" && credentials.Username != "" { if credentials.Userkey != "" && credentials.Username != "" {
return true, nil return true, nil
} else { } else {
credentials.del() credentials.Del()
return false, errors.New("incredients incomplete") return false, errors.New("incredients incomplete")
} }
} }
func (credentials *FactorioCredentials) del() error { func (credentials *Credentials) Del() error {
var err error var err error
config := bootstrap.GetConfig()
err = os.Remove(config.FactorioCredentialsFile) err = os.Remove(config.FactorioCredentialsFile)
if err != nil { if err != nil {
log.Printf("error delete the credentialfile: %s", err) log.Printf("error delete the credentialfile: %s", err)

View File

@ -1,14 +1,17 @@
package main package factorio
import ( import (
"github.com/mroote/factorio-server-manager/bootstrap"
"log" "log"
"github.com/hpcloud/tail" "github.com/hpcloud/tail"
) )
func tailLog(filename string) ([]string, error) { func TailLog(filename string) ([]string, error) {
result := []string{} result := []string{}
config := bootstrap.GetConfig()
t, err := tail.TailFile(config.FactorioLog, tail.Config{Follow: false}) t, err := tail.TailFile(config.FactorioLog, tail.Config{Follow: false})
if err != nil { if err != nil {
log.Printf("Error tailing log %s", err) log.Printf("Error tailing log %s", err)

View File

@ -1,4 +1,4 @@
package main package factorio
import ( import (
"archive/zip" "archive/zip"
@ -26,9 +26,9 @@ type ModsResultList struct {
ModsResult []ModsResult `json:"mods"` 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 err error
var mods Mods var mods Mods
@ -47,7 +47,7 @@ func newMods(destination string) (Mods, error) {
return mods, nil return mods, nil
} }
func (mods *Mods) listInstalledMods() ModsResultList { func (mods *Mods) ListInstalledMods() ModsResultList {
result := ModsResultList{make([]ModsResult, 0)} result := ModsResultList{make([]ModsResult, 0)}
for _, modInfo := range mods.ModInfoList.Mods { for _, modInfo := range mods.ModInfoList.Mods {
@ -73,7 +73,7 @@ func (mods *Mods) listInstalledMods() ModsResultList {
return result return result
} }
func (mods *Mods) deleteMod(modName string) error { func (mods *Mods) DeleteMod(modName string) error {
var err error var err error
err = mods.ModInfoList.deleteMod(modName) err = mods.ModInfoList.deleteMod(modName)
@ -95,7 +95,7 @@ func (mods *Mods) createMod(modName string, fileName string, fileRc io.Reader) e
var err error var err error
//check if mod already exists and delete it //check if mod already exists and delete it
if mods.ModSimpleList.checkModExists(modName) { if mods.ModSimpleList.CheckModExists(modName) {
err = mods.ModInfoList.deleteMod(modName) err = mods.ModInfoList.deleteMod(modName)
if err != nil { if err != nil {
log.Printf("error when deleting mod: %s", err) 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 // also add to ModSimpleList if not there yet
if !mods.ModSimpleList.checkModExists(modName) { if !mods.ModSimpleList.CheckModExists(modName) {
err = mods.ModSimpleList.createMod(modName) err = mods.ModSimpleList.createMod(modName)
if err != nil { if err != nil {
log.Printf("error creating mod in modSimpleList: %s", err) 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 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 err error
var credentials FactorioCredentials var credentials Credentials
status, err := credentials.load() status, err := credentials.Load()
if err != nil { if err != nil {
log.Printf("error loading credentials: %s", err) log.Printf("error loading credentials: %s", err)
return err return err
@ -175,7 +175,7 @@ func (mods *Mods) downloadMod(url string, filename string, modId string) error {
return nil 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 var err error
if filepath.Ext(header.Filename) != ".zip" { if filepath.Ext(header.Filename) != ".zip" {
@ -211,10 +211,10 @@ func (mods *Mods) uploadMod(file multipart.File, header *multipart.FileHeader) e
return nil 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 var err error
err = mods.downloadMod(url, filename, modName) err = mods.DownloadMod(url, filename, modName)
if err != nil { if err != nil {
log.Printf("updateMod ... error when downloading the new Mod: %s", err) log.Printf("updateMod ... error when downloading the new Mod: %s", err)
return err return err

View File

@ -1,4 +1,4 @@
package main package factorio
import ( import (
"archive/zip" "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 { err = filepath.Walk(modInfoList.Destination, func(path string, info os.FileInfo, err error) error {
if !info.IsDir() && filepath.Ext(path) == ".zip" { if !info.IsDir() && filepath.Ext(path) == ".zip" {
err = fileLock.RLock(path) err = FileLock.RLock(path)
if err != nil && err == lockfile.ErrorAlreadyLocked { if err != nil && err == lockfile.ErrorAlreadyLocked {
log.Println(err) log.Println(err)
return nil return nil
@ -58,7 +58,7 @@ func (modInfoList *ModInfoList) listInstalledMods() error {
log.Printf("error locking file: %s", err) log.Printf("error locking file: %s", err)
return err return err
} }
defer fileLock.RUnlock(path) defer FileLock.RUnlock(path)
zipFile, err := zip.OpenReader(path) zipFile, err := zip.OpenReader(path)
if err != nil { if err != nil {
@ -107,11 +107,13 @@ func (modInfoList *ModInfoList) listInstalledMods() error {
break break
} }
server, _ := GetFactorioServer()
if !base.Equals(NilVersion) { if !base.Equals(NilVersion) {
modInfo.Compatibility = FactorioServ.Version.Compare(base, op) modInfo.Compatibility = server.Version.Compare(base, op)
} else { } else {
log.Println("error finding basemodDependency. Using FactorioVersion...") 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) modInfoList.Mods = append(modInfoList.Mods, modInfo)
@ -136,10 +138,10 @@ func (modInfoList *ModInfoList) deleteMod(modName string) error {
if mod.Name == modName { if mod.Name == modName {
filePath := filepath.Join(modInfoList.Destination, mod.FileName) filePath := filepath.Join(modInfoList.Destination, mod.FileName)
fileLock.LockW(filePath) FileLock.LockW(filePath)
//delete mod //delete mod
err = os.Remove(filePath) err = os.Remove(filePath)
fileLock.Unlock(filePath) FileLock.Unlock(filePath)
if err != nil { if err != nil {
log.Printf("ModInfoList ... error when deleting mod: %s", err) log.Printf("ModInfoList ... error when deleting mod: %s", err)
return err return err
@ -207,7 +209,7 @@ func (modInfoList *ModInfoList) createMod(modName string, fileName string, modFi
} }
defer newFile.Close() defer newFile.Close()
fileLock.LockW(filePath) FileLock.LockW(filePath)
_, err = io.Copy(newFile, modFile) _, err = io.Copy(newFile, modFile)
if err != nil { if err != nil {
@ -221,7 +223,7 @@ func (modInfoList *ModInfoList) createMod(modName string, fileName string, modFi
return err return err
} }
fileLock.Unlock(filePath) FileLock.Unlock(filePath)
//reload the list //reload the list
err = modInfoList.listInstalledMods() err = modInfoList.listInstalledMods()

View File

@ -1,4 +1,4 @@
package main package factorio
import ( import (
"encoding/json" "encoding/json"
@ -108,7 +108,7 @@ func (modSimpleList *ModSimpleList) deleteMod(modName string) error {
return nil return nil
} }
func (modSimpleList *ModSimpleList) checkModExists(modName string) bool { func (modSimpleList *ModSimpleList) CheckModExists(modName string) bool {
for _, singleMod := range modSimpleList.Mods { for _, singleMod := range modSimpleList.Mods {
if singleMod.Name == modName { if singleMod.Name == modName {
return true return true
@ -139,7 +139,7 @@ func (modSimpleList *ModSimpleList) createMod(modName string) error {
return nil return nil
} }
func (modSimpleList *ModSimpleList) toggleMod(modName string) (error, bool) { func (modSimpleList *ModSimpleList) ToggleMod(modName string) (error, bool) {
var err error var err error
var newEnabled bool var newEnabled bool

View File

@ -1,7 +1,8 @@
package main package factorio
import ( import (
"errors" "errors"
"github.com/mroote/factorio-server-manager/bootstrap"
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
@ -22,7 +23,7 @@ type ModPackResultList struct {
ModPacks []ModPackResult `json:"mod_packs"` ModPacks []ModPackResult `json:"mod_packs"`
} }
func newModPackMap() (ModPackMap, error) { func NewModPackMap() (ModPackMap, error) {
var err error var err error
modPackMap := make(ModPackMap) modPackMap := make(ModPackMap)
@ -39,7 +40,7 @@ func newModPack(modPackFolder string) (*ModPack, error) {
var err error var err error
var modPack ModPack var modPack ModPack
modPack.Mods, err = newMods(modPackFolder) modPack.Mods, err = NewMods(modPackFolder)
if err != nil { if err != nil {
log.Printf("error on loading mods in mod_pack_dir: %s", err) log.Printf("error on loading mods in mod_pack_dir: %s", err)
return &modPack, err return &modPack, err
@ -51,6 +52,7 @@ func newModPack(modPackFolder string) (*ModPack, error) {
func (modPackMap *ModPackMap) reload() error { func (modPackMap *ModPackMap) reload() error {
var err error var err error
newModPackMap := make(ModPackMap) newModPackMap := make(ModPackMap)
config := bootstrap.GetConfig()
err = filepath.Walk(config.FactorioModPackDir, func(path string, info os.FileInfo, err error) error { err = filepath.Walk(config.FactorioModPackDir, func(path string, info os.FileInfo, err error) error {
if path == config.FactorioModPackDir || !info.IsDir() { if path == config.FactorioModPackDir || !info.IsDir() {
@ -77,13 +79,13 @@ func (modPackMap *ModPackMap) reload() error {
return nil return nil
} }
func (modPackMap *ModPackMap) listInstalledModPacks() ModPackResultList { func (modPackMap *ModPackMap) ListInstalledModPacks() ModPackResultList {
var modPackResultList ModPackResultList var modPackResultList ModPackResultList
for modPackName, modPack := range *modPackMap { for modPackName, modPack := range *modPackMap {
var modPackResult ModPackResult var modPackResult ModPackResult
modPackResult.Name = modPackName modPackResult.Name = modPackName
modPackResult.Mods = modPack.Mods.listInstalledMods() modPackResult.Mods = modPack.Mods.ListInstalledMods()
modPackResultList.ModPacks = append(modPackResultList.ModPacks, modPackResult) modPackResultList.ModPacks = append(modPackResultList.ModPacks, modPackResult)
} }
@ -91,12 +93,12 @@ func (modPackMap *ModPackMap) listInstalledModPacks() ModPackResultList {
return modPackResultList return modPackResultList
} }
func (modPackMap *ModPackMap) createModPack(modPackName string) error { func (modPackMap *ModPackMap) CreateModPack(modPackName string) error {
var err error var err error
config := bootstrap.GetConfig()
modPackFolder := filepath.Join(config.FactorioModPackDir, modPackName) modPackFolder := filepath.Join(config.FactorioModPackDir, modPackName)
if modPackMap.checkModPackExists(modPackName) == true { if modPackMap.CheckModPackExists(modPackName) == true {
log.Printf("ModPack %s already existis", modPackName) log.Printf("ModPack %s already existis", modPackName)
return errors.New("ModPack " + modPackName + " already exists, please choose a different name") 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 return nil
} }
func (modPackMap *ModPackMap) createEmptyModPack(packName string) error { func (modPackMap *ModPackMap) CreateEmptyModPack(packName string) error {
var err error var err error
config := bootstrap.GetConfig()
modPackFolder := filepath.Join(config.FactorioModPackDir, packName) modPackFolder := filepath.Join(config.FactorioModPackDir, packName)
if modPackMap.checkModPackExists(packName) == true { if modPackMap.CheckModPackExists(packName) == true {
log.Printf("ModPack %s already existis", packName) log.Printf("ModPack %s already existis", packName)
return errors.New("ModPack " + packName + " already exists, please choose a different name") 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 return nil
} }
func (modPackMap *ModPackMap) checkModPackExists(modPackName string) bool { func (modPackMap *ModPackMap) CheckModPackExists(modPackName string) bool {
for modPackId := range *modPackMap { for modPackId := range *modPackMap {
if modPackId == modPackName { if modPackId == modPackName {
return true return true
@ -195,9 +197,9 @@ func (modPackMap *ModPackMap) checkModPackExists(modPackName string) bool {
return false return false
} }
func (modPackMap *ModPackMap) deleteModPack(modPackName string) error { func (modPackMap *ModPackMap) DeleteModPack(modPackName string) error {
var err error var err error
config := bootstrap.GetConfig()
modPackDir := filepath.Join(config.FactorioModPackDir, modPackName) modPackDir := filepath.Join(config.FactorioModPackDir, modPackName)
err = os.RemoveAll(modPackDir) err = os.RemoveAll(modPackDir)
@ -215,9 +217,9 @@ func (modPackMap *ModPackMap) deleteModPack(modPackName string) error {
return nil return nil
} }
func (modPack *ModPack) loadModPack() error { func (modPack *ModPack) LoadModPack() error {
var err error var err error
config := bootstrap.GetConfig()
//get filemode, so it can be restored //get filemode, so it can be restored
fileInfo, err := os.Stat(config.FactorioModsDir) fileInfo, err := os.Stat(config.FactorioModsDir)
if err != nil { if err != nil {

View File

@ -1,4 +1,4 @@
package main package factorio
import ( import (
"encoding/json" "encoding/json"
@ -7,10 +7,29 @@ import (
"log" "log"
"net/http" "net/http"
"net/url" "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 // 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) req, err := http.NewRequest(http.MethodGet, "https://mods.factorio.com/api/mods?page_size=max", nil)
if err != nil { if err != nil {
return "error", err, 500 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 // 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 var jsonVal ModPortalStruct
req, err := http.NewRequest(http.MethodGet, "https://mods.factorio.com/api/mods/"+modId, nil) 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 //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 var err error
resp, err := http.PostForm("https://auth.factorio.com/api-login", 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 return err.Error(), err, http.StatusInternalServerError
} }
credentials := FactorioCredentials{ credentials := Credentials{
Username: username, Username: username,
Userkey: successResponse[0], Userkey: successResponse[0],
} }
err = credentials.save() err = credentials.Save()
if err != nil { if err != nil {
log.Printf("error saving the credentials. %s", err) log.Printf("error saving the credentials. %s", err)
return err.Error(), err, http.StatusInternalServerError return err.Error(), err, http.StatusInternalServerError

View File

@ -1,10 +1,11 @@
package main package factorio
import ( import (
"archive/zip" "archive/zip"
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"github.com/mroote/factorio-server-manager/bootstrap"
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
@ -20,9 +21,9 @@ type LoginSuccessResponse struct {
UserKey []string `json:""` UserKey []string `json:""`
} }
func deleteAllMods() error { func DeleteAllMods() error {
var err error var err error
config := bootstrap.GetConfig()
modsDirInfo, err := os.Stat(config.FactorioModsDir) modsDirInfo, err := os.Stat(config.FactorioModsDir)
if err != nil { if err != nil {
log.Printf("error getting stats of FactorioModsDir: %s", err) log.Printf("error getting stats of FactorioModsDir: %s", err)
@ -46,13 +47,12 @@ func deleteAllMods() error {
return nil return nil
} }
func modStartUp() { func ModStartUp() {
var err error config := bootstrap.GetConfig()
//get main-folder info //get main-folder info
factorioDirInfo, err := os.Stat(config.FactorioDir) factorioDirInfo, err := os.Stat(config.FactorioDir)
if err != nil { 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 return
} }
factorioDirPerm := factorioDirInfo.Mode().Perm() factorioDirPerm := factorioDirInfo.Mode().Perm()
@ -66,7 +66,7 @@ func modStartUp() {
//crate mod_pack dir //crate mod_pack dir
if _, err = os.Stat(config.FactorioModPackDir); os.IsNotExist(err) { if _, err = os.Stat(config.FactorioModPackDir); os.IsNotExist(err) {
log.Println("no ModPackDir found ... creating one ...") log.Println("no ModPackDir found ... creating one ...")
os.Mkdir(config.FactorioModPackDir, factorioDirPerm) _ = os.Mkdir(config.FactorioModPackDir, factorioDirPerm)
} }
oldModpackDir := filepath.Join(config.FactorioDir, "modpacks") oldModpackDir := filepath.Join(config.FactorioDir, "modpacks")
@ -125,7 +125,7 @@ func modStartUp() {
} }
defer modPackFile.Close() defer modPackFile.Close()
mods, err := newMods(modPackDir) mods, err := NewMods(modPackDir)
if err != nil { if err != nil {
log.Printf("error reading mods: %s", err) log.Printf("error reading mods: %s", err)
return err return err

View File

@ -1,6 +1,7 @@
package main package factorio
import ( import (
"github.com/mroote/factorio-server-manager/bootstrap"
"log" "log"
"strconv" "strconv"
@ -9,8 +10,10 @@ import (
func connectRC() error { func connectRC() error {
var err error var err error
config := bootstrap.GetConfig()
rconAddr := config.ServerIP + ":" + strconv.Itoa(config.FactorioRconPort) 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 { if err != nil {
log.Printf("Cannot create rcon session: %s", err) log.Printf("Cannot create rcon session: %s", err)
return err return err

View File

@ -1,4 +1,4 @@
package main package factorio
import ( import (
"archive/zip" "archive/zip"

View File

@ -1,4 +1,4 @@
package main package factorio
import ( import (
"testing" "testing"

View File

@ -1,8 +1,9 @@
package main package factorio
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/mroote/factorio-server-manager/bootstrap"
"log" "log"
"os" "os"
"os/exec" "os/exec"
@ -21,7 +22,7 @@ func (s Save) String() string {
} }
// Lists save files in factorio/saves // Lists save files in factorio/saves
func listSaves(saveDir string) (saves []Save, err error) { func ListSaves(saveDir string) (saves []Save, err error) {
saves = []Save{} saves = []Save{}
err = filepath.Walk(saveDir, func(path string, info os.FileInfo, err error) error { err = filepath.Walk(saveDir, func(path string, info os.FileInfo, err error) error {
if info.IsDir() && info.Name() == "saves" { if info.IsDir() && info.Name() == "saves" {
@ -37,8 +38,9 @@ func listSaves(saveDir string) (saves []Save, err error) {
return return
} }
func findSave(name string) (*Save, error) { func FindSave(name string) (*Save, error) {
saves, err := listSaves(config.FactorioSavesDir) config := bootstrap.GetConfig()
saves, err := ListSaves(config.FactorioSavesDir)
if err != nil { if err != nil {
return nil, fmt.Errorf("error listing saves: %v", err) 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") return nil, errors.New("save not found")
} }
func (s *Save) remove() error { func (s *Save) Remove() error {
if s.Name == "" { if s.Name == "" {
return errors.New("save name cannot be blank") return errors.New("save name cannot be blank")
} }
config := bootstrap.GetConfig()
return os.Remove(filepath.Join(config.FactorioSavesDir, s.Name)) return os.Remove(filepath.Join(config.FactorioSavesDir, s.Name))
} }
// Create savefiles for Factorio // Create savefiles for Factorio
func createSave(filePath string) (string, error) { func CreateSave(filePath string) (string, error) {
err := os.MkdirAll(filepath.Dir(filePath), 0755) err := os.MkdirAll(filepath.Dir(filePath), 0755)
if err != nil { if err != nil {
log.Printf("Error in creating Factorio save: %s", err) log.Printf("Error in creating Factorio save: %s", err)
@ -69,6 +71,7 @@ func createSave(filePath string) (string, error) {
} }
args := []string{"--create", filePath} args := []string{"--create", filePath}
config := bootstrap.GetConfig()
cmdOutput, err := exec.Command(config.FactorioBinary, args...).Output() cmdOutput, err := exec.Command(config.FactorioBinary, args...).Output()
if err != nil { if err != nil {
log.Printf("Error in creating Factorio save: %s", err) log.Printf("Error in creating Factorio save: %s", err)

View File

@ -1,24 +1,24 @@
package main package factorio
import ( import (
"bufio" "bufio"
"encoding/json" "encoding/json"
"fmt" "github.com/mroote/factorio-server-manager/bootstrap"
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
"math/rand"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"sync"
"github.com/majormjr/rcon" "github.com/majormjr/rcon"
) )
type FactorioServer struct { type Server struct {
Cmd *exec.Cmd `json:"-"` Cmd *exec.Cmd `json:"-"`
Savefile string `json:"savefile"` Savefile string `json:"savefile"`
Latency int `json:"latency"` Latency int `json:"latency"`
@ -35,24 +35,22 @@ type FactorioServer struct {
LogChan chan []string `json:"-"` LogChan chan []string `json:"-"`
} }
func randomPort() int { var instantiated Server
// Returns random port to use for rcon connection var once sync.Once
return rand.Intn(45000-40000) + 40000
}
func autostart() { func (f *Server) autostart() {
var err error var err error
if FactorioServ.BindIP == "" { if f.BindIP == "" {
FactorioServ.BindIP = "0.0.0.0" f.BindIP = "0.0.0.0"
} }
if FactorioServ.Port == 0 { if f.Port == 0 {
FactorioServ.Port = 34197 f.Port = 34197
} }
FactorioServ.Savefile = "Load Latest" f.Savefile = "Load Latest"
err = FactorioServ.Run() err = f.Run()
if err != nil { if err != nil {
log.Printf("Error starting Factorio server: %+v", err) log.Printf("Error starting Factorio server: %+v", err)
@ -61,134 +59,146 @@ func autostart() {
} }
func initFactorio() (f *FactorioServer, err error) { func GetFactorioServer() (f Server, err error) {
f = new(FactorioServer) once.Do(func() {
f.Settings = make(map[string]interface{}) f = Server{}
f.Settings = make(map[string]interface{})
if err = os.MkdirAll(config.FactorioConfigDir, 0755); err != nil { config := bootstrap.GetConfig()
return nil, fmt.Errorf("failed to create config directory: %v", err) if err = os.MkdirAll(config.FactorioConfigDir, 0755); err != nil {
} log.Printf("failed to create bootstrap directory: %v", err)
return
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)
} }
err = example.Close() settingsPath := filepath.Join(config.FactorioConfigDir, config.SettingsFile)
if err != nil { var settings *os.File
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()
}
// before reading reset offset if _, err := os.Stat(settingsPath); os.IsNotExist(err) {
if _, err = settings.Seek(0, 0); err != nil { // copy example settings to supplied settings file, if not exists
return nil, fmt.Errorf("error while seeking in settings file: %v", err) log.Printf("Server settings at %s not found, copying example server settings.\n", settingsPath)
}
if err = json.NewDecoder(settings).Decode(&f.Settings); err != nil { examplePath := filepath.Join(config.FactorioDir, "data", "server-settings.example.json")
return nil, fmt.Errorf("error reading %s: %v", settingsPath, err)
}
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{} settings, err = os.Create(settingsPath)
//Load factorio version if err != nil {
if config.glibcCustom == "true" { log.Printf("failed to create server settings file: %v", err)
out, err = exec.Command(config.glibcLocation, "--library-path", config.glibcLibLoc, config.FactorioBinary, "--version").Output() return
} else { }
out, err = exec.Command(config.FactorioBinary, "--version").Output() defer settings.Close()
}
if err != nil { _, err = io.Copy(settings, example)
log.Printf("error on loading factorio version: %s", err) if err != nil {
return log.Printf("failed to copy example server settings: %v", err)
} return
}
reg := regexp.MustCompile("Version.*?((\\d+\\.)?(\\d+\\.)?(\\*|\\d+)+)") err = example.Close()
found := reg.FindStringSubmatch(string(out)) if err != nil {
err = f.Version.UnmarshalText([]byte(found[1])) log.Printf("failed to close example server settings: %s", err)
if err != nil { return
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 { } else {
data, err := ioutil.ReadFile(filepath.Join(config.FactorioConfigDir, config.FactorioAdminFile)) // otherwise, open file normally
settings, err = os.Open(settingsPath)
if err != nil { if err != nil {
log.Printf("Error loading FactorioAdminFile: %s", err) log.Printf("failed to open server settings file: %v", err)
return f, err return
} }
defer settings.Close()
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
} }
}
if config.autostart == "true" { // before reading reset offset
go autostart() 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 var err error
config := bootstrap.GetConfig()
data, err := json.MarshalIndent(f.Settings, "", " ") data, err := json.MarshalIndent(f.Settings, "", " ")
if err != nil { if err != nil {
log.Println("Failed to marshal FactorioServerSettings: ", err) 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 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 //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" { if config.GlibcCustom == "true" {
log.Println("Custom glibc selected, glibc.so location:", config.glibcLocation, " lib location:", config.glibcLibLoc) 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, "--library-path", config.GlibcLibLoc, config.FactorioBinary, "--executable-path", config.FactorioBinary)
} }
args = append(args, args = append(args,
@ -222,9 +232,9 @@ func (f *FactorioServer) Run() error {
args = append(args, "--start-server", filepath.Join(config.FactorioSavesDir, f.Savefile)) args = append(args, "--start-server", filepath.Join(config.FactorioSavesDir, f.Savefile))
} }
if config.glibcCustom == "true" { if config.GlibcCustom == "true" {
log.Println("Starting server with command: ", config.glibcLocation, args) log.Println("Starting server with command: ", config.GlibcLocation, args)
f.Cmd = exec.Command(config.glibcLocation, args...) f.Cmd = exec.Command(config.GlibcLocation, args...)
} else { } else {
log.Println("Starting server with command: ", config.FactorioBinary, args) log.Println("Starting server with command: ", config.FactorioBinary, args)
f.Cmd = exec.Command(config.FactorioBinary, args...) f.Cmd = exec.Command(config.FactorioBinary, args...)
@ -268,7 +278,7 @@ func (f *FactorioServer) Run() error {
return nil return nil
} }
func (f *FactorioServer) parseRunningCommand(std io.ReadCloser) (err error) { func (f *Server) parseRunningCommand(std io.ReadCloser) (err error) {
stdScanner := bufio.NewScanner(std) stdScanner := bufio.NewScanner(std)
for stdScanner.Scan() { for stdScanner.Scan() {
log.Printf("Factorio Server: %s", stdScanner.Text()) log.Printf("Factorio Server: %s", stdScanner.Text())
@ -308,7 +318,8 @@ func (f *FactorioServer) parseRunningCommand(std io.ReadCloser) (err error) {
return nil 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") logfileName := filepath.Join(config.FactorioDir, "factorio-server-console.log")
file, err := os.OpenFile(logfileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) file, err := os.OpenFile(logfileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
if err != nil { if err != nil {
@ -327,7 +338,7 @@ func (f *FactorioServer) writeLog(logline string) error {
return nil return nil
} }
func (f *FactorioServer) checkLogError(logline []string) error { func (f *Server) checkLogError(logline []string) error {
// TODO Handle errors generated by running Factorio Server // TODO Handle errors generated by running Factorio Server
log.Println(logline) log.Println(logline)

View File

@ -1,7 +1,7 @@
// use this file only when compiling not windows (all unix systems) // use this file only when compiling not windows (all unix systems)
// +build !windows // +build !windows
package main package factorio
import ( import (
"log" "log"
@ -10,7 +10,7 @@ import (
// Stubs for windows-only functions // Stubs for windows-only functions
func (f *FactorioServer) Kill() error { func (f *Server) Kill() error {
err := f.Cmd.Process.Signal(os.Kill) err := f.Cmd.Process.Signal(os.Kill)
if err != nil { if err != nil {
if err.Error() == "os: process already finished" { if err.Error() == "os: process already finished" {
@ -31,7 +31,7 @@ func (f *FactorioServer) Kill() error {
return nil return nil
} }
func (f *FactorioServer) Stop() error { func (f *Server) Stop() error {
err := f.Cmd.Process.Signal(os.Interrupt) err := f.Cmd.Process.Signal(os.Interrupt)
if err != nil { if err != nil {
if err.Error() == "os: process already finished" { if err.Error() == "os: process already finished" {

View File

@ -1,4 +1,4 @@
package main package factorio
import ( import (
"log" "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) err := f.Cmd.Process.Signal(os.Kill)
if err != nil { if err != nil {
if err.Error() == "os: process already finished" { if err.Error() == "os: process already finished" {
@ -58,7 +58,7 @@ func (f *FactorioServer) Kill() error {
return nil 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. // Disable our own handling of CTRL+C, so we don't close when we send it to the console.
setCtrlHandlingIsDisabledForThisProcess(true) setCtrlHandlingIsDisabledForThisProcess(true)

View File

@ -1,4 +1,4 @@
package main package factorio
import ( import (
"encoding/binary" "encoding/binary"

View File

@ -1,140 +1,69 @@
package main package main
import ( import (
"encoding/json"
"flag" "flag"
"fmt" "github.com/mroote/factorio-server-manager/api"
"github.com/mroote/factorio-server-manager/bootstrap"
"github.com/mroote/factorio-server-manager/factorio"
"log" "log"
"net/http" "net/http"
"os"
"path/filepath"
"runtime"
) )
type Config struct { func main() {
FactorioDir string `json:"factorio_dir"` // parse command flags and pass them to bootstrap
FactorioSavesDir string `json:"saves_dir"` flags := parseFlags()
FactorioModsDir string `json:"mods_dir"` bootstrap.SetFlags(flags)
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
}
var ( // get the all configs based on the flags
config Config config := bootstrap.GetConfig()
FactorioServ *FactorioServer
Auth *AuthHTTP
)
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 { if err != nil {
log.Printf("%s: %s", msg, err) log.Printf("Error occurred during Server initializaion: %v\n", err)
panic(fmt.Sprintf("%s: %s", msg, 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 func parseFlags() bootstrap.Flags {
// JSON config file contains default values, confFile := flag.String("conf", "./conf.json", "Specify location of Factorio Server Manager bootstrap file.")
// 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.")
factorioDir := flag.String("dir", "./", "Specify location of Factorio directory.") factorioDir := flag.String("dir", "./", "Specify location of Factorio directory.")
serverIP := flag.String("host", "0.0.0.0", "Specify IP for webserver to listen on.") 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.") 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.") 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).") 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") 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") 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)") 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)") 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]") autostart := flag.String("autostart", "false", "Autostart factorio server on bootup of FSM, default false [true/false]")
flag.Parse() 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" { return bootstrap.Flags{
appdata := os.Getenv("APPDATA") ConfFile: confFile,
config.FactorioLog = filepath.Join(appdata, "Factorio", "factorio-current.log") FactorioDir: factorioDir,
} else { ServerIP: serverIP,
config.FactorioLog = filepath.Join(config.FactorioDir, "factorio-current.log") 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))
}