diff --git a/.gitignore b/.gitignore index be8934c..a691a6b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,10 @@ remotegameplay room.json laplace scripts/ -server/ +server/docker/ client/ p2p/ +# Ignore SQLite database created +xplane11-webRTC.db + + diff --git a/config/config.go b/config/config.go index 63dbad0..dc209db 100644 --- a/config/config.go +++ b/config/config.go @@ -24,6 +24,8 @@ type Config struct { SSHPassword string NATEscapeServerPort string NATEscapeBarrierPort string + BackendURL string + Rate float64 } // Exists reports whether the named file or directory exists. @@ -63,6 +65,8 @@ func SetDefaults() error { defaults["Rooms"] = defaultPath + "room.json" defaults["ScriptToExecute"] = "" defaults["SSHPassword"] = "" + defaults["Rate"] = 0.0 + defaults["BackendURL"] = "https://xplane-webrtc.akilan.io" //Paths to search for config file configPaths = append(configPaths, defaultPath) diff --git a/core/core.go b/core/core.go new file mode 100644 index 0000000..1cbf176 --- /dev/null +++ b/core/core.go @@ -0,0 +1,129 @@ +package core + +import ( + "encoding/json" + "errors" + "fmt" + "github.com/Akilan1999/p2p-rendering-computation/server" + "github.com/Akilan1999/remotegameplay/config" + "io/ioutil" + "net/http" + "net/url" + "strconv" + "strings" +) + +// GameSession A single Game session. In the following implementation +// the server can have only 1 user occupying it por session. +type GameSession struct { + Link string `json:"LinkID"` + Rate float64 `json:"Rate"` + Server *server.SysInfo `json:"ServerInformation"` +} + +// BroadcastServerToBackend Broadcasts server information for the backend +func BroadcastServerToBackend() error { + // Get server specs + serverInfo := server.ServerInfo() + + // Get information from the config file + config, err := config.ConfigInit() + if err != nil { + return err + } + + var gameSession GameSession + + respIpv4orIPv6 := Ip4or6(config.IPAddress) + + // Adding game session information + respIpv4orIPv6 = "https://" + respIpv4orIPv6 + + // Get room information + _, err = ReadRoomsFile() + if err != nil { + return err + } + + // Game session url + //+ file.ID + gameSession.Link = respIpv4orIPv6 + ":" + config.NATEscapeServerPort + "/?id=" + // Rate for the game session + gameSession.Rate = config.Rate + // Server specs to struct GameSession + // De-referencing server info + gameSession.Server = serverInfo + // convert Struct to JSON string + gameSessionString, err := StringPrettyPrint(gameSession) + if err != nil { + return err + } + + // Printing the entire game session string to ensure the session + // is running + fmt.Println(gameSessionString) + + form := url.Values{} + form.Add("Link", gameSession.Link) + form.Add("Rate", strconv.FormatFloat(gameSession.Rate, 'E', -1, 64)) + form.Add("HostName", gameSession.Server.Hostname) + form.Add("Platform", gameSession.Server.Platform) + form.Add("RAM", strconv.Itoa(int(gameSession.Server.RAM))) + form.Add("Disk", strconv.Itoa(int(gameSession.Server.Disk))) + form.Add("GPU", gameSession.Server.GPU.Gpu.GpuName) + form.Add("CPU", gameSession.Server.CPU) + + req, err := http.NewRequest("POST", config.BackendURL+"AddGameSession", strings.NewReader(form.Encode())) + if err != nil { + return err + } + + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + // Sending request to the backend server + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + // Getting the response + response, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + // Checking if the response succeeded + if string(response) != "success" { + return errors.New(string(response)) + } + + return nil +} + +// StringPrettyPrint print the contents of the obj ( +// Reference: https://stackoverflow.com/questions/24512112/how-to-print-struct-variables-in-console +func StringPrettyPrint(data interface{}) (string, error) { + var p []byte + // var err := error + p, err := json.MarshalIndent(data, "", "\t") + if err != nil { + return "", err + } + return fmt.Sprintf("%s \n", p), nil +} + +// Ip4or6 Helper function to check if the IP address is IPV4 or +// IPV6 (https://socketloop.com/tutorials/golang-check-if-ip-address-is-version-4-or-6) +func Ip4or6(s string) string { + for i := 0; i < len(s); i++ { + switch s[i] { + case '.': + return s + case ':': + return "[" + s + "]" + } + } + return "[" + s + "]" + +} diff --git a/go.mod b/go.mod index 6d5fc94..09d198f 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,13 @@ replace github.com/Akilan1999/p2p-rendering-computation => /Users/akilan/Documen require ( github.com/Akilan1999/p2p-rendering-computation v0.0.0-00010101000000-000000000000 github.com/fatedier/frp v0.47.0 // indirect + github.com/gin-gonic/gin v1.6.3 + github.com/google/uuid v1.3.0 github.com/gorilla/websocket v1.5.0 + github.com/kataras/go-mailer v0.1.0 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect github.com/spf13/viper v1.8.1 + github.com/valyala/bytebufferpool v1.0.0 // indirect + gorm.io/driver/sqlite v1.4.4 + gorm.io/gorm v1.24.5 ) diff --git a/go.sum b/go.sum index b38e976..e67c5c9 100644 --- a/go.sum +++ b/go.sum @@ -116,7 +116,6 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apenella/go-ansible v1.1.0/go.mod h1:tHqFBSwWXF9k2hkLQWfq6oxMQZHslySQqttkA2siyqE= @@ -325,7 +324,6 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= @@ -333,7 +331,6 @@ github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4u github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8= github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= @@ -556,6 +553,11 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -573,6 +575,8 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kataras/go-mailer v0.1.0 h1:KffyTZnKbaNzkRoyckaJZ4/jsUigvyUWxdrXXTTK7cY= +github.com/kataras/go-mailer v0.1.0/go.mod h1:+js8BH5a6EFHhufrz90OBaL9vSv2OFwWyPtJuP98mGk= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= @@ -618,7 +622,6 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= -github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -629,6 +632,8 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= +github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= @@ -937,6 +942,8 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= @@ -1292,7 +1299,6 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1628,6 +1634,11 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc= +gorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI= +gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= +gorm.io/gorm v1.24.5 h1:g6OPREKqqlWq4kh/3MCQbZKImeB9e6Xgc4zD+JgNZGE= +gorm.io/gorm v1.24.5/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= diff --git a/main.go b/main.go index e012d6d..6242994 100644 --- a/main.go +++ b/main.go @@ -1,191 +1,219 @@ package main import ( - "encoding/json" - "flag" - "fmt" - "github.com/Akilan1999/remotegameplay/config" - "github.com/Akilan1999/remotegameplay/core" - "log" - "math/rand" - "net/http" - "os/exec" - "time" + "encoding/json" + "flag" + "fmt" + "github.com/Akilan1999/remotegameplay/config" + "github.com/Akilan1999/remotegameplay/core" + gameserver "github.com/Akilan1999/remotegameplay/server" + "log" + "math/rand" + "net/http" + "os/exec" + "time" ) func main() { - addr := flag.String("addr", "localhost", "Listen address") - port := flag.String("port", "8888", "port for running the server") - tls := flag.Bool("tls", false, "Use TLS") - //setconfig := flag.Bool("setconfig", false, "Generates a config file") - certFile := flag.String("certFile", "files/server.crt", "TLS cert file") - keyFile := flag.String("keyFile", "files/server.key", "TLS key file") - headless := flag.Bool("headless", false, "Creating screenshare using headless mode") - roomInfo := flag.Bool("roomInfo", false, "Getting room id of headless server") - killServer := flag.Bool("killServer", false, "Kills the laplace") - killChromium := flag.Bool("killChromium", false, "Kills all chromuim") - BinaryToExcute := flag.String("BinaryToExecute", "", "Providing path (i.e Absolute path) of binary to execute") + addr := flag.String("addr", "localhost", "Listen address") + port := flag.String("port", "8888", "port for running the server") + tls := flag.Bool("tls", false, "Use TLS") + //setconfig := flag.Bool("setconfig", false, "Generates a config file") + certFile := flag.String("certFile", "files/server.crt", "TLS cert file") + keyFile := flag.String("keyFile", "files/server.key", "TLS key file") + headless := flag.Bool("headless", false, "Creating screenshare using headless mode") + roomInfo := flag.Bool("roomInfo", false, "Getting room id of headless server") + killServer := flag.Bool("killServer", false, "Kills the laplace") + killChromium := flag.Bool("killChromium", false, "Kills all chromuim") + BinaryToExcute := flag.String("BinaryToExecute", "", "Providing path (i.e Absolute path) of binary to execute") + GameServer := flag.Bool("GameServer", false, "Starts the game server by default") + Migrate := flag.Bool("Migrate", false, "Sets up the tables for the Sqlite database") - flag.Parse() + flag.Parse() - rand.Seed(time.Now().UnixNano()) - server := core.GetHttp() + rand.Seed(time.Now().UnixNano()) + server := core.GetHttp() - err := config.SetDefaults() - if err != nil { - return - } + err := config.SetDefaults() + if err != nil { + return + } - // running implementation to escape NAT - Server, barrireKVM, err := core.EscapeNAT(*port) - if err != nil { - log.Fatalln(err) - } + // Print out room information + if *roomInfo { + room, err := core.ReadRoomsFile() + if err != nil { + log.Fatalln(err) + } - Config, err := config.ConfigInit() - if err != nil { - log.Fatalln(err) - } + PrettyPrint(room) + return + } - Config.IPAddress = "64.227.168.102" - Config.NATEscapeServerPort = Server - Config.NATEscapeBarrierPort = barrireKVM + // Running in headless mode + if *headless { + Config, err := config.ConfigInit() + if err != nil { + log.Fatalln(err) + } - fmt.Println(Config) + // Returns the URl address type + Addr := Ip4or6(Config.IPAddress) - err = Config.WriteConfig() - if err != nil { - log.Fatalln(err) - } + // If address is provided + if *addr != "" { + Addr = *addr + // Add brackets if the ip address is ipv6 + Addr = Ip4or6(Addr) + } - // Print out room information - if *roomInfo { - room, err := core.ReadRoomsFile() - if err != nil { - log.Fatalln(err) - } + var TaskExecute string - PrettyPrint(room) - return - } + if *BinaryToExcute != "" { + TaskExecute = *BinaryToExcute + } else { + // Read binary from config file + TaskExecute = Config.ScriptToExecute + } - // Running in headless mode - if *headless { - Config, err := config.ConfigInit() - if err != nil { - log.Fatalln(err) - } + // Starting screen share headless + cmd := exec.Command("chromium-browser", "--no-sandbox", "--auto-select-desktop-capture-source=Entire screen", "--url", "https://"+Addr+":"+*port+"/?mode=headless", "--ignore-certificate-errors") + if err := cmd.Start(); err != nil { + log.Fatalln(err) + } - // Returns the URl address type - Addr := Ip4or6(Config.IPAddress) + // Makes program sleep for 2 seconds to allow chromium browser to open + time.Sleep(3 * time.Second) - // If address is provided - if *addr != "" { - Addr = *addr - // Add brackets if the ip address is ipv6 - Addr = Ip4or6(Addr) - } + // Task to be executed + err = RunTask(TaskExecute) + if err != nil { + fmt.Println(err) + return + } - var TaskExecute string + return - if *BinaryToExcute != "" { - TaskExecute = *BinaryToExcute - } else { - // Read binary from config file - TaskExecute = Config.ScriptToExecute - } + } - // Starting screen share headless - cmd := exec.Command("chromium-browser", "--no-sandbox", "--auto-select-desktop-capture-source=Entire screen", "--url", "https://"+Addr+":"+*port+"/?mode=headless", "--ignore-certificate-errors") - if err := cmd.Start(); err != nil { - log.Fatalln(err) - } + // kills laplace server + if *killServer { + cmd := exec.Command("pkill", "remotegameplay") + if err := cmd.Run(); err != nil { + fmt.Println(err) + } + return + } + // kills chromium server + if *killChromium { + cmd := exec.Command("pkill", "chromium") + if err := cmd.Run(); err != nil { + fmt.Println(err) + } + return + } - // Makes program sleep for 2 seconds to allow chromium browser to open - time.Sleep(3 * time.Second) + if *Migrate { + // Connect to Sqlite + connect, err := gameserver.Connect() + if err != nil { + fmt.Println(err) + } + // Migrate table + session, err := gameserver.CreateTables(connect) + if err != nil { + fmt.Println(err) + } + session.Scan(gameserver.GameSession{}) - // Task to be executed - err = RunTask(TaskExecute) - if err != nil { - fmt.Println(err) - return - } + return + } - return + if *GameServer { + gameserver.Server(*port) + } else { + if *tls { + log.Println("Listening on TLS:", *addr+":"+*port) + if err := http.ListenAndServeTLS(*addr+":"+*port, *certFile, *keyFile, server); err != nil { + log.Fatalln(err) + } + } else { + log.Println("Listening:", *addr+":"+*port) + if err := http.ListenAndServe(*addr+":"+*port, server); err != nil { + log.Fatalln(err) + } + } + } - } + // running implementation to escape NAT + Server, barrireKVM, err := core.EscapeNAT(*port) + if err != nil { + log.Fatalln(err) + } - // kills laplace server - if *killServer { - cmd := exec.Command("pkill", "remotegameplay") - if err := cmd.Run(); err != nil { - fmt.Println(err) - } - return - } - // kills chromium server - if *killChromium { - cmd := exec.Command("pkill", "chromium") - if err := cmd.Run(); err != nil { - fmt.Println(err) - } - return - } + Config, err := config.ConfigInit() + if err != nil { + log.Fatalln(err) + } - if *tls { - log.Println("Listening on TLS:", *addr+":"+*port) - if err := http.ListenAndServeTLS(*addr+":"+*port, *certFile, *keyFile, server); err != nil { - log.Fatalln(err) - } - } else { - log.Println("Listening:", *addr+":"+*port) - if err := http.ListenAndServe(*addr+":"+*port, server); err != nil { - log.Fatalln(err) - } - } + Config.IPAddress = "64.227.168.102" + Config.NATEscapeServerPort = Server + Config.NATEscapeBarrierPort = barrireKVM - // Start P2PRC server - //_, err = abstractions.Start() - //if err != nil { - // return - //} + err = Config.WriteConfig() + if err != nil { + log.Fatalln(err) + } + + err = core.BroadcastServerToBackend() + if err != nil { + fmt.Println(err) + } else { + fmt.Println("success broadcasting to game server") + } + + // Start P2PRC server + //_, err = abstractions.Start() + //if err != nil { + // return + //} } // PrettyPrint print the contents of the obj ( // Reference: https://stackoverflow.com/questions/24512112/how-to-print-struct-variables-in-console func PrettyPrint(data interface{}) { - var p []byte - // var err := error - p, err := json.MarshalIndent(data, "", "\t") - if err != nil { - fmt.Println(err) - return - } - fmt.Printf("%s \n", p) + var p []byte + // var err := error + p, err := json.MarshalIndent(data, "", "\t") + if err != nil { + fmt.Println(err) + return + } + fmt.Printf("%s \n", p) } // Ip4or6 Helper function to check if the IP address is IPV4 or // IPV6 (https://socketloop.com/tutorials/golang-check-if-ip-address-is-version-4-or-6) func Ip4or6(s string) string { - for i := 0; i < len(s); i++ { - switch s[i] { - case '.': - return s - case ':': - return "[" + s + "]" - } - } - return "[" + s + "]" + for i := 0; i < len(s); i++ { + switch s[i] { + case '.': + return s + case ':': + return "[" + s + "]" + } + } + return "[" + s + "]" } func RunTask(task string) error { - // Halts the process - cmd := exec.Command("sh", task) - if err := cmd.Start(); err != nil { - return err - } + // Halts the process + cmd := exec.Command("sh", task) + if err := cmd.Start(); err != nil { + return err + } - return nil + return nil } diff --git a/server/auth/auth.go b/server/auth/auth.go new file mode 100644 index 0000000..1130b50 --- /dev/null +++ b/server/auth/auth.go @@ -0,0 +1,16 @@ +package auth + +import ( + "crypto/sha256" +) + + +// HashPassword Hash password passed through the +// parameter +func HashPassword(Password string) (string, error) { + data := []byte(Password) + hash := sha256.Sum256(data) + // returning hash as a string + return string(hash[:]), nil +} + diff --git a/server/auth/auth_test.go b/server/auth/auth_test.go new file mode 100644 index 0000000..1bf2973 --- /dev/null +++ b/server/auth/auth_test.go @@ -0,0 +1,17 @@ +package auth + +import ( + "fmt" + "testing" +) + +// Function to test if the password is hashed +func TestHashPassword(t *testing.T) { + Password, err := HashPassword("AKILAN1999") + if err != nil { + fmt.Println(err) + t.Fail() + } + fmt.Println("Password Hash") + fmt.Println(Password) +} diff --git a/server/connection.go b/server/connection.go new file mode 100644 index 0000000..1aaf40f --- /dev/null +++ b/server/connection.go @@ -0,0 +1,15 @@ +package server + +import ( + "gorm.io/driver/sqlite" + "gorm.io/gorm" +) + +// Connect Connection to the Sqlite database +func Connect() (*gorm.DB, error) { + db, err := gorm.Open(sqlite.Open("xplane11-webRTC.db"), &gorm.Config{}) + if err != nil { + return nil, err + } + return db, nil +} diff --git a/server/connection_test.go b/server/connection_test.go new file mode 100644 index 0000000..ed6c9a3 --- /dev/null +++ b/server/connection_test.go @@ -0,0 +1,16 @@ +package server + +import ( + "fmt" + "testing" +) + +// Testing connection to the Database +func TestConnect(t *testing.T) { + // Connect to database created + _, err := Connect() + if err != nil { + fmt.Println(err) + t.Fail() + } +} \ No newline at end of file diff --git a/server/login.go b/server/login.go new file mode 100644 index 0000000..b6ffbf2 --- /dev/null +++ b/server/login.go @@ -0,0 +1,26 @@ +package server + +import ( + "errors" + "github.com/Akilan1999/remotegameplay/server/auth" + "gorm.io/gorm" +) + +func AuthLogin(db *gorm.DB, user *Users) (string, error) { + // Generate Hash from password + password, err := auth.HashPassword(user.Password) + if err != nil { + return "", err + } + + match, err := CheckIfEmailAndPasswordMatch(db, user.EmailID, password) + if err != nil { + return "", err + } + + if match == "success" { + return "success", nil + } + + return "", errors.New("Something is wrong with from our side. Email us: me@akilan.io to find out more. ") +} diff --git a/server/mailServer.go b/server/mailServer.go new file mode 100644 index 0000000..fa3a760 --- /dev/null +++ b/server/mailServer.go @@ -0,0 +1,44 @@ +package server + +// MailerSend This function sends an automated email to the +// person requesting it +func MailerSend(subject, email, message string) error { + // Load email credentials from the configuration file + //Config, err := config.ConfigInit() + //if err != nil { + // return err + //} + + // sender configuration. + //config := mailer.Config{ + // Host: Config.EmailHost, + // Username: Config.EmailID, + // Password: Config.EmailPassword, + // FromAddr: Config.EmailID, + // Port: Config.EmailPort, + // // Enable UseCommand to support sendmail unix command, + // // if this field is true then Host, Username, Password and Port are not required, + // // because this info already exists in your local sendmail configuration. + // // + // // Defaults to false. + // UseCommand: false, + //} + + // initialize a new mail sender service. + //sender := mailer.New(config) + // + //// the rich message body. + //content := message + // + //// the recipient(s). + //to := []string{email} + // + //// send the e-mail. + //err = sender.Send(subject, content, to...) + // + //if err != nil { + // return err + //} + + return nil +} diff --git a/server/mailServer_test.go b/server/mailServer_test.go new file mode 100644 index 0000000..9411529 --- /dev/null +++ b/server/mailServer_test.go @@ -0,0 +1,14 @@ +package server + +import ( + "fmt" + "testing" +) + +func TestMailerSend(t *testing.T) { + err := MailerSend("Test", "akilanselva@hotmail.com", "Welcome from Xplane=WebRTC") + if err != nil { + fmt.Println(err) + t.Fail() + } +} diff --git a/server/models.go b/server/models.go new file mode 100644 index 0000000..c6e9293 --- /dev/null +++ b/server/models.go @@ -0,0 +1,341 @@ +package server + +import ( + "errors" + "github.com/Akilan1999/remotegameplay/server/auth" + "github.com/google/uuid" + "gorm.io/gorm" +) + +//var users []User = []User{ +// User{Username: "foobar", FirstName: "Foo", LastName: "Bar", Salary: 200}, +// User{Username: "helloworld", FirstName: "Hello", LastName: "World", Salary: 200}, +// User{Username: "john", FirstName: "John", Salary: 200}, +//} + +// LoginSession Storing information of the login session +type LoginSession struct { + SessionKey string `json:"SessionKey"` + UserID string `json:"UserID"` + User Users `gorm:"foreignKey:UserID;references:UserID" json:"Users"` +} + +// Users This struct focuses on storing user +// information +type Users struct { + UserID string `json:"userID"` + Name string `form:"Name" json:"Name"` + Password string `form:"Password" binding:"required" json:"Password"` + EmailID string `form:"EmailID" binding:"required" json:"EmailID"` +} + +// GameSession A single Game session. In the following implementation +// the server can have only 1 user occupying it por session. +type GameSession struct { + GameSessionID string `json:"GameSessionID"` + // Link of the stream started for gameplay + Link string `form:"Link" binding:"required" json:"LinkID"` + ServerID string `json:"ServerID"` + Rate float64 `form:"Rate" binding:"required" json:"Rate"` + Server ServerSpecs `gorm:"foreignKey:ID;references:ServerID" json:"ServerInformation"` + // State of the server if it's in use + // or free + State bool + UserID string `json:"UserID"` + User Users `gorm:"foreignKey:UserID;references:UserID" json:"ServerInformation"` +} + +// ServerSpecs Server specs information +type ServerSpecs struct { + ID string `bson: ID` + Hostname string `form:"Hostname" binding:"required" bson:hostname` + Platform string `form:"Platform" binding:"required" bson:platform` + CPU string `form:"CPU" binding:"required" bson:cpu` + RAM string `form:"RAM" binding:"required" bson:ram` + Disk string `form:"Disk" binding:"required" bson:disk` + GPU string `form:"GPU" binding:"required" bson:GPU` +} + +// CreateTables CreateDB Add tables to the database +func CreateTables(db *gorm.DB) (*gorm.DB, error) { + // Creates table to store login sessions + db.AutoMigrate(LoginSession{}) + // Creates table to store user information + db.AutoMigrate(Users{}) + // Creates table of type GameSessions + db.AutoMigrate(GameSession{}) + // Creates table of type ServerSpecs + db.AutoMigrate(ServerSpecs{}) + // returns variable DB of type *gorm.DB and error + // which is nil at the current moment + return db, nil +} + +// RemoveTableGameSession Removes table of type +func RemoveTableGameSession(db *gorm.DB) (*gorm.DB, error) { + var gameSession GameSession + // Creates table of type GameSessions + db.Delete(gameSession) + // returns variable DB of type *gorm.DB and error + // which is nil at the current moment + return db, nil +} + +// DisplayGameSessions Returns all the rows of all the game session information +// to display +func DisplayGameSessions(db *gorm.DB) ([]*GameSession, error) { + // select * from GameSessions + rows, err := db.Model(&GameSession{}).Rows() + if err != nil { + return nil, err + } + defer rows.Close() + + // Variable to store information of all game sessions + var GameSessions []*GameSession + + // Iterates through all game session rows + for rows.Next() { + var gameSession GameSession + err := db.ScanRows(rows, &gameSession) + if err != nil { + return nil, err + } + + // GetServerSpecs based on ServerSpecs ID + // derived from the game session + specs, err := GetSeverSpecs(db, gameSession.ServerID) + if err != nil { + return nil, err + } + // Add server information to the struct Game session + gameSession.Server = *specs + + // Append game result to the array + GameSessions = append(GameSessions, &gameSession) + } + + return GameSessions, nil +} + +// GetSeverSpecs Get server specs information based on the ID provided +func GetSeverSpecs(db *gorm.DB, ID string) (*ServerSpecs, error) { + // query to get server specs to that game session + ServerSpecsRows, err := db.Model(&ServerSpecs{}).Where("ID = ?", ID).Rows() + if err != nil { + return nil, err + } + defer ServerSpecsRows.Close() + + // Variable to store server specs + var serverSpecs ServerSpecs + + // iterate thought the game session available in that server + for ServerSpecsRows.Next() { + err := db.ScanRows(ServerSpecsRows, &serverSpecs) + if err != nil { + return nil, err + } + } + + return &serverSpecs, nil +} + +// AddServerSpecs Add server specs +func AddServerSpecs(db *gorm.DB, gameSession *GameSession) error { + // Generate Random UUID for Game Session and server specs + gameSessionID, err := uuid.NewUUID() + if err != nil { + return err + } + serverSpecsID, err := uuid.NewRandom() + if err != nil { + return err + } + // Create for entry for server specs because + // the game session foreign relies on the + // server specs primary key + gameSession.Server.ID = serverSpecsID.String() + + // Adding the IDs for both the game session and + // the foreign key for server specs + gameSession.GameSessionID = gameSessionID.String() + gameSession.ServerID = serverSpecsID.String() + + // Create Game Session + db.Create(gameSession) + + return nil +} + +// CheckIfEmailExits Function to check if the username has been taken already or not +func CheckIfEmailExits(db *gorm.DB, Email string) error { + UserRows, err := db.Model(&Users{}).Where("email_id = ?", Email).Rows() + if err != nil { + return err + } + defer UserRows.Close() + + if UserRows.Next() { + return errors.New("Email already taken. ") + } + return nil +} + +// RegisterUser Function to insert new user information +func RegisterUser(db *gorm.DB, UserInformation *Users) error { + // Check if the username is already taken + err := CheckIfEmailExits(db, UserInformation.EmailID) + if err != nil { + return err + } + + // Generate UserID using UUID + id := uuid.New() + UserInformation.UserID = id.String() + + // Generate Hash for the plaintext password + password, err := auth.HashPassword(UserInformation.Password) + if err != nil { + return err + } + UserInformation.Password = password + // Insert the record to the database + db.Create(UserInformation) + + // Send email to the user that the user is successfully created + //err = MailerSend("Xplane 11 WebRTC", UserInformation.EmailID, `

Welcome to Xplane 11 WebRTC ! `+UserInformation.Name+`

`) + //if err != nil { + // return err + //} + + return nil +} + +// CheckIfEmailAndPasswordMatch Check email and password information for login +func CheckIfEmailAndPasswordMatch(db *gorm.DB, emailID string, Password string) (string, error) { + UserRows, err := db.Model(&Users{}).Where("email_id = ? AND password = ?", emailID, Password).Rows() + if err != nil { + return "", err + } + defer UserRows.Close() + + if UserRows.Next() { + return "success", nil + } + + return "", errors.New("Wrong login email or password ") +} + +// GetUserID Gets userID based on the EmailID +func GetUserID(db *gorm.DB, emailID string) (string, error) { + UserRows, err := db.Model(&Users{}).Where("email_id = ? ", emailID).Rows() + if err != nil { + return "", err + } + defer UserRows.Close() + + // Variable to store user information + var users Users + + for UserRows.Next() { + err = db.ScanRows(UserRows, &users) + if err != nil { + return "", err + } + return users.UserID, nil + } + + return "", errors.New("User not found. ") +} + +// getUserInformation Gets user information based on the user ID provided +func getUserInformation(db *gorm.DB, ID string) (*Users, error) { + UserRows, err := db.Model(&Users{}).Where("user_id = ? ", ID).Rows() + if err != nil { + return nil, err + } + defer UserRows.Close() + + // Variable to store user information + var users Users + + for UserRows.Next() { + err = db.ScanRows(UserRows, &users) + if err != nil { + return nil, err + } + return &users, nil + } + + return nil, errors.New("User not found. ") +} + +// CreateLoginSession function to create login session and setting expiry time +// for the login session +func CreateLoginSession(db *gorm.DB, user *Users) (*LoginSession, error) { + // Get userID to be added to the session + userID, err := GetUserID(db, user.EmailID) + if err != nil { + return nil, err + } + // Generating Session ID + newUUID, err := uuid.NewRandom() + if err != nil { + return nil, err + } + + // Create login session + var loginSession LoginSession + // Adding UUID key as session ID + loginSession.SessionKey = newUUID.String() + // Adding User ID to link to the user information + loginSession.UserID = userID + + // Add session information to the table sessions + db.Create(loginSession) + + return &loginSession, nil +} + +// RemoveLoginSession Removes session from the database based on the session ID provided +func RemoveLoginSession(db *gorm.DB, SessionID string) error { + // Delete session from the database + err := db.Where("session_key = ?", SessionID).Delete(LoginSession{}) + if err != nil { + return err.Error + } + return nil +} + +// GetUserInformation Gets the user information based on the session ID provided +func GetUserInformation(db *gorm.DB, SessionID string) (*Users, error) { + // Gets user ID from Session ID + // select * from LoginSessions where session_key = + rows, err := db.Model(&LoginSession{}).Where("session_key = ?", SessionID).Rows() + if err != nil { + return nil, err + } + defer rows.Close() + + // Iterates through all game session rows + for rows.Next() { + var loginSession LoginSession + err := db.ScanRows(rows, &loginSession) + if err != nil { + return nil, err + } + + // Get User information based on the User ID + user, err := getUserInformation(db, loginSession.UserID) + if err != nil { + return nil, err + } + // Ensure the password field is empty + user.Password = "" + // return struct user + return user, nil + } + + return nil, errors.New("Session not found. ") +} diff --git a/server/models_test.go b/server/models_test.go new file mode 100644 index 0000000..4b08896 --- /dev/null +++ b/server/models_test.go @@ -0,0 +1,209 @@ +package server + +import ( + "encoding/json" + "fmt" + "gorm.io/gorm" + "testing" +) + +// helper functions: These functions are designed +// to reduce repetitive steps +func CreateAndInsertData() (*gorm.DB,error) { + // Connect to database created + DB , err := Connect() + if err != nil { + return nil,err + } + + // Create the appropriate table + DB, err = CreateTables(DB) + if err != nil { + return nil,err + } + + DB.Scan(&GameSession{}) + + // Setting dummy data for the game session + var Session GameSession + Session.Link = "Test" + Session.ServerID = "1" + Session.GameSessionID = "1" + + Session.Server = ServerSpecs{ID: "1",Hostname : "Test",GPU: "Nvidia GTX1080ti"} + + // Insert test rows + DB.Model(GameSession{}).Create(&Session) + + res := DB.Scan(GameSession{}) + fmt.Println(res.Error) + // prints row count + fmt.Println(res.RowsAffected) + + return DB,nil +} + +// PrettyPrint print the contents of the obj ( +// Reference: https://stackoverflow.com/questions/24512112/how-to-print-struct-variables-in-console +func PrettyPrint(data interface{}) { + var p []byte + // var err := error + p, err := json.MarshalIndent(data, "", "\t") + if err != nil { + fmt.Println(err) + return + } + fmt.Printf("%s \n", p) +} + +// To Ensure GameSession table is created +func TestCreateTableGameSession(t *testing.T) { + // Connect to database created + DB , err := Connect() + if err != nil { + fmt.Println(err) + t.Fail() + } + DB, err = CreateTables(DB) + if err != nil { + fmt.Println(err) + t.Fail() + } + + DB.Scan(&GameSession{}) + +} + +// To ensure the GameSession table is removed +func TestRemoveTableGameSession(t *testing.T) { + // Connect to database created + DB , err := Connect() + if err != nil { + fmt.Println(err) + t.Fail() + } + DB, err = RemoveTableGameSession(DB) + if err != nil { + fmt.Println(err) + t.Fail() + } + + DB.Scan(&GameSession{}) +} + +// Testing Insert rows +func TestInsertAndDeleteRows(t *testing.T) { + // Connect to database created + DB , err := Connect() + if err != nil { + fmt.Println(err) + t.Fail() + } + + // Create the appropriate table + DB, err = CreateTables(DB) + if err != nil { + fmt.Println(err) + t.Fail() + } + + DB.Scan(&GameSession{}) + + // Setting dummy data for the game session + var Session GameSession + Session.Link = "https://2.49.230.55:8888/?id=fervent_quizzical_whippet" + Session.ServerID = "1" + Session.GameSessionID = "1" + + Session.Server = ServerSpecs{ID: "1",Hostname: "akilan-Swift-SF514-54GT",GPU: "NvidiaGT 775m", RAM: 8000, CPU: "Intel(R) Core(TM) i7-1065G7 CPU @ 1.30GHz" } + + // Insert test rows + DB.Model(GameSession{}).Create(&Session) + + res := DB.Scan(GameSession{}) + fmt.Println(res.Error) + // prints row count + fmt.Println(res.RowsAffected) + + // remove rows from the table created + // Delete the row from the server specs table + //DB.Delete(&ServerSpecs{}, 1) + // Delete table from GameSession Table + //DB.Delete(&GameSession{}, 1) +} + +// Test function to display all rows of the game +// session +func TestDisplayGameSessions(t *testing.T) { + // Creates a test game session table and + // inserts dummy data + DB, err := CreateAndInsertData() + if err != nil { + fmt.Println(err) + t.Fail() + } + // Gets information about all the rows + Result , err := DisplayGameSessions(DB) + if err != nil { + fmt.Println(err) + t.Fail() + } + + PrettyPrint(Result) + + //defer rows.Close() + + //var gameSession GameSession + // + //// prints out the results + //for rows.Next() { + // DB.ScanRows(rows, &gameSession) + // fmt.Println(gameSession.ServerID) + //} + +} + +// Function to check if the user information is +// getting successfully inserted into the database +func TestRegisterUser(t *testing.T) { + // Create a database connection + connect, err := Connect() + if err != nil { + fmt.Println(err) + t.Fail() + } + // Setting test registration information + users := Users{Password: "Test123", EmailID: "akilanselva@hotmail.com"} + + err = RegisterUser(connect, &users) + if err != nil { + fmt.Println(err) + t.Fail() + } + +} + +// Tests to see the game session and server specs are +// added +func TestAddServerSpecs(t *testing.T) { + // Create a database connection + connect, err := Connect() + if err != nil { + fmt.Println(err) + t.Fail() + } + + // Setting dummy data for the game session + var Session GameSession + Session.Link = "Test" + + Session.Server = ServerSpecs{Hostname : "Test",GPU: "Nvidia GTX1080ti"} + + // Calling server session + err = AddServerSpecs(connect, &Session) + if err != nil { + fmt.Println(err) + t.Fail() + } + +} diff --git a/server/server.go b/server/server.go new file mode 100644 index 0000000..8006c9d --- /dev/null +++ b/server/server.go @@ -0,0 +1,188 @@ +package server + +import ( + "fmt" + "github.com/gin-gonic/gin" + "net/http" + "strconv" +) + +// Server Starts the server for the client side +func Server(port string) error { + + r := gin.Default() + + r.Static("/assets", "./templates/assets") + + r.LoadHTMLGlob("./templates/*.html") + // htmlRender.TemplatesDir = "templates/" // default + // htmlRender.Ext = ".html" // default + + // Tell gin to use our html render + + // Initialise connection to Sqlite DB + connect, err := Connect() + if err != nil { + return err + } + + // Gets default information about the game sessions + r.GET("/", func(c *gin.Context) { + // Get client session ID + session, err := c.Cookie("SessionID") + if err != nil { + c.Redirect(http.StatusFound, "/login") + } + + // User information based on session ID + User, err := GetUserInformation(connect, session) + if err != nil { + c.Redirect(http.StatusFound, "/login") + } + + // Get information of all game sessions available + GameSessions, err := DisplayGameSessions(connect) + if err != nil { + c.String(http.StatusOK, fmt.Sprint(err)) + } + c.HTML(http.StatusOK, "index.html", gin.H{ + "title": "Xplane11-WebRTC", + "GameSessions": GameSessions, + "User": User, + }) + }) + + // Opens the forgot password page + r.GET("/forgotpassword", func(c *gin.Context) { + c.HTML(http.StatusOK, "forgotpassword.html", gin.H{ + "title": "Xplane11-WebRTC", + }) + }) + + // Opens the login page + r.GET("/login", func(c *gin.Context) { + c.HTML(http.StatusOK, "login.html", gin.H{ + "title": "Xplane11-WebRTC", + }) + }) + + // Validate login information + r.POST("/login", func(c *gin.Context) { + // you can bind multipart form with explicit binding declaration: + // c.ShouldBindWith(&form, binding.Form) + // or you can simply use autobinding with ShouldBind method: + // source: https://github.com/gin-gonic/gin#multiparturlencoded-binding + var users Users + // in this case proper binding will be automatically selected + if err := c.ShouldBind(&users); err != nil { + c.String(http.StatusBadRequest, "bad request") + } + + result, err := AuthLogin(connect, &users) + if err != nil { + c.String(http.StatusOK, fmt.Sprint(err)) + } else { + if result == "success" { + // Create Login session + session, err := CreateLoginSession(connect, &users) + if err != nil { + c.String(http.StatusExpectationFailed, "error") + } + // TODO redirect to homepage + // Set client Cookie as Session ID + // To be changed when we use HTTPS + c.SetCookie("SessionID", session.SessionKey, 3600, "/", "localhost", false, true) + // redirects to the home page + //c.Redirect(http.StatusFound, "/") + + // Sends message success. which then redirects back to the home page + c.String(http.StatusOK, "Success!") + } else { + c.String(http.StatusOK, "Something is wrong with from our side. Email us: me@akilan.io to find out more. ") + } + } + }) + + // Route for logout + r.GET("/logout", func(c *gin.Context) { + // Get client session ID + session, err := c.Cookie("SessionID") + if err != nil { + c.Redirect(http.StatusFound, "/login") + } + + // Remove Session ID from the database + RemoveLoginSession(connect, session) + + // redirect to the login page + c.Redirect(http.StatusFound, "/login") + }) + + // Opens the registration page + r.GET("/register", func(c *gin.Context) { + c.HTML(http.StatusOK, "registration.html", gin.H{ + "title": "Xplane11-WebRTC", + }) + }) + + // Register user information required + r.POST("/register", func(c *gin.Context) { + // you can bind multipart form with explicit binding declaration: + // c.ShouldBindWith(&form, binding.Form) + // or you can simply use autobinding with ShouldBind method: + // source: https://github.com/gin-gonic/gin#multiparturlencoded-binding + var users Users + // in this case proper binding will be automatically selected + if err := c.ShouldBind(&users); err != nil { + c.String(http.StatusBadRequest, "bad request") + } + + err = RegisterUser(connect, &users) + if err != nil { + c.String(http.StatusOK, fmt.Sprint(err)) + } else { + c.String(http.StatusOK, "Success! Head back to the login page") + } + }) + + // Add new server information + r.POST("/AddGameSession", func(c *gin.Context) { + + // TODO: Binding + // you can bind multipart form with explicit binding declaration: + // c.ShouldBindWith(&form, binding.Form) + // or you can simply use autobinding with ShouldBind method: + // source: https://github.com/gin-gonic/gin#multiparturlencoded-binding + var gameSession GameSession + + gameSession.Rate, _ = strconv.ParseFloat(c.PostForm("Rate"), 8) + gameSession.Server.GPU = c.PostForm("GPU") + gameSession.Link = c.PostForm("Link") + gameSession.Server.CPU = c.PostForm("CPU") + gameSession.Server.GPU = c.PostForm("GPU") + gameSession.Server.Platform = c.PostForm("Platform") + gameSession.Server.Hostname = c.PostForm("HostName") + gameSession.Server.RAM = c.PostForm("RAM") + gameSession.Server.Disk = c.PostForm("Disk") + + // in this case proper binding will be automatically selected + //if err := c.ShouldBind(&gameSession); err != nil { + // c.String(http.StatusBadRequest, "bad request") + //} + + err = AddServerSpecs(connect, &gameSession) + if err != nil { + c.String(http.StatusOK, fmt.Sprint(err)) + } else { + c.String(http.StatusOK, "Success") + } + }) + + // Run gin server on the specified port + err = r.Run(":" + port) + if err != nil { + return err + } + + return nil +} diff --git a/templates/addgamesession.html b/templates/addgamesession.html new file mode 100644 index 0000000..4c59ad1 --- /dev/null +++ b/templates/addgamesession.html @@ -0,0 +1,81 @@ + + + + + + + + Admin - Free Bulma template + + + + + + + + + + + + +
+
+
+
+
+
+

+ Hello, User. +

+

+ I hope you are having a great day! +

+ +
+
+
+
+
+ {{ range .GameSessions }} +
+
+

{{ .Server.GPU }}

+

RAM: {{ .Server.RAM }}

+

Rate: 1$/hr

+

CPU: {{ .Server.CPU }}

+

Disk: {{ .Server.Disk }}

+
+ Play +
+
+ {{ end }} +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/templates/assets/css/admin.css b/templates/assets/css/admin.css new file mode 100644 index 0000000..9b87c7b --- /dev/null +++ b/templates/assets/css/admin.css @@ -0,0 +1,82 @@ +html, body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + font-size: 16px; + line-height: 1.5; + height: 100%; + background: #ECF0F3; +} +nav.navbar { + border-top: 4px solid #276cda; + margin-bottom: 1rem; +} +.navbar-item.brand-text { + font-weight: 300; +} +.navbar-item, .navbar-link { + font-size: 14px; + font-weight: 700; +} +.columns { + width: 100%; + height: 100%; + margin-left: 0; +} +.menu-label { + color: #8F99A3; + letter-spacing: 1.3; + font-weight: 700; +} +.menu-list a { + color: #0F1D38; + font-size: 14px; + font-weight: 700; +} +.menu-list a:hover { + background-color: transparent; + color: #276cda; +} +.menu-list a.is-active { + background-color: transparent; + color: #276cda; + font-weight: 700; +} +.card { + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.18); + margin-bottom: 2rem; +} +.card-header-title { + color: #8F99A3; + font-weight: 400; +} +.info-tiles { + margin: 1rem 0; +} +.info-tiles .subtitle { + font-weight: 300; + color: #8F99A3; +} +.hero.welcome.is-info { + background: #36D1DC; + background: -webkit-linear-gradient(to right, #5B86E5, #36D1DC); + background: linear-gradient(to right, #5B86E5, #36D1DC); +} +.hero.welcome .title, .hero.welcome .subtitle { + color: hsl(192, 17%, 99%); +} +.card .content { + font-size: 14px; +} +.card-footer-item { + font-size: 14px; + font-weight: 700; + color: #8F99A3; +} +.card-footer-item:hover { +} +.card-table .table { + margin-bottom: 0; +} +.events-card .card-table { + max-height: 250px; + overflow-y: scroll; +} \ No newline at end of file diff --git a/templates/assets/css/register.css b/templates/assets/css/register.css new file mode 100644 index 0000000..a2d4d98 --- /dev/null +++ b/templates/assets/css/register.css @@ -0,0 +1,4 @@ +.va { + display: flex; + align-items: center; +} \ No newline at end of file diff --git a/templates/assets/js/bulma.js b/templates/assets/js/bulma.js new file mode 100644 index 0000000..23ffa0a --- /dev/null +++ b/templates/assets/js/bulma.js @@ -0,0 +1,10 @@ +// The following code is based off a toggle menu by @Bradcomp +// source: https://gist.github.com/Bradcomp/a9ef2ef322a8e8017443b626208999c1 +(function() { + var burger = document.querySelector('.burger'); + var menu = document.querySelector('#'+burger.dataset.target); + burger.addEventListener('click', function() { + burger.classList.toggle('is-active'); + menu.classList.toggle('is-active'); + }); +})(); \ No newline at end of file diff --git a/templates/forgotpassword.html b/templates/forgotpassword.html new file mode 100644 index 0000000..bbaa162 --- /dev/null +++ b/templates/forgotpassword.html @@ -0,0 +1,153 @@ + + + + + + + + Register - Free Bulma template + + + + + + + +
+
+
+
+
+

Xplane 11 WebRTC

+
+

Enjoy the XPlane 11 gameplay performance without any setup

+

+
+
+

Forgot password

+

We will send a reset password link to your email

+
+
+
+ +
+
+ + +
+ Create a new account +
+ Already have an account ? +
+ +
+
+
+
+
+
+ +
+
+
+ + + + \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..baa473b --- /dev/null +++ b/templates/index.html @@ -0,0 +1,85 @@ + + + + + + + + Admin - Free Bulma template + + + + + + + + + + + + +
+
+
+
+
+
+

+ Hello, {{ .User.Name }} +

+

+ I hope you are having a great day! +

+ +
+
+
+
+
+ {{ range .GameSessions }} +
+
+

{{ .Server.GPU }}

+

RAM: {{ .Server.RAM }} MB

+

Rate: {{ .Rate }}$/hr

+

CPU: {{ .Server.CPU }}

+

Disk: {{ .Server.Disk }} MB

+

Platform: {{ .Server.Platform }}

+
+ Play +
+
+ {{ end }} +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..64abb3b --- /dev/null +++ b/templates/login.html @@ -0,0 +1,218 @@ + + + + + + + + Register - Free Bulma template + + + + + + + +
+
+
+
+
+

Xplane 11 WebRTC

+
+

Enjoy the XPlane 11 gameplay performance without any setup

+

+
+
+

Login

+

We cool so long as your computer supports a browser

+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+

+

+

+
+
+ + +
+ Create a new account +
+ Forgot password ? +
+ +
+
+
+
+
+
+ +
+
+
+ + + + + \ No newline at end of file diff --git a/templates/registration.html b/templates/registration.html new file mode 100644 index 0000000..c7d5218 --- /dev/null +++ b/templates/registration.html @@ -0,0 +1,249 @@ + + + + + + + + Register - Free Bulma template + + + + + + + +
+
+
+
+
+

Xplane 11 WebRTC

+
+

Enjoy the XPlane 11 gameplay performance without any setup

+

+
+
+

Sign up today

+

We cool so long as your computer supports a browser

+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

+

+

+

+
+
+ + +
+ Already have an account ? +
+ Forgot password ? +
+
+ +
+
+
+
+
+
+ +
+
+
+ + + + + + \ No newline at end of file