mirror of
https://github.com/OpenFactorioServerManager/factorio-server-manager.git
synced 2025-01-18 04:59:02 +02:00
553 lines
14 KiB
Go
553 lines
14 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
|
|
"github.com/gorilla/mux"
|
|
)
|
|
|
|
type JSONResponse struct {
|
|
Success bool `json:"success"`
|
|
Data interface{} `json:"data,string"`
|
|
}
|
|
|
|
func Index(w http.ResponseWriter, r *http.Request) {
|
|
fmt.Fprintln(w, "hello world")
|
|
}
|
|
|
|
// Returns JSON response of all mods installed in factorio/mods
|
|
func ListInstalledMods(w http.ResponseWriter, r *http.Request) {
|
|
var err error
|
|
resp := JSONResponse{
|
|
Success: false,
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
|
|
|
|
modDir := config.FactorioDir + "/mods"
|
|
|
|
resp.Data, err = listInstalledMods(modDir)
|
|
if err != nil {
|
|
resp.Data = fmt.Sprintf("Error in ListInstalledMods handler: %s", err)
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error in list mods: %s", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
resp.Success = true
|
|
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error in list mods: %s", err)
|
|
}
|
|
}
|
|
|
|
// Toggles mod passed in through mod variable
|
|
// Updates mod-list.json file to toggle the enabled status of mods
|
|
func ToggleMod(w http.ResponseWriter, r *http.Request) {
|
|
var err error
|
|
resp := JSONResponse{
|
|
Success: false,
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
|
|
|
|
vars := mux.Vars(r)
|
|
modName := vars["mod"]
|
|
|
|
m, err := parseModList()
|
|
if err != nil {
|
|
resp.Data = fmt.Sprintf("Could not parse mod list: %s", err)
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error in list mods: %s", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
err = m.toggleMod(modName)
|
|
if err != nil {
|
|
resp.Data = fmt.Sprintf("Could not toggle mod: %s error: %s", modName, err)
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error in list mods: %s", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
resp.Success = true
|
|
resp.Data = m
|
|
|
|
if err = json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error in toggle mod: %s", err)
|
|
}
|
|
}
|
|
|
|
// Returns JSON response of all mods in the mod-list.json file
|
|
func ListMods(w http.ResponseWriter, r *http.Request) {
|
|
var err error
|
|
resp := JSONResponse{
|
|
Success: false,
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
|
|
|
|
resp.Data, err = parseModList()
|
|
if err != nil {
|
|
resp.Data = fmt.Sprintf("Could not parse mod list: %s", err)
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error in list mods: %s", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
resp.Success = true
|
|
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error listing mods: %s", err)
|
|
}
|
|
}
|
|
|
|
// Uploads mod to the mods directory
|
|
func UploadMod(w http.ResponseWriter, r *http.Request) {
|
|
var err error
|
|
resp := JSONResponse{
|
|
Success: false,
|
|
}
|
|
|
|
switch r.Method {
|
|
case "GET":
|
|
resp.Data = "Unsupported method"
|
|
if err = json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error listing mods: %s", err)
|
|
}
|
|
case "POST":
|
|
log.Println("Uploading file")
|
|
r.ParseMultipartForm(32 << 20)
|
|
file, header, err := r.FormFile("modfile")
|
|
if err != nil {
|
|
log.Printf("No mod filename provided for upload: %s", err)
|
|
json.NewEncoder(w).Encode("No mod file provided.")
|
|
return
|
|
}
|
|
defer file.Close()
|
|
|
|
out, err := os.Create(config.FactorioModsDir + "/" + header.Filename)
|
|
if err != nil {
|
|
resp.Data = err.Error()
|
|
json.NewEncoder(w).Encode(resp)
|
|
log.Printf("Error in out")
|
|
return
|
|
}
|
|
defer out.Close()
|
|
|
|
_, err = io.Copy(out, file)
|
|
if err != nil {
|
|
resp.Data = err.Error()
|
|
json.NewEncoder(w).Encode(resp)
|
|
log.Printf("Error in io copy")
|
|
return
|
|
}
|
|
log.Printf("Uploaded mod file: %s", header.Filename)
|
|
resp.Data = "File '" + header.Filename + "' submitted successfully"
|
|
resp.Success = true
|
|
json.NewEncoder(w).Encode(resp)
|
|
|
|
default:
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
|
|
func RemoveMod(w http.ResponseWriter, r *http.Request) {
|
|
var err error
|
|
resp := JSONResponse{
|
|
Success: false,
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
|
|
|
|
vars := mux.Vars(r)
|
|
modName := vars["mod"]
|
|
|
|
err = rmMod(modName)
|
|
if err == nil {
|
|
// No error returned means mod was removed
|
|
resp.Data = fmt.Sprintf("Removed mod: %s", modName)
|
|
resp.Success = true
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error removing mod: %s", err)
|
|
}
|
|
} else {
|
|
log.Printf("Error in remove mod handler: %s", err)
|
|
resp.Data = fmt.Sprintf("Error in remove mod handler: %s", err)
|
|
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error removing mod: %s", err)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
func DownloadMod(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
|
|
|
|
vars := mux.Vars(r)
|
|
mod := vars["mod"]
|
|
modFile := config.FactorioModsDir + "/" + mod
|
|
|
|
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", mod))
|
|
log.Printf("%s downloading: %s", r.Host, modFile)
|
|
|
|
http.ServeFile(w, r, modFile)
|
|
}
|
|
|
|
// Lists all save files in the factorio/saves directory
|
|
func ListSaves(w http.ResponseWriter, r *http.Request) {
|
|
var err error
|
|
resp := JSONResponse{
|
|
Success: false,
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
|
|
|
|
saveDir := config.FactorioDir + "/saves"
|
|
|
|
resp.Data, err = listSaves(saveDir)
|
|
if err != nil {
|
|
resp.Data = fmt.Sprintf("Error listing save files: %s", err)
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error listing saves: %s", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
resp.Success = true
|
|
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error listing saves: %s", err)
|
|
}
|
|
}
|
|
|
|
func DLSave(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/octet-stream")
|
|
|
|
vars := mux.Vars(r)
|
|
save := vars["save"]
|
|
saveName := config.FactorioSavesDir + "/" + save
|
|
|
|
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", save))
|
|
log.Printf("%s downloading: %s", r.Host, saveName)
|
|
|
|
http.ServeFile(w, r, saveName)
|
|
}
|
|
|
|
func UploadSave(w http.ResponseWriter, r *http.Request) {
|
|
var err error
|
|
resp := JSONResponse{
|
|
Success: false,
|
|
}
|
|
|
|
switch r.Method {
|
|
case "GET":
|
|
resp.Data = "Unsupported method"
|
|
resp.Success = false
|
|
if err = json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error listing mods: %s", err)
|
|
}
|
|
case "POST":
|
|
log.Println("Uploading save file")
|
|
r.ParseMultipartForm(32 << 20)
|
|
file, header, err := r.FormFile("savefile")
|
|
if err != nil {
|
|
resp.Success = false
|
|
resp.Data = err.Error()
|
|
json.NewEncoder(w).Encode(resp)
|
|
log.Printf("Error in upload save formfile: %s", err.Error())
|
|
return
|
|
}
|
|
defer file.Close()
|
|
|
|
out, err := os.Create(config.FactorioSavesDir + "/" + header.Filename)
|
|
if err != nil {
|
|
resp.Success = false
|
|
resp.Data = err.Error()
|
|
json.NewEncoder(w).Encode(resp)
|
|
log.Printf("Error in out: %s", err)
|
|
return
|
|
}
|
|
defer out.Close()
|
|
|
|
_, err = io.Copy(out, file)
|
|
if err != nil {
|
|
resp.Success = false
|
|
resp.Data = err.Error()
|
|
json.NewEncoder(w).Encode(resp)
|
|
log.Printf("Error in io copy: %s", err)
|
|
return
|
|
}
|
|
log.Printf("Uploaded save file: %s", header.Filename)
|
|
resp.Data = "File '" + header.Filename + "' uploaded successfully"
|
|
resp.Success = true
|
|
json.NewEncoder(w).Encode(resp)
|
|
default:
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
|
|
// Deletes provided save
|
|
func RemoveSave(w http.ResponseWriter, r *http.Request) {
|
|
var err error
|
|
resp := JSONResponse{
|
|
Success: false,
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
|
|
|
|
vars := mux.Vars(r)
|
|
saveName := vars["save"]
|
|
|
|
err = rmSave(saveName)
|
|
if err == nil {
|
|
// save was removed
|
|
resp.Data = fmt.Sprintf("Removed save: %s", saveName)
|
|
resp.Success = true
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error removing save %s", err)
|
|
}
|
|
} else {
|
|
log.Printf("Error in remove save handler: %s", err)
|
|
resp.Data = fmt.Sprintf("Error in remove save handler: %s", err)
|
|
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error removing save: %s", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Launches Factorio server binary with --create flag to create save
|
|
// Url must include save name for creation of savefile
|
|
func CreateSaveHandler(w http.ResponseWriter, r *http.Request) {
|
|
var err error
|
|
resp := JSONResponse{
|
|
Success: false,
|
|
}
|
|
|
|
vars := mux.Vars(r)
|
|
saveName := vars["save"]
|
|
|
|
if saveName == "" {
|
|
log.Printf("Error creating save, no name provided: %s", err)
|
|
resp.Data = "No save name provided."
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error encoding save handler response: %s", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
cmdOut, err := createSave(saveName)
|
|
if err != nil {
|
|
log.Printf("Error creating save: %s", err)
|
|
resp.Data = "Error creating savefile."
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error encoding save handler response: %s", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
resp.Success = true
|
|
resp.Data = fmt.Sprintf("Save %s created successfully. Command output: \n%s", saveName, cmdOut)
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error encoding save response: %s", err)
|
|
}
|
|
}
|
|
|
|
// Returns last lines of the factorio-current.log file
|
|
func LogTail(w http.ResponseWriter, r *http.Request) {
|
|
var err error
|
|
resp := JSONResponse{
|
|
Success: false,
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
|
|
|
|
resp.Data, err = tailLog(config.FactorioLog)
|
|
if err != nil {
|
|
resp.Data = fmt.Sprintf("Could not tail %s: %s", config.FactorioLog, err)
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Could not tail %s: %s", config.FactorioLog, err)
|
|
}
|
|
return
|
|
}
|
|
|
|
resp.Success = true
|
|
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error tailing logfile", err)
|
|
}
|
|
}
|
|
|
|
// Return JSON response of config.ini file
|
|
func LoadConfig(w http.ResponseWriter, r *http.Request) {
|
|
var err error
|
|
resp := JSONResponse{
|
|
Success: false,
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
|
|
|
|
configContents, err := loadConfig(config.FactorioConfigFile)
|
|
if err != nil {
|
|
log.Printf("Could not retrieve config.ini: %s", err)
|
|
resp.Data = "Error getting config.ini"
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error tailing logfile", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
resp.Data = configContents
|
|
resp.Success = true
|
|
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error encoding config file JSON reponse: ", err)
|
|
}
|
|
|
|
log.Printf("Sent config.ini response")
|
|
}
|
|
|
|
func StartServer(w http.ResponseWriter, r *http.Request) {
|
|
var err error
|
|
resp := JSONResponse{
|
|
Success: false,
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
|
|
|
|
if FactorioServ.Running {
|
|
resp.Data = "Factorio server is already running"
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error encoding JSON response: ", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
switch r.Method {
|
|
case "GET":
|
|
log.Printf("GET not supported for startserver handler")
|
|
resp.Data = "Unsupported method"
|
|
resp.Success = false
|
|
if err = json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error listing mods: %s", err)
|
|
}
|
|
case "POST":
|
|
log.Printf("Starting Factorio server.")
|
|
|
|
// TODO get form parameters for starting server
|
|
|
|
body, err := ioutil.ReadAll(r.Body)
|
|
if err != nil {
|
|
log.Printf("Error in starting factorio server handler body: %s", err)
|
|
return
|
|
}
|
|
log.Printf("Starting Factorio server with settings: %v", string(body))
|
|
|
|
err = json.Unmarshal(body, &FactorioServ)
|
|
if err != nil {
|
|
log.Printf("Error unmarshaling server settings JSON: %s", err)
|
|
return
|
|
}
|
|
|
|
go func() {
|
|
err = FactorioServ.Run()
|
|
if err != nil {
|
|
log.Printf("Error starting Factorio server: %s", err)
|
|
resp.Data = fmt.Sprintf("Error starting Factorio server: %s", err)
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error encoding config file JSON reponse: ", err)
|
|
}
|
|
return
|
|
}
|
|
}()
|
|
|
|
if FactorioServ.Running {
|
|
log.Printf("Factorio server started on port: %s", FactorioServ.Port)
|
|
}
|
|
|
|
resp.Data = fmt.Sprintf("Factorio server started on port: %s", FactorioServ.Port)
|
|
resp.Success = true
|
|
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error encoding config file JSON reponse: ", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func StopServer(w http.ResponseWriter, r *http.Request) {
|
|
resp := JSONResponse{
|
|
Success: false,
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
|
|
|
|
if FactorioServ.Running {
|
|
err := FactorioServ.Stop()
|
|
if err != nil {
|
|
log.Printf("Error in stop server handler: %s", err)
|
|
resp.Data = fmt.Sprintf("Error in stop server handler: %s", err)
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error encoding config file JSON reponse: ", err)
|
|
}
|
|
return
|
|
}
|
|
log.Printf("Stopped Factorio server.")
|
|
resp.Success = true
|
|
resp.Data = fmt.Sprintf("Factorio server stopped")
|
|
} else {
|
|
resp.Data = "Factorio server is not running"
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error encoding config file JSON reponse: ", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error encoding config file JSON reponse: ", err)
|
|
}
|
|
}
|
|
|
|
func RunningServer(w http.ResponseWriter, r *http.Request) {
|
|
resp := JSONResponse{
|
|
Success: false,
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
|
|
|
|
if FactorioServ.Running {
|
|
log.Printf("Creating server status response")
|
|
resp.Success = true
|
|
status := map[string]string{}
|
|
status["status"] = "running"
|
|
status["port"] = strconv.Itoa(FactorioServ.Port)
|
|
status["savefile"] = FactorioServ.Savefile
|
|
resp.Data = status
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error encoding config file JSON reponse: ", err)
|
|
}
|
|
log.Printf("Server status sent with data: %+v", resp.Data)
|
|
} else {
|
|
log.Printf("Server not running, creating status response")
|
|
resp.Success = true
|
|
status := map[string]string{}
|
|
status["status"] = "stopped"
|
|
resp.Data = status
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
log.Printf("Error encoding config file JSON reponse: ", err)
|
|
}
|
|
}
|
|
}
|