connected remotegameprivate and backend

This commit is contained in:
2023-02-24 15:41:40 +00:00
parent 574317d286
commit 0fadadee2d
24 changed files with 2101 additions and 151 deletions

16
server/auth/auth.go Normal file
View File

@@ -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
}

17
server/auth/auth_test.go Normal file
View File

@@ -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)
}

15
server/connection.go Normal file
View File

@@ -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
}

16
server/connection_test.go Normal file
View File

@@ -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()
}
}

26
server/login.go Normal file
View File

@@ -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. ")
}

44
server/mailServer.go Normal file
View File

@@ -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
}

14
server/mailServer_test.go Normal file
View File

@@ -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()
}
}

341
server/models.go Normal file
View File

@@ -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, `<h1>Welcome to Xplane 11 WebRTC ! `+UserInformation.Name+`</h1>`)
//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 = <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. ")
}

209
server/models_test.go Normal file
View File

@@ -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()
}
}

188
server/server.go Normal file
View File

@@ -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
}