diff --git a/main.go b/main.go index b5b55cb..fe750b7 100644 --- a/main.go +++ b/main.go @@ -4,15 +4,15 @@ import ( "log" "net/http" - "github.com/Ennovar/gPanel/pkg/server" + "github.com/Ennovar/gPanel/pkg/gpserver" "github.com/gorilla/context" ) func main() { - gpServer := server.New() + mains := gpserver.New() log.Printf("To Exit: CTRL+C") log.Print("Listening (server) on localhost:2083, serving out of the server/document_root/ directory...") - http.ListenAndServe("localhost:2083", context.ClearHandler(gpServer)) + http.ListenAndServe("localhost:2083", context.ClearHandler(mains)) } diff --git a/pkg/api/api_handler.go b/pkg/api/api_handler.go deleted file mode 100644 index 87f814f..0000000 --- a/pkg/api/api_handler.go +++ /dev/null @@ -1,50 +0,0 @@ -// Package api handles all API calls -package api - -import ( - "net/http" - "strings" - - "github.com/Ennovar/gPanel/pkg/api/log" - "github.com/Ennovar/gPanel/pkg/api/server" - "github.com/Ennovar/gPanel/pkg/api/user" - "github.com/Ennovar/gPanel/pkg/public" -) - -// HandleAPI function takes a path and determines if it is an API call, if it is it will -// call the specified API. It returns two booleans, the first being if it is an API call and -// the second is the response of the API call's function. -// -// The path of an API is formatted as such: document_roots/webhost/api/some/thing. The path is split -// by the sequence of characters "api", returning the first half of the URL which is discarded, and the -// second half which will look like /some/thing. The second half is processed to see whether or not it is -// a valid API, and then handled accordingly from there. -func HandleAPI(res http.ResponseWriter, req *http.Request, path string, publicServer *public.Controller) (bool, bool) { - splitUrl := strings.SplitN(path, "api", 2) - suspectApi := strings.ToLower(splitUrl[len(splitUrl)-1]) - - switch suspectApi { - case "/user/auth": - return true, user.Auth(res, req) - case "/user/register": - return true, user.Register(res, req) - case "/user/logout": - return true, user.Logout(res, req) - case "/server/status": - return true, server.Status(res, req, publicServer) - case "/server/start": - return true, server.Start(res, req, publicServer) - case "/server/shutdown": - return true, server.Shutdown(res, req, publicServer) - case "/server/restart": - return true, server.Restart(res, req, publicServer) - case "/server/maintenance": - return true, server.Maintenance(res, req, publicServer) - case "/log/read": - return true, log.Read(res, req) - case "/log/delete": - return true, log.Delete(res, req) - default: - return false, false - } -} diff --git a/pkg/api/user/auth.go b/pkg/api/user/auth.go index a83e3ed..69a0afb 100644 --- a/pkg/api/user/auth.go +++ b/pkg/api/user/auth.go @@ -4,6 +4,7 @@ package user import ( "encoding/json" "net/http" + "strings" "time" "github.com/Ennovar/gPanel/pkg/database" @@ -15,7 +16,7 @@ import ( // Auth function is accessed by an API call from the webhost root // by accessing /user_auth and sending it a post request with userRequestData // struct in JSON format. -func Auth(res http.ResponseWriter, req *http.Request) bool { +func Auth(res http.ResponseWriter, req *http.Request, dir string) bool { if req.Method != "POST" { http.Error(res, req.Method+" HTTP method is unsupported for this API.", http.StatusMethodNotAllowed) return false @@ -32,7 +33,7 @@ func Auth(res http.ResponseWriter, req *http.Request) bool { return false } - ds, err := database.Open(database.DBLOC_MAIN) + ds, err := database.Open(dir + database.DB_USERS) if err != nil || ds == nil { http.Error(res, err.Error(), http.StatusInternalServerError) return false @@ -80,7 +81,13 @@ func Auth(res http.ResponseWriter, req *http.Request) bool { return false } - store := networking.GetStore(networking.ACCOUNT_USER_AUTH) + var store networking.Store + if strings.Contains(dir, "bundles/") { + store = networking.GetStore(networking.ACCOUNT_USER_AUTH) + } else { + store = networking.GetStore(networking.SERVER_USER_AUTH) + } + err = store.Set(res, req, "token", token, (60 * 60 * 24)) err2 := store.Set(res, req, "user", userRequestData.User, (60 * 60 * 24)) if err != nil || err2 != nil { diff --git a/pkg/api/user/get_secret.go b/pkg/api/user/get_secret.go index f0d1e06..8cc7470 100644 --- a/pkg/api/user/get_secret.go +++ b/pkg/api/user/get_secret.go @@ -7,7 +7,7 @@ import "github.com/Ennovar/gPanel/pkg/database" // only used on the server side to help verify users are who they say they // are. func GetSecret(user string, directory string) (string, error) { - ds, err := database.Open(directory + database.DBLOC_MAIN) + ds, err := database.Open(directory + database.DB_USERS) if err != nil { return "", err } diff --git a/pkg/api/user/logout.go b/pkg/api/user/logout.go index cfecb5c..19f94b4 100644 --- a/pkg/api/user/logout.go +++ b/pkg/api/user/logout.go @@ -3,6 +3,7 @@ package user import ( "net/http" + "strings" "github.com/Ennovar/gPanel/pkg/networking" ) @@ -10,13 +11,19 @@ import ( // Logout function is accessed by an API call from the webhost root // by accessing /user_logout and sending it an empty POST request. This function will // delete the user-auth cookie session store -func Logout(res http.ResponseWriter, req *http.Request) bool { +func Logout(res http.ResponseWriter, req *http.Request, dir string) bool { if req.Method != "POST" { http.Error(res, req.Method+" HTTP method is unsupported for this API.", http.StatusMethodNotAllowed) return false } - store := networking.GetStore(networking.ACCOUNT_USER_AUTH) + var store networking.Store + if strings.Contains(dir, "bundles/") { + store = networking.GetStore(networking.ACCOUNT_USER_AUTH) + } else { + store = networking.GetStore(networking.SERVER_USER_AUTH) + } + err := store.Delete(res, req) if err != nil { diff --git a/pkg/api/user/register.go b/pkg/api/user/register.go index cd6fb8d..c8b303f 100644 --- a/pkg/api/user/register.go +++ b/pkg/api/user/register.go @@ -12,7 +12,7 @@ import ( // Register function is accessed by an API call from the webhost root // by accessing /user_register and sending it a post request with userRequestData // struct in JSON format. -func Register(res http.ResponseWriter, req *http.Request) bool { +func Register(res http.ResponseWriter, req *http.Request, dir string) bool { if req.Method != "POST" { http.Error(res, req.Method+" HTTP method is unsupported for this API.", http.StatusMethodNotAllowed) return false @@ -32,7 +32,7 @@ func Register(res http.ResponseWriter, req *http.Request) bool { return false } - ds, err := database.Open(database.DBLOC_MAIN) + ds, err := database.Open(dir + database.DB_USERS) if err != nil || ds == nil { http.Error(res, err.Error(), http.StatusInternalServerError) return false diff --git a/pkg/database/database.go b/pkg/database/database.go index dfcf525..9ab111f 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -11,7 +11,7 @@ import ( // Database constants const ( - DBLOC_MAIN = "datastore.db" + DB_USERS = "users.db" ) // Bucket constants diff --git a/pkg/gpaccount/apihandler.go b/pkg/gpaccount/apihandler.go new file mode 100644 index 0000000..bc0c346 --- /dev/null +++ b/pkg/gpaccount/apihandler.go @@ -0,0 +1,48 @@ +// Package gpaccount handles the logic of the gPanel account server +package gpaccount + +import ( + "net/http" + "strings" + + "github.com/Ennovar/gPanel/pkg/api/log" + "github.com/Ennovar/gPanel/pkg/api/server" + "github.com/Ennovar/gPanel/pkg/api/user" +) + +func (con *Controller) apiHandler(res http.ResponseWriter, req *http.Request) (bool, bool) { + path := req.URL.Path[1:] + if len(path) == 0 { + path = (con.Directory + "index.html") + } else { + path = (con.Directory + path) + } + + splitUrl := strings.SplitN(path, "api", 2) + suspectApi := strings.ToLower(splitUrl[len(splitUrl)-1]) + + switch suspectApi { + case "/user/auth": + return true, user.Auth(res, req, con.Directory) + case "/user/register": + return true, user.Register(res, req, con.Directory) + case "/user/logout": + return true, user.Logout(res, req, con.Directory) + case "/server/status": + return true, server.Status(res, req, con.Public) + case "/server/start": + return true, server.Start(res, req, con.Public) + case "/server/shutdown": + return true, server.Shutdown(res, req, con.Public) + case "/server/restart": + return true, server.Restart(res, req, con.Public) + case "/server/maintenance": + return true, server.Maintenance(res, req, con.Public) + case "/log/read": + return true, log.Read(res, req) + case "/log/delete": + return true, log.Delete(res, req) + default: + return false, false + } +} diff --git a/pkg/account/authentication.go b/pkg/gpaccount/authentication.go similarity index 95% rename from pkg/account/authentication.go rename to pkg/gpaccount/authentication.go index 6631470..f78e930 100644 --- a/pkg/account/authentication.go +++ b/pkg/gpaccount/authentication.go @@ -1,5 +1,5 @@ -// Package account handles the logic of the gPanel account server -package account +// Package gpaccount handles the logic of the gPanel account server +package gpaccount import ( "net/http" diff --git a/pkg/account/account.go b/pkg/gpaccount/gpaccount.go similarity index 75% rename from pkg/account/account.go rename to pkg/gpaccount/gpaccount.go index 6a7a16b..8ea1bd7 100644 --- a/pkg/account/account.go +++ b/pkg/gpaccount/gpaccount.go @@ -1,17 +1,13 @@ -// Package account handles the logic of the gPanel account server -package account +// Package gpaccount handles the logic of the gPanel account server +package gpaccount import ( - "context" - "errors" - "fmt" "io" "net/http" "os" "strconv" "time" - "github.com/Ennovar/gPanel/pkg/api" "github.com/Ennovar/gPanel/pkg/file" "github.com/Ennovar/gPanel/pkg/public" "github.com/Ennovar/gPanel/pkg/routing" @@ -28,7 +24,7 @@ type Controller struct { } var controller Controller -var server http.Server +var httpserver http.Server // New returns a new Controller reference. func New(root string) *Controller { @@ -44,7 +40,7 @@ func New(root string) *Controller { ServerLogger: serverErrorLogger, } - server = http.Server{ + httpserver = http.Server{ Addr: "localhost:" + strconv.Itoa(controller.Port), Handler: &controller, ReadTimeout: 30 * time.Second, @@ -55,37 +51,6 @@ func New(root string) *Controller { return &controller } -func (con *Controller) Start() error { - if con.Status == 1 { - return errors.New("Account server is already on.") - } - - con.Status = 1 - go server.ListenAndServe() - return nil -} - -func (con *Controller) Stop(graceful bool) error { - if graceful { - context, cancel := context.WithTimeout(context.Background(), con.GracefulShutdownTimeout) - defer cancel() - - err := server.Shutdown(context) - if err == nil { - return nil - } - - fmt.Printf("Graceful shutdown failed attempting forced: %v\n", err) - } - - if err := server.Close(); err != nil { - return err - } - - con.Status = 0 - return nil -} - // ServeHTTP function routes all requests for the private webhost server. It is used in the main // function inside of the http.ListenAndServe() function for the private webhost host. func (con *Controller) ServeHTTP(res http.ResponseWriter, req *http.Request) { @@ -104,7 +69,7 @@ func (con *Controller) ServeHTTP(res http.ResponseWriter, req *http.Request) { } } - isApi, _ := api.HandleAPI(res, req, path, con.Public) + isApi, _ := con.apiHandler(res, req) if isApi { // API methods handle HTTP logic from here diff --git a/pkg/gpaccount/toggle.go b/pkg/gpaccount/toggle.go new file mode 100644 index 0000000..b96b274 --- /dev/null +++ b/pkg/gpaccount/toggle.go @@ -0,0 +1,39 @@ +// Package gpaccount handles the logic of the gPanel account server +package gpaccount + +import ( + "context" + "errors" + "fmt" +) + +func (con *Controller) Start() error { + if con.Status == 1 { + return errors.New("Account server is already on.") + } + + con.Status = 1 + go httpserver.ListenAndServe() + return nil +} + +func (con *Controller) Stop(graceful bool) error { + if graceful { + context, cancel := context.WithTimeout(context.Background(), con.GracefulShutdownTimeout) + defer cancel() + + err := httpserver.Shutdown(context) + if err == nil { + return nil + } + + fmt.Printf("Graceful shutdown failed attempting forced: %v\n", err) + } + + if err := httpserver.Close(); err != nil { + return err + } + + con.Status = 0 + return nil +} diff --git a/pkg/gpserver/apihandler.go b/pkg/gpserver/apihandler.go new file mode 100644 index 0000000..1a10bef --- /dev/null +++ b/pkg/gpserver/apihandler.go @@ -0,0 +1,48 @@ +// Package gpserver handles the logic of the gPanel server +package gpserver + +import ( + "net/http" + "strings" + + "github.com/Ennovar/gPanel/pkg/api/log" + "github.com/Ennovar/gPanel/pkg/api/server" + "github.com/Ennovar/gPanel/pkg/api/user" +) + +func (con *Controller) apiHandler(res http.ResponseWriter, req *http.Request, curBundle int) (bool, bool) { + path := req.URL.Path[1:] + if len(path) == 0 { + path = (con.Directory + "index.html") + } else { + path = (con.Directory + path) + } + + splitUrl := strings.SplitN(path, "api", 2) + suspectApi := strings.ToLower(splitUrl[len(splitUrl)-1]) + + switch suspectApi { + case "/user/auth": + return true, user.Auth(res, req, con.Directory) + case "/user/register": + return true, user.Register(res, req, con.Directory) + case "/user/logout": + return true, user.Logout(res, req, con.Directory) + case "/server/status": + return true, server.Status(res, req, con.Bundles[curBundle].Public) + case "/server/start": + return true, server.Start(res, req, con.Bundles[curBundle].Public) + case "/server/shutdown": + return true, server.Shutdown(res, req, con.Bundles[curBundle].Public) + case "/server/restart": + return true, server.Restart(res, req, con.Bundles[curBundle].Public) + case "/server/maintenance": + return true, server.Maintenance(res, req, con.Bundles[curBundle].Public) + case "/log/read": + return true, log.Read(res, req) + case "/log/delete": + return true, log.Delete(res, req) + default: + return false, false + } +} diff --git a/pkg/server/authentication.go b/pkg/gpserver/authentication.go similarity index 95% rename from pkg/server/authentication.go rename to pkg/gpserver/authentication.go index 8c31f9f..46e457d 100644 --- a/pkg/server/authentication.go +++ b/pkg/gpserver/authentication.go @@ -1,4 +1,5 @@ -package server +// Package gpserver handles the logic of the gPanel server +package gpserver import ( "net/http" diff --git a/pkg/server/server.go b/pkg/gpserver/gpserver.go similarity index 77% rename from pkg/server/server.go rename to pkg/gpserver/gpserver.go index f7f4260..37ad4eb 100644 --- a/pkg/server/server.go +++ b/pkg/gpserver/gpserver.go @@ -1,25 +1,26 @@ -package server +// Package gpserver handles the logic of the gPanel server +package gpserver import ( "io" "net/http" "os" - "github.com/Ennovar/gPanel/pkg/account" + "github.com/Ennovar/gPanel/pkg/gpaccount" "github.com/Ennovar/gPanel/pkg/routing" ) type Controller struct { Directory string DocumentRoot string - Bundles []account.Controller + Bundles []gpaccount.Controller } func New() *Controller { return &Controller{ Directory: "server/", DocumentRoot: "document_root/", - Bundles: []account.Controller{}, + Bundles: []gpaccount.Controller{}, } } @@ -38,6 +39,13 @@ func (con *Controller) ServeHTTP(res http.ResponseWriter, req *http.Request) { } } + isApi, _ := con.apiHandler(res, req, 0) + + if isApi { + // API methods handle HTTP logic from here + return + } + f, err := os.Open(path) if err != nil { diff --git a/pkg/networking/session_store.go b/pkg/networking/session_store.go index 44d6647..e023085 100644 --- a/pkg/networking/session_store.go +++ b/pkg/networking/session_store.go @@ -15,14 +15,14 @@ const ( SERVER_USER_AUTH = "gpanel-server-user-auth" ) -type store struct { +type Store struct { handle *sessions.CookieStore cookieName string } // GetStore function takes a name and either creates/grabs a store with that name. -func GetStore(name string) store { - sessionStore := store{ +func GetStore(name string) Store { + sessionStore := Store{ handle: sessions.NewCookieStore(key), cookieName: name, } @@ -31,7 +31,7 @@ func GetStore(name string) store { } // Set function is attached to the store struct and will set a session value inside of the current store. -func (s *store) Set(res http.ResponseWriter, req *http.Request, key string, value interface{}, expire int) error { +func (s *Store) Set(res http.ResponseWriter, req *http.Request, key string, value interface{}, expire int) error { session, err := s.handle.Get(req, s.cookieName) if err != nil { @@ -49,7 +49,7 @@ func (s *store) Set(res http.ResponseWriter, req *http.Request, key string, valu } // Read function is attached to the store struct and will read a given session value inside of the current store. -func (s *store) Read(res http.ResponseWriter, req *http.Request, key string) (interface{}, error) { +func (s *Store) Read(res http.ResponseWriter, req *http.Request, key string) (interface{}, error) { session, err := s.handle.Get(req, s.cookieName) if err != nil { @@ -61,7 +61,7 @@ func (s *store) Read(res http.ResponseWriter, req *http.Request, key string) (in } // Delete function is attached to the store struct and will delete a given session value inside of the current store. -func (s *store) Delete(res http.ResponseWriter, req *http.Request) error { +func (s *Store) Delete(res http.ResponseWriter, req *http.Request) error { session, err := s.handle.Get(req, s.cookieName) if err != nil { diff --git a/server/users.db b/server/users.db new file mode 100644 index 0000000..bd899a4 Binary files /dev/null and b/server/users.db differ