diff --git a/.gitignore b/.gitignore index 376319d..281344a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.dll *.so *.dylib +.idea/* # Test binary, build with `go test -c` *.test @@ -20,7 +21,7 @@ /logs/* # ignore configuration file -/config.json +/config.yml # Ignore Vagrant stuff /.vagrant diff --git a/api/_testdata/config.json b/api/_testdata/config.json deleted file mode 100644 index 327c993..0000000 --- a/api/_testdata/config.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "authKeys": [ - "existingkey" - ] -} diff --git a/api/_testdata/config.yml b/api/_testdata/config.yml new file mode 100644 index 0000000..2d04127 --- /dev/null +++ b/api/_testdata/config.yml @@ -0,0 +1 @@ +authKey: 'existingkey' diff --git a/api/api.go b/api/api.go index 49c47b8..ce06713 100644 --- a/api/api.go +++ b/api/api.go @@ -11,23 +11,25 @@ import ( "github.com/Pterodactyl/wings/config" ) -// API is a grouping struct for the api -type API struct { +type InternalAPI struct { router *gin.Engine } -// NewAPI creates a new Api object -func NewAPI() API { - return API{} +func NewAPI() InternalAPI { + return InternalAPI{} } -// Listen starts the api http server -func (api *API) Listen() { +// Configure the API and begin listening on the configured IP and Port. +func (api *InternalAPI) Listen() { + listener := fmt.Sprintf("%s:%d", viper.GetString(config.APIHost), viper.GetInt(config.APIPort)) + if !viper.GetBool(config.Debug) { gin.SetMode(gin.ReleaseMode) } api.router = gin.Default() + api.router.RedirectTrailingSlash = false + api.RegisterRoutes() api.router.Use(func(c *gin.Context) { c.Header("Access-Control-Allow-Origin", "*") @@ -47,7 +49,3 @@ func (api *API) Listen() { log.Info("Now listening on %s", listenString) log.Fatal(http.ListenAndServe(listenString, nil)) } - -func getRoot(c *gin.Context) { - c.String(http.StatusOK, "hello!") -} diff --git a/api/auth_test.go b/api/auth_test.go index 086f4a0..c66e868 100644 --- a/api/auth_test.go +++ b/api/auth_test.go @@ -12,7 +12,7 @@ import ( "github.com/Pterodactyl/wings/control" ) -const configFile = "_testdata/config.json" +const configFile = "_testdata/config.yml" func TestAuthHandler(t *testing.T) { gin.SetMode(gin.ReleaseMode) diff --git a/api/handlers.go b/api/handlers.go index 34c0ace..77a43eb 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -12,8 +12,7 @@ import ( log "github.com/sirupsen/logrus" ) -// handleGetIndex handles GET / -func handleGetIndex(c *gin.Context) { +func GetIndex(c *gin.Context) { auth := GetContextAuthManager(c) if auth != nil && auth.HasPermission("c:info") { diff --git a/api/handlers_server_files.go b/api/handlers_server_files.go deleted file mode 100644 index d4c5ae3..0000000 --- a/api/handlers_server_files.go +++ /dev/null @@ -1,51 +0,0 @@ -package api - -import "github.com/gin-gonic/gin" - -func handlePostFilesFolder(c *gin.Context) { - -} - -func handleGetDirectory(c *gin.Context) { - -} - -func handlePostFileCopy(c *gin.Context) { - -} - -func handlePostFileMove(c *gin.Context) { - -} - -func handlePostFileDelete(c *gin.Context) { - -} - -func handlePostFileCompress(c *gin.Context) { - -} - -func handlePostFileDecompress(c *gin.Context) { - -} - -func handleGetFileStat(c *gin.Context) { - -} - -func handleGetFile(c *gin.Context) { - -} - -func handlePostFile(c *gin.Context) { - -} - -func handleDeleteFile(c *gin.Context) { - -} - -func handleGetDownloadFile(c *gin.Context) { - -} diff --git a/api/routes.go b/api/routes.go index a6451c4..7804e19 100644 --- a/api/routes.go +++ b/api/routes.go @@ -1,9 +1,49 @@ package api -func (api *API) registerRoutes() { - api.router.GET("/", AuthHandler(""), handleGetIndex) - api.router.PATCH("/config", AuthHandler("c:config"), handlePatchConfig) +func (api *InternalAPI) RegisterRoutes() { + // Register routes for v1 of the API. This API should be fully backwards compatable with + // the existing Nodejs Daemon API. + v1 := api.router.Group("/v1") + { + v1.GET("/", AuthHandler(""), GetIndex) + v1.PATCH("/config", AuthHandler("c:config"), PatchConfiguration) - api.registerServerRoutes() - api.registerServerFileRoutes() + v1.GET("/servers", AuthHandler("c:list"), handleGetServers) + v1.POST("/servers", AuthHandler("c:create"), handlePostServers) + + v1ServerRoutes := v1.Group("/servers/:server") + { + v1ServerRoutes.GET("/", AuthHandler("s:get"), handleGetServer) + v1ServerRoutes.PATCH("/", AuthHandler("s:config"), handlePatchServer) + v1ServerRoutes.DELETE("/", AuthHandler("g:server:delete"), handleDeleteServer) + v1ServerRoutes.POST("/reinstall", AuthHandler("s:install-server"), handlePostServerReinstall) + v1ServerRoutes.POST("/rebuild", AuthHandler("g:server:rebuild"), handlePostServerRebuild) + v1ServerRoutes.POST("/password", AuthHandler(""), handlePostServerPassword) + v1ServerRoutes.POST("/power", AuthHandler("s:power"), handlePostServerPower) + v1ServerRoutes.POST("/command", AuthHandler("s:command"), handlePostServerCommand) + v1ServerRoutes.GET("/log", AuthHandler("s:console"), handleGetServerLog) + v1ServerRoutes.POST("/suspend", AuthHandler(""), handlePostServerSuspend) + v1ServerRoutes.POST("/unsuspend", AuthHandler(""), handlePostServerUnsuspend) + } + + v1ServerFileRoutes := v1.Group("/servers/:server/files") + { + v1ServerFileRoutes.GET("/file/:file", AuthHandler("s:files:read"), handleGetFile) + v1ServerFileRoutes.GET("/stat/:file", AuthHandler("s:files:"), handleGetFileStat) + v1ServerFileRoutes.GET("/dir/:directory", AuthHandler("s:files:get"), handleGetDirectory) + + v1ServerFileRoutes.POST("/dir/:directory", AuthHandler("s:files:create"), handlePostFilesFolder) + v1ServerFileRoutes.POST("/file/:file", AuthHandler("s:files:post"), handlePostFile) + + v1ServerFileRoutes.POST("/copy/:file", AuthHandler("s:files:copy"), handlePostFileCopy) + v1ServerFileRoutes.POST("/move/:file", AuthHandler("s:files:move"), handlePostFileMove) + v1ServerFileRoutes.POST("/rename/:file", AuthHandler("s:files:move"), handlePostFileMove) + v1ServerFileRoutes.POST("/compress/:file", AuthHandler("s:files:compress"), handlePostFileCompress) + v1ServerFileRoutes.POST("/decompress/:file", AuthHandler("s:files:decompress"), handlePostFileDecompress) + + v1ServerFileRoutes.DELETE("/file/:file", AuthHandler("s:files:delete"), handleDeleteFile) + + v1ServerFileRoutes.GET("/download/:token", handleGetDownloadFile) + } + } } diff --git a/api/routes_server.go b/api/routes_server.go deleted file mode 100644 index 9eb6e6d..0000000 --- a/api/routes_server.go +++ /dev/null @@ -1,20 +0,0 @@ -package api - -func (api *API) registerServerRoutes() { - - api.router.GET("/servers", AuthHandler("c:list"), handleGetServers) - - api.router.POST("/servers", AuthHandler("c:create"), handlePostServers) - api.router.GET("/servers/:server", AuthHandler("s:get"), handleGetServer) - api.router.PATCH("/servers/:server", AuthHandler("s:config"), handlePatchServer) - api.router.DELETE("/servers/:server", AuthHandler("g:server:delete"), handleDeleteServer) - - api.router.POST("/servers/:server/reinstall", AuthHandler("s:install-server"), handlePostServerReinstall) - api.router.POST("/servers/:server/rebuild", AuthHandler("g:server:rebuild"), handlePostServerRebuild) - api.router.POST("/servers/:server/password", AuthHandler(""), handlePostServerPassword) - api.router.POST("/servers/:server/power", AuthHandler("s:power"), handlePostServerPower) - api.router.POST("/servers/:server/command", AuthHandler("s:command"), handlePostServerCommand) - api.router.GET("/servers/:server/log", AuthHandler("s:console"), handleGetServerLog) - api.router.POST("/servers/:server/suspend", AuthHandler(""), handlePostServerSuspend) - api.router.POST("/servers/:server/unsuspend", AuthHandler(""), handlePostServerUnsuspend) -} diff --git a/api/routes_server_files.go b/api/routes_server_files.go deleted file mode 100644 index 747e05c..0000000 --- a/api/routes_server_files.go +++ /dev/null @@ -1,20 +0,0 @@ -package api - -func (api *API) registerServerFileRoutes() { - api.router.GET("/servers/:server/files/file/:file", AuthHandler("s:files:read"), handleGetFile) - api.router.GET("/servers/:server/files/stat/:file", AuthHandler("s:files:"), handleGetFileStat) - api.router.GET("/servers/:server/files/dir/:directory", AuthHandler("s:files:get"), handleGetDirectory) - - api.router.POST("/servers/:server/files/dir/:directory", AuthHandler("s:files:create"), handlePostFilesFolder) - api.router.POST("/servers/:server/files/file/:file", AuthHandler("s:files:post"), handlePostFile) - - api.router.POST("/servers/:server/files/copy/:file", AuthHandler("s:files:copy"), handlePostFileCopy) - api.router.POST("/servers/:server/files/move/:file", AuthHandler("s:files:move"), handlePostFileMove) - api.router.POST("/servers/:server/files/rename/:file", AuthHandler("s:files:move"), handlePostFileMove) - api.router.POST("/servers/:server/files/compress/:file", AuthHandler("s:files:compress"), handlePostFileCompress) - api.router.POST("/servers/:server/files/decompress/:file", AuthHandler("s:files:decompress"), handlePostFileDecompress) - - api.router.DELETE("/servers/:server/files/file/:file", AuthHandler("s:files:delete"), handleDeleteFile) - - api.router.GET("/servers/:server/files/download/:token", handleGetDownloadFile) -} diff --git a/command/root.go b/command/root.go index 21cb0a3..195b86c 100644 --- a/command/root.go +++ b/command/root.go @@ -26,7 +26,7 @@ var RootCommand = &cobra.Command{ var configPath string func init() { - RootCommand.Flags().StringVarP(&configPath, "config", "c", "./config.json", "Allows to set the path of the configuration file.") + RootCommand.Flags().StringVarP(&configPath, "config", "c", "./config.yml", "Allows to set the path of the configuration file.") } // Execute registers the RootCommand diff --git a/config.example.json b/config.example.json deleted file mode 100644 index 27dc704..0000000 --- a/config.example.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "debug": false, - "dataPath": "/srv/wings", - "api": { - "host": "0.0.0.0", - "port": 8080, - "ssl": { - "enabled": false, - "generateLetsEncrypt": true, - "certificate": "/etc/letsencrypt/live/pterodactyl.app/fullchain.pem", - "key": "/etc/letsencrypt/live/pterodactyl.app/privkey.pem" - }, - "uploads": { - "maximumSize": 150000000 - } - }, - "docker": { - "socket": "/var/run/docker.sock", - "autoupdateImages": true, - "networkInterface": "172.18.0.1", - "timezonePath": "/etc/timezone" - }, - "sftp": { - "host": "0.0.0.0", - "port": 2022 - }, - "query": { - "killOnFail": false, - "failLimit": 5 - }, - "remote": "https://pterodactyl.app", - "log": { - "path": "/srv/wings/logs/", - "level": "info", - "deleteAfterDays": 100 - }, - "authKeys": [ - "somekey" - ] -} diff --git a/config.yml.example b/config.yml.example new file mode 100644 index 0000000..a170504 --- /dev/null +++ b/config.yml.example @@ -0,0 +1,28 @@ +debug: false +data: '/srv/daemon-data' +api: + host: '0.0.0.0' + port: 8080 + ssl: + enabled: false + cert: '' + key: '' + uploads: + maximumSize: 150000000 +docker: + socket: '/var/run/docker.sock' + autoupdateImages: true + networkInterface: '172.18.0.1' + timezonePath: '/etc/timezone' +sftp: + host: '0.0.0.0' + port: 2022 +query: + killOnFail: true + failLimit: 5 +remote: 'http://example.com' +log: + path: './logs/' + level: 'info' + deleteAfterDays: 10 +authKey: 'test123' diff --git a/config/config.go b/config/config.go index 47bef4a..7635697 100644 --- a/config/config.go +++ b/config/config.go @@ -10,6 +10,7 @@ func LoadConfiguration(path string) error { viper.SetConfigFile(path) } else { viper.AddConfigPath("./") + viper.SetConfigType("yaml") viper.SetConfigName("config") } @@ -49,7 +50,7 @@ func setDefaults() { // ContainsAuthKey checks wether the config contains a specified authentication key func ContainsAuthKey(key string) bool { - for _, k := range viper.GetStringSlice(AuthKeys) { + for _, k := range viper.GetStringSlice(AuthKey) { if k == key { return true } diff --git a/config/config_test.go b/config/config_test.go index 3a89d1f..1773208 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/assert" ) -const configFile = "../config.example.json" +const configFile = "../config.yml.example" func TestLoadConfiguraiton(t *testing.T) { err := LoadConfiguration(configFile) diff --git a/config/keys.go b/config/keys.go index 5764edb..fcf6304 100644 --- a/config/keys.go +++ b/config/keys.go @@ -21,7 +21,7 @@ const ( SSLGenerateLetsencrypt = "api.ssl.letsencrypt" // SSLCertificate is a string containing the location of // a ssl certificate to use - SSLCertificate = "api.ssl.certificate" + SSLCertificate = "api.ssl.cert" // SSLKey is a string containing the location of the key // for the ssl certificate SSLKey = "api.ssl.key" @@ -61,6 +61,6 @@ const ( // logs should be stored. They will be deleted after. If set to 0 // logs will be stored indefinitely. LogDeleteAfterDays = "log.deleteAfterDays" - // AuthKeys contains an array of auth keys that will be replaced by something better - AuthKeys = "authkeys" + // AuthKey contains a key that will be replaced by something better + AuthKey = "authKey" )