123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574 |
- // Functions to deal with the agGrid (UI).
- package main
- import (
- "database/sql"
- "encoding/json"
- "fmt"
- "gopkg.in/guregu/null.v3/zero"
- "io/ioutil"
- "net/http"
- "path/filepath"
- "runtime"
- )
- // Auxiliary functions for HTTP handling
- // checkErrHTTP returns an error via HTTP and also logs the error.
- func checkErrHTTP(w http.ResponseWriter, httpStatus int, errorMessage string, err error) {
- if err != nil {
- http.Error(w, fmt.Sprintf(errorMessage, err), httpStatus)
- pc, file, line, ok := runtime.Caller(1)
- Log.Error("(", http.StatusText(httpStatus), ") ", filepath.Base(file), ":", line, ":", pc, ok, " - error:", errorMessage, err)
- }
- }
- // checkErrPanicHTTP returns an error via HTTP and logs the error with a panic.
- func checkErrPanicHTTP(w http.ResponseWriter, httpStatus int, errorMessage string, err error) {
- if err != nil {
- http.Error(w, fmt.Sprintf(errorMessage, err), httpStatus)
- pc, file, line, ok := runtime.Caller(1)
- Log.Panic("(", http.StatusText(httpStatus), ") ", filepath.Base(file), ":", line, ":", pc, ok, " - panic:", errorMessage, err)
- }
- }
- // logErrHTTP assumes that the error message was already composed and writes it to HTTP and logs it.
- // this is mostly to avoid code duplication and make sure that all entries are written similarly
- func logErrHTTP(w http.ResponseWriter, httpStatus int, errorMessage string) {
- http.Error(w, errorMessage, httpStatus)
- Log.Error("(" + http.StatusText(httpStatus) + ") " + errorMessage)
- }
- // funcName is @Sonia's solution to get the name of the function that Go is currently running.
- // This will be extensively used to deal with figuring out where in the code the errors are!
- // Source: https://stackoverflow.com/a/10743805/1035977 (20170708)
- func funcName() string {
- pc, _, _, _ := runtime.Caller(1)
- return runtime.FuncForPC(pc).Name()
- }
- // Main functions to respond to agGrid
- //
- // Each function class has a struct type to deal with database requests
- // objectType is a struct to hold data retrieved from the database, used by several functions (including JSON).
- type ObjectType struct {
- UUID zero.String
- Name zero.String
- BotKey zero.String
- BotName zero.String
- Type zero.String // `json:"string"`
- Position zero.String
- Rotation zero.String
- Velocity zero.String
- LastUpdate zero.String
- Origin zero.String
- Phantom zero.String // `json:"string"`
- Prims zero.String // `json:"string"`
- BBHi zero.String
- BBLo zero.String
- Coords_region string // These two are not on the database but calculated on demand (20170722)
- Coords_xyz []string // can be a string since it will never be deJSONified
- }
- // uiObjects creates a JSON representation of the Obstacles table and spews it out.
- func uiObjects(w http.ResponseWriter, r *http.Request) {
- var (
- rowArr []interface{}
- Object ObjectType
- )
- db, err := sql.Open(PDO_Prefix, GoBotDSN)
- checkErrPanic(err)
- defer db.Close()
- // query
- rows, err := db.Query("SELECT * FROM Obstacles")
- checkErr(err)
- for rows.Next() {
- err = rows.Scan(
- &Object.UUID,
- &Object.Name,
- &Object.BotKey,
- &Object.BotName,
- &Object.Type,
- &Object.Position,
- &Object.Rotation,
- &Object.Velocity,
- &Object.LastUpdate,
- &Object.Origin,
- &Object.Phantom,
- &Object.Prims,
- &Object.BBHi,
- &Object.BBLo,
- )
- // Log.Debug("Row extracted:", Object)
- rowArr = append(rowArr, Object)
- }
- checkErr(err)
- defer rows.Close()
- // produces neatly indented output; see https://blog.golang.org/json-and-go but especially http://stackoverflow.com/a/37084385/1035977
- if data, err := json.MarshalIndent(rowArr, "", " "); err != nil {
- checkErr(err)
- } else {
- // Log.Debugf("json.MarshalIndent:\n%s\n\n", data)
- _, err := fmt.Fprintf(w, "%s", data)
- //if (err == nil) { Log.Debugf("Wrote %d bytes to interface\n", n) } else { checkErr(err) }
- checkErr(err)
- }
- // return
- }
- // uiObjectsUpdate receives a JSON representation of one row (from the agGrid) in order to update our database.
- func uiObjectsUpdate(w http.ResponseWriter, r *http.Request) {
- body, err := ioutil.ReadAll(r.Body) // from https://stackoverflow.com/questions/15672556/handling-json-post-request-in-go (20170524)
- checkErrPanic(err)
- // Log.Debug("\nBody is >>", string(body), "<<")
- var obj ObjectType
- err = json.Unmarshal(body, &obj)
- checkErrPanic(err)
- // Log.Debug("\nJSON decoded body is >>", obj, "<<")
- // update database
- // open database connection and see if we can update the inventory for this object
- db, err := sql.Open(PDO_Prefix, GoBotDSN)
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Connect failed:", err)
- defer db.Close()
- stmt, err := db.Prepare("REPLACE INTO Obstacles (`UUID`, `Name`, `BotKey`, `BotName`, `Type`, `Position`, `Rotation`, `Velocity`, `LastUpdate`, `Origin`, `Phantom`, `Prims`, `BBHi`, `BBLo`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)")
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Replace prepare failed:", err)
- defer stmt.Close()
- _, err = stmt.Exec(obj.UUID, obj.Name, obj.BotKey, obj.BotName, obj.Type, obj.Position,
- obj.Rotation, obj.Velocity, obj.LastUpdate, obj.Origin, obj.Phantom, obj.Prims, obj.BBHi, obj.BBLo)
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Replace exec failed:", err)
- // w.WriteHeader(http.StatusOK)
- // w.Header().Set("Content-type", "text/plain; charset=utf-8")
- // fmt.Fprintln(w, obj, "successfully updated.")
- // Log.Debug(obj, "successfully updated.")
- // return
- }
- // uiObjectsRemove receives a list of UUIDs to remove from the Obstacles table.
- func uiObjectsRemove(w http.ResponseWriter, r *http.Request) {
- body, err := ioutil.ReadAll(r.Body)
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Cannot read body of HTTP Request:", err)
- // Log.Debug("\nObjects body is >>", string(body), "<<")
- // open database connection and see if we can remove the object UUIDs we got
- db, err := sql.Open(PDO_Prefix, GoBotDSN)
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Connect failed:", err)
- defer db.Close()
- _, err = db.Exec(fmt.Sprintf("DELETE FROM Obstacles WHERE UUID IN (%s)", string(body)))
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Objects remove failed:", err)
- Log.Debug("Object UUIDs >>", string(body), "<< successfully removed.")
- }
- // agentType is a struct to hold data retrieved from the database.
- type AgentType struct {
- UUID zero.String
- Name zero.String
- OwnerName zero.String
- OwnerKey zero.String
- Location zero.String
- Position zero.String
- Rotation zero.String
- Velocity zero.String
- Energy zero.String
- Money zero.String
- Happiness zero.String
- Class zero.String
- SubType zero.String
- PermURL zero.String
- LastUpdate zero.String
- BestPath zero.String
- SecondBestPath zero.String
- CurrentTarget zero.String
- Coords_region string
- Coords_xyz []string
- }
- // uiAgents creates a JSON representation of the Agents table and spews it out.
- func uiAgents(w http.ResponseWriter, r *http.Request) {
- var (
- rowArr []interface{}
- Agent AgentType
- )
- db, err := sql.Open(PDO_Prefix, GoBotDSN)
- checkErrPanic(err)
- defer db.Close()
- rows, err := db.Query("SELECT * FROM Agents")
- checkErr(err)
- for rows.Next() {
- err = rows.Scan(
- &Agent.UUID,
- &Agent.Name,
- &Agent.OwnerName,
- &Agent.OwnerKey,
- &Agent.Location,
- &Agent.Position,
- &Agent.Rotation,
- &Agent.Velocity,
- &Agent.Energy,
- &Agent.Money,
- &Agent.Happiness,
- &Agent.Class,
- &Agent.SubType,
- &Agent.PermURL,
- &Agent.LastUpdate,
- &Agent.BestPath,
- &Agent.SecondBestPath,
- &Agent.CurrentTarget,
- )
- rowArr = append(rowArr, Agent)
- }
- checkErr(err)
- defer rows.Close()
- if data, err := json.MarshalIndent(rowArr, "", " "); err != nil {
- checkErr(err)
- } else {
- _, err := fmt.Fprintf(w, "%s", data)
- checkErr(err)
- }
- // return
- }
- // uiAgentsUpdate receives a JSON representation of one row (from the agGrid) in order to update our database.
- func uiAgentsUpdate(w http.ResponseWriter, r *http.Request) {
- body, err := ioutil.ReadAll(r.Body)
- checkErrPanic(err)
- var ag AgentType
- err = json.Unmarshal(body, &ag)
- checkErrPanic(err)
- db, err := sql.Open(PDO_Prefix, GoBotDSN)
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Connect failed:", err)
- defer db.Close()
- stmt, err := db.Prepare("REPLACE INTO Agents (`UUID`, `Name`, `OwnerName`, `OwnerKey`, `Location`, `Position`, `Rotation`, `Velocity`, `Energy`, `Money`, `Happiness`, `Class`, `SubType`, `PermURL`, `LastUpdate`, `BestPath`, `SecondBestPath`, `CurrentTarget`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Replace prepare failed:", err)
- defer stmt.Close()
- _, err = stmt.Exec(ag.UUID, ag.Name, ag.OwnerName, ag.OwnerKey, ag.Location, ag.Position,
- ag.Rotation, ag.Velocity, ag.Energy, ag.Money, ag.Happiness, ag.Class, ag.SubType, ag.PermURL,
- ag.LastUpdate, ag.BestPath, ag.SecondBestPath, ag.CurrentTarget)
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Replace exec failed:", err)
- // return
- }
- // uiAgentsRemove receives a list of UUIDs to remove from the Agents table.
- func uiAgentsRemove(w http.ResponseWriter, r *http.Request) {
- body, err := ioutil.ReadAll(r.Body)
- checkErrPanic(err)
- // Log.Debug("\nAgents Body is >>", string(body), "<<")
- db, err := sql.Open(PDO_Prefix, GoBotDSN)
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Connect failed:", err)
- defer db.Close()
- _, err = db.Exec(fmt.Sprintf("DELETE FROM Agents WHERE UUID IN (%s)", string(body)))
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Agents remove failed:", err)
- Log.Debug("Agents UUIDs >>", string(body), "<< successfully removed.")
- }
- // PositionType is a struct to hold data retrieved from the database, used by several functions (including JSON).
- type PositionType struct {
- PermURL zero.String
- UUID zero.String
- Name zero.String
- OwnerName zero.String
- Location zero.String
- Position zero.String
- Rotation zero.String
- Velocity zero.String
- LastUpdate zero.String
- OwnerKey zero.String
- ObjectType zero.String
- ObjectClass zero.String
- RateEnergy zero.String
- RateMoney zero.String
- RateHappiness zero.String
- Coords_region string
- Coords_xyz []string
- DistanceToAgent float64 // This does not get saved to the database, since it's different for every agent (20170811).
- }
- // uiPositions creates a JSON representation of the Positions table and spews it out.
- func uiPositions(w http.ResponseWriter, r *http.Request) {
- var (
- rowArr []interface{}
- Position PositionType
- )
- db, err := sql.Open(PDO_Prefix, GoBotDSN)
- checkErrPanic(err)
- defer db.Close()
- // query
- rows, err := db.Query("SELECT * FROM Positions")
- checkErr(err)
- for rows.Next() {
- err = rows.Scan(
- &Position.PermURL,
- &Position.UUID,
- &Position.Name,
- &Position.OwnerName,
- &Position.Location,
- &Position.Position,
- &Position.Rotation,
- &Position.Velocity,
- &Position.LastUpdate,
- &Position.OwnerKey,
- &Position.ObjectType,
- &Position.ObjectClass,
- &Position.RateEnergy,
- &Position.RateMoney,
- &Position.RateHappiness,
- )
- rowArr = append(rowArr, Position)
- }
- checkErr(err)
- defer rows.Close()
- if data, err := json.MarshalIndent(rowArr, "", " "); err != nil {
- checkErr(err)
- } else {
- _, err := fmt.Fprintf(w, "%s", data)
- checkErr(err)
- }
- // return
- }
- // uiPositionsUpdate receives a JSON representation of one row (from the agGrid) in order to update our database.
- func uiPositionsUpdate(w http.ResponseWriter, r *http.Request) {
- body, err := ioutil.ReadAll(r.Body)
- checkErrPanic(err)
- var pos PositionType
- err = json.Unmarshal(body, &pos)
- checkErrPanic(err)
- db, err := sql.Open(PDO_Prefix, GoBotDSN)
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Connect failed:", err)
- defer db.Close()
- stmt, err := db.Prepare("REPLACE INTO Positions (`PermURL`, `UUID`, `Name`, `OwnerName`, `Location`, `Position`, `Rotation`, `Velocity`, `LastUpdate`, `OwnerKey`, `ObjectType`, `ObjectClass`, `RateEnergy`, `RateMoney`, `RateHappiness`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Replace prepare failed:", err)
- defer stmt.Close()
- _, err = stmt.Exec(pos.PermURL, pos.UUID, pos.Name, pos.OwnerName, pos.Location, pos.Position,
- pos.Rotation, pos.Velocity, pos.LastUpdate, pos.OwnerKey, pos.ObjectType, pos.ObjectClass,
- pos.RateEnergy, pos.RateMoney, pos.RateHappiness)
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Replace exec failed:", err)
- // return
- }
- // uiPositionsRemove receives a list of UUIDs to remove from the Positions table.
- func uiPositionsRemove(w http.ResponseWriter, r *http.Request) {
- body, err := ioutil.ReadAll(r.Body)
- checkErrPanic(err)
- // Log.Debug("\nPositions Body is >>", string(body), "<<")
- db, err := sql.Open(PDO_Prefix, GoBotDSN)
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Connect failed:", err)
- defer db.Close()
- _, err = db.Exec(fmt.Sprintf("DELETE FROM Positions WHERE UUID IN (%s)", string(body)))
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Positions remove failed:", err)
- Log.Debug("Positions UUIDs >>", string(body), "<< successfully removed.")
- }
- // inventoryType is a struct to hold data retrieved from the database, used by several functions (including JSON).
- type inventoryType struct {
- UUID zero.String
- Name zero.String
- Type zero.String
- LastUpdate zero.String
- Permissions zero.String
- }
- // uiInventory creates a JSON representation of the Inventory table and spews it out.
- func uiInventory(w http.ResponseWriter, r *http.Request) {
- var (
- rowArr []interface{}
- Inventory inventoryType
- )
- db, err := sql.Open(PDO_Prefix, GoBotDSN)
- checkErrPanic(err)
- defer db.Close()
- // query
- rows, err := db.Query("SELECT * FROM Inventory")
- checkErr(err)
- for rows.Next() {
- err = rows.Scan(
- &Inventory.UUID,
- &Inventory.Name,
- &Inventory.Type,
- &Inventory.LastUpdate,
- &Inventory.Permissions,
- )
- rowArr = append(rowArr, Inventory)
- }
- checkErr(err)
- defer rows.Close()
- if data, err := json.MarshalIndent(rowArr, "", " "); err != nil {
- checkErr(err)
- } else {
- _, err := fmt.Fprintf(w, "%s", data)
- checkErr(err)
- }
- // return
- }
- // uiInventoryUpdate receives a JSON representation of one row (from the agGrid) in order to update our database.
- func uiInventoryUpdate(w http.ResponseWriter, r *http.Request) {
- body, err := ioutil.ReadAll(r.Body)
- checkErrPanic(err)
- var inv inventoryType
- err = json.Unmarshal(body, &inv)
- checkErrPanic(err)
- db, err := sql.Open(PDO_Prefix, GoBotDSN)
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Connect failed:", err)
- defer db.Close()
- stmt, err := db.Prepare("REPLACE INTO Inventory (`UUID`, `Name`, `Type`, `LastUpdate`, `Permissions`) VALUES (?,?,?,?,?)")
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Replace prepare failed:", err)
- defer stmt.Close()
- _, err = stmt.Exec(inv.UUID, inv.Name, inv.Type, inv.LastUpdate, inv.Permissions)
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Replace exec failed:", err)
- // return
- }
- // uiInventoryRemove receives a list of UUIDs to remove from the Inventory table.
- func uiInventoryRemove(w http.ResponseWriter, r *http.Request) {
- body, err := ioutil.ReadAll(r.Body)
- checkErrPanic(err)
- // Log.Debug("\nInventory Body is >>", string(body), "<<")
- db, err := sql.Open(PDO_Prefix, GoBotDSN)
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Connect failed:", err)
- defer db.Close()
- _, err = db.Exec(fmt.Sprintf("DELETE FROM Inventory WHERE UUID IN (%s)", string(body)))
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Inventory remove failed:", err)
- Log.Debug("Inventory UUIDs >>", string(body), "<< successfully removed.")
- }
- // userManagementType is a struct to hold data retrieved from the database, used by several functions (including JSON).
- type userManagementType struct {
- Email zero.String
- Password zero.String
- }
- // uiUserManagement creates a JSON representation of the Users table and spews it out.
- func uiUserManagement(w http.ResponseWriter, r *http.Request) {
- var (
- rowArr []interface{}
- UserManagement userManagementType
- )
- db, err := sql.Open(PDO_Prefix, GoBotDSN)
- checkErrPanic(err)
- defer db.Close()
- // query
- rows, err := db.Query("SELECT * FROM Users")
- checkErrPanic(err)
- for rows.Next() {
- err = rows.Scan(
- &UserManagement.Email,
- &UserManagement.Password,
- )
- rowArr = append(rowArr, UserManagement)
- }
- checkErr(err)
- defer rows.Close()
- if data, err := json.MarshalIndent(rowArr, "", " "); err != nil {
- checkErr(err)
- } else {
- _, err := fmt.Fprintf(w, "%s", data)
- checkErr(err)
- }
- // return
- }
- // uiUserManagementUpdate receives a JSON representation of one row (from the agGrid) in order to update our database.
- func uiUserManagementUpdate(w http.ResponseWriter, r *http.Request) {
- body, err := ioutil.ReadAll(r.Body)
- checkErrPanic(err)
- var user userManagementType
- err = json.Unmarshal(body, &user)
- checkErrPanic(err)
- db, err := sql.Open(PDO_Prefix, GoBotDSN)
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Connect failed:", err)
- defer db.Close()
- stmt, err := db.Prepare("REPLACE INTO Users (`Email`, `Password`) VALUES (?,?)")
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Replace prepare failed:", err)
- defer stmt.Close()
- _, err = stmt.Exec(user.Email, user.Password)
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Replace exec failed:", err)
- // return
- }
- // uiUserManagementRemove receives a list of UUIDs to remove from the UserManagement table.
- func uiUserManagementRemove(w http.ResponseWriter, r *http.Request) {
- body, err := ioutil.ReadAll(r.Body)
- checkErrPanic(err)
- // Log.Debug("\nInventory Body is >>", string(body), "<<")
- db, err := sql.Open(PDO_Prefix, GoBotDSN)
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Connect failed:", err)
- defer db.Close()
- _, err = db.Exec(fmt.Sprintf("DELETE FROM Users WHERE Email IN (%s)", string(body)))
- checkErrPanicHTTP(w, http.StatusServiceUnavailable, funcName() + ": Users remove failed:", err)
- Log.Debug("User(s) Email(s) >>", string(body), "<< successfully removed.")
- }
|