Merge branch 'develop' of github.com:Pterodactyl/wings into develop
# Conflicts: # api/api.go # api/core_routes.go # api/handlers_server.go # api/routes_server.go # config.example.json # utils/logging.go
This commit is contained in:
commit
44f42eec8c
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -3,6 +3,7 @@
|
||||||
*.dll
|
*.dll
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
|
.idea/*
|
||||||
|
|
||||||
# Test binary, build with `go test -c`
|
# Test binary, build with `go test -c`
|
||||||
*.test
|
*.test
|
||||||
|
@ -20,7 +21,7 @@
|
||||||
/logs/*
|
/logs/*
|
||||||
|
|
||||||
# ignore configuration file
|
# ignore configuration file
|
||||||
/config.json
|
/config.yml
|
||||||
|
|
||||||
# Ignore Vagrant stuff
|
# Ignore Vagrant stuff
|
||||||
/.vagrant
|
/.vagrant
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"authKeys": [
|
|
||||||
"existingkey"
|
|
||||||
]
|
|
||||||
}
|
|
1
api/_testdata/config.yml
Normal file
1
api/_testdata/config.yml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
authKey: 'existingkey'
|
20
api/api.go
20
api/api.go
|
@ -11,23 +11,25 @@ import (
|
||||||
"github.com/Pterodactyl/wings/config"
|
"github.com/Pterodactyl/wings/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// API is a grouping struct for the api
|
type InternalAPI struct {
|
||||||
type API struct {
|
|
||||||
router *gin.Engine
|
router *gin.Engine
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAPI creates a new Api object
|
func NewAPI() InternalAPI {
|
||||||
func NewAPI() API {
|
return InternalAPI{}
|
||||||
return API{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen starts the api http server
|
// Configure the API and begin listening on the configured IP and Port.
|
||||||
func (api *API) Listen() {
|
func (api *InternalAPI) Listen() {
|
||||||
|
listener := fmt.Sprintf("%s:%d", viper.GetString(config.APIHost), viper.GetInt(config.APIPort))
|
||||||
|
|
||||||
if !viper.GetBool(config.Debug) {
|
if !viper.GetBool(config.Debug) {
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
api.router = gin.Default()
|
api.router = gin.Default()
|
||||||
|
api.router.RedirectTrailingSlash = false
|
||||||
|
api.RegisterRoutes()
|
||||||
|
|
||||||
api.router.Use(func(c *gin.Context) {
|
api.router.Use(func(c *gin.Context) {
|
||||||
c.Header("Access-Control-Allow-Origin", "*")
|
c.Header("Access-Control-Allow-Origin", "*")
|
||||||
|
@ -47,7 +49,3 @@ func (api *API) Listen() {
|
||||||
log.Info("Now listening on %s", listenString)
|
log.Info("Now listening on %s", listenString)
|
||||||
log.Fatal(http.ListenAndServe(listenString, nil))
|
log.Fatal(http.ListenAndServe(listenString, nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRoot(c *gin.Context) {
|
|
||||||
c.String(http.StatusOK, "hello!")
|
|
||||||
}
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
"github.com/Pterodactyl/wings/control"
|
"github.com/Pterodactyl/wings/control"
|
||||||
)
|
)
|
||||||
|
|
||||||
const configFile = "_testdata/config.json"
|
const configFile = "_testdata/config.yml"
|
||||||
|
|
||||||
func TestAuthHandler(t *testing.T) {
|
func TestAuthHandler(t *testing.T) {
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
|
|
@ -12,8 +12,7 @@ import (
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// handleGetIndex handles GET /
|
func GetIndex(c *gin.Context) {
|
||||||
func handleGetIndex(c *gin.Context) {
|
|
||||||
auth := GetContextAuthManager(c)
|
auth := GetContextAuthManager(c)
|
||||||
|
|
||||||
if auth != nil && auth.HasPermission("c:info") {
|
if auth != nil && auth.HasPermission("c:info") {
|
||||||
|
|
|
@ -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) {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,9 +1,49 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
func (api *API) registerRoutes() {
|
func (api *InternalAPI) RegisterRoutes() {
|
||||||
api.router.GET("/", AuthHandler(""), handleGetIndex)
|
// Register routes for v1 of the API. This API should be fully backwards compatable with
|
||||||
api.router.PATCH("/config", AuthHandler("c:config"), handlePatchConfig)
|
// the existing Nodejs Daemon API.
|
||||||
|
v1 := api.router.Group("/v1")
|
||||||
|
{
|
||||||
|
v1.GET("/", AuthHandler(""), GetIndex)
|
||||||
|
v1.PATCH("/config", AuthHandler("c:config"), PatchConfiguration)
|
||||||
|
|
||||||
api.registerServerRoutes()
|
v1.GET("/servers", AuthHandler("c:list"), handleGetServers)
|
||||||
api.registerServerFileRoutes()
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -26,7 +26,7 @@ var RootCommand = &cobra.Command{
|
||||||
var configPath string
|
var configPath string
|
||||||
|
|
||||||
func init() {
|
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
|
// Execute registers the RootCommand
|
||||||
|
|
|
@ -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"
|
|
||||||
]
|
|
||||||
}
|
|
28
config.yml.example
Normal file
28
config.yml.example
Normal file
|
@ -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'
|
|
@ -10,6 +10,7 @@ func LoadConfiguration(path string) error {
|
||||||
viper.SetConfigFile(path)
|
viper.SetConfigFile(path)
|
||||||
} else {
|
} else {
|
||||||
viper.AddConfigPath("./")
|
viper.AddConfigPath("./")
|
||||||
|
viper.SetConfigType("yaml")
|
||||||
viper.SetConfigName("config")
|
viper.SetConfigName("config")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +50,7 @@ func setDefaults() {
|
||||||
|
|
||||||
// ContainsAuthKey checks wether the config contains a specified authentication key
|
// ContainsAuthKey checks wether the config contains a specified authentication key
|
||||||
func ContainsAuthKey(key string) bool {
|
func ContainsAuthKey(key string) bool {
|
||||||
for _, k := range viper.GetStringSlice(AuthKeys) {
|
for _, k := range viper.GetStringSlice(AuthKey) {
|
||||||
if k == key {
|
if k == key {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
const configFile = "../config.example.json"
|
const configFile = "../config.yml.example"
|
||||||
|
|
||||||
func TestLoadConfiguraiton(t *testing.T) {
|
func TestLoadConfiguraiton(t *testing.T) {
|
||||||
err := LoadConfiguration(configFile)
|
err := LoadConfiguration(configFile)
|
||||||
|
|
|
@ -21,7 +21,7 @@ const (
|
||||||
SSLGenerateLetsencrypt = "api.ssl.letsencrypt"
|
SSLGenerateLetsencrypt = "api.ssl.letsencrypt"
|
||||||
// SSLCertificate is a string containing the location of
|
// SSLCertificate is a string containing the location of
|
||||||
// a ssl certificate to use
|
// a ssl certificate to use
|
||||||
SSLCertificate = "api.ssl.certificate"
|
SSLCertificate = "api.ssl.cert"
|
||||||
// SSLKey is a string containing the location of the key
|
// SSLKey is a string containing the location of the key
|
||||||
// for the ssl certificate
|
// for the ssl certificate
|
||||||
SSLKey = "api.ssl.key"
|
SSLKey = "api.ssl.key"
|
||||||
|
@ -61,6 +61,6 @@ const (
|
||||||
// logs should be stored. They will be deleted after. If set to 0
|
// logs should be stored. They will be deleted after. If set to 0
|
||||||
// logs will be stored indefinitely.
|
// logs will be stored indefinitely.
|
||||||
LogDeleteAfterDays = "log.deleteAfterDays"
|
LogDeleteAfterDays = "log.deleteAfterDays"
|
||||||
// AuthKeys contains an array of auth keys that will be replaced by something better
|
// AuthKey contains a key that will be replaced by something better
|
||||||
AuthKeys = "authkeys"
|
AuthKey = "authKey"
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user