diff --git a/.gitignore b/.gitignore index 8d3db4c2..93753753 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,4 @@ uploads /build/ /captures/ test.go -/dist/ \ No newline at end of file +/dist/ diff --git a/config/application.json b/config/application.json new file mode 100644 index 00000000..36dd89bf --- /dev/null +++ b/config/application.json @@ -0,0 +1,86 @@ +{ + "gin": { + "cors": { + "allow_methods": ["GET", "POST", "PUT", "DELETE"], + "allow_origins": ["*"] + }, + "jwt": { + "expiration": 180 + }, + "cache": { + "provider": "memory", + "redis": { + "host": "cache", + "port": 6379, + "password": "", + "db": 0 + } + }, + "host": "0.0.0.0", + "port": 8888 + }, + "container": { + "provider": "docker", + "entry": "127.0.0.1", + "docker": { + "uri": "unix:///var/run/docker.sock" + }, + "k8s": { + "namespace": "default", + "config": { + "path": "./configs/k8s.yml" + } + }, + "proxy": { + "enabled": false, + "type": "ws", + "traffic_capture": { + "enabled": false + } + } + }, + "db": { + "provider": "sqlite", + "postgres": { + "dbname": "cloudsdale", + "host": "db", + "username": "cloudsdale", + "password": "cloudsdale", + "port": 5432, + "sslmode": "disable" + }, + "mysql": { + "dbname": "cloudsdale", + "host": "db", + "username": "cloudsdale", + "password": "cloudsdale", + "port": 3306 + }, + "sqlite": { + "path": "./db/db.sqlite" + } + }, + "email": { + "address": "", + "password": "", + "smtp": { + "host": "", + "port": 0 + } + }, + "captcha": { + "enabled": false, + "provider": "turnstile", + "turnstile": { + "url": "https://challenges.cloudflare.com/turnstile/v0/siteverify", + "site_key": "", + "secret_key": "" + }, + "recaptcha": { + "url:": "https://www.google.com/recaptcha/api/siteverify", + "site_key": "", + "secret_key": "", + "threshold": 0.5 + } + } +} diff --git a/config/platform.json b/config/platform.json new file mode 100644 index 00000000..1fd63c4d --- /dev/null +++ b/config/platform.json @@ -0,0 +1,19 @@ +{ + "site": { + "description": "Hack for fun not for profit.", + "title": "Cloudsdale" + }, + "container": { + "parallel_limit": 1, + "request_limit": 0 + }, + "user": { + "registration": { + "enabled": true, + "email": { + "domain": [], + "verification": false + } + } + } +} \ No newline at end of file diff --git a/internal/app/app.go b/internal/app/app.go index 2cccd4c6..6aba41d9 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -29,11 +29,10 @@ import ( "html/template" "net/http" "os" - "strconv" ) func init() { - data, _ := files.FS.ReadFile("statics/banner.txt") + data, _ := files.F().ReadFile("statics/banner.txt") banner := string(data) t, _ := template.New("cloudsdale").Parse(banner) _ = t.Execute(os.Stdout, struct { @@ -97,13 +96,17 @@ func Run() { // Frontend resources r.Use(middleware.Frontend("/")) - s := &http.Server{ - Addr: config.AppCfg().Gin.Host + ":" + strconv.Itoa(config.AppCfg().Gin.Port), + srv := &http.Server{ + Addr: fmt.Sprintf( + "%s:%d", + config.AppCfg().Gin.Host, + config.AppCfg().Gin.Port, + ), Handler: r, } zap.L().Info(fmt.Sprintf("Here's the address! %s:%d", config.AppCfg().Gin.Host, config.AppCfg().Gin.Port)) zap.L().Info("The Cloudsdale service is running! Enjoy your hacking challenges!") - err := s.ListenAndServe() + err := srv.ListenAndServe() if err != nil { zap.L().Fatal("Err... It seems that the port for Cloudsdale is not available. Plz try again.") } diff --git a/internal/app/config/application.go b/internal/app/config/application.go index 95a11f6d..a11ada66 100644 --- a/internal/app/config/application.go +++ b/internal/app/config/application.go @@ -2,6 +2,7 @@ package config import ( "github.com/elabosak233/cloudsdale/internal/extension/files" + "github.com/elabosak233/cloudsdale/internal/utils" "github.com/spf13/viper" "go.uber.org/zap" "io" @@ -36,10 +37,6 @@ type ApplicationCfg struct { DB int `yaml:"db" json:"db" mapstructure:"db"` } `yaml:"redis" json:"redis" mapstructure:"redis"` } `yaml:"cache" json:"cache" mapstructure:"cache"` - Paths struct { - Assets string `yaml:"assets" json:"assets" mapstructure:"assets"` - Media string `yaml:"media" json:"media" mapstructure:"media"` - } `yaml:"paths" json:"paths" mapstructure:"paths"` } `yaml:"gin" json:"gin" mapstructure:"gin"` Email struct { Address string `yaml:"address" json:"address" mapstructure:"address"` @@ -75,7 +72,7 @@ type ApplicationCfg struct { Sslmode string `yaml:"sslmode" json:"sslmode" mapstructure:"sslmode"` } `yaml:"postgres" json:"postgres" mapstructure:"postgres"` SQLite struct { - Filename string `yaml:"filename" json:"filename" mapstructure:"filename"` + Path string `yaml:"path" json:"path" mapstructure:"path"` } `yaml:"sqlite" json:"sqlite" mapstructure:"sqlite"` MySQL struct { Host string `yaml:"host" json:"host" mapstructure:"host"` @@ -87,23 +84,24 @@ type ApplicationCfg struct { } `yaml:"db" json:"db" mapstructure:"db"` Container struct { Provider string `yaml:"provider" json:"provider" mapstructure:"provider"` + Entry string `yaml:"entry" json:"entry" mapstructure:"entry"` Docker struct { - URI string `yaml:"uri" json:"uri" mapstructure:"uri"` - Entry string `yaml:"entry" json:"entry" mapstructure:"entry"` + URI string `yaml:"uri" json:"uri" mapstructure:"uri"` } `yaml:"docker" json:"docker" mapstructure:"docker"` K8s struct { NameSpace string `yaml:"namespace" json:"namespace" mapstructure:"namespace"` - Config string `yaml:"config" json:"config" mapstructure:"config"` - Entry string `yaml:"entry" json:"entry" mapstructure:"entry"` + Config struct { + Path string `yaml:"path" json:"path" mapstructure:"path"` + } `yaml:"config" json:"config" mapstructure:"config"` } `yaml:"k8s" json:"k8s" mapstructure:"k8s"` Proxy struct { - Enabled bool `yaml:"enabled" json:"enabled" mapstructure:"enabled"` - Type string `yaml:"type" json:"type" mapstructure:"type"` + Enabled bool `yaml:"enabled" json:"enabled" mapstructure:"enabled"` + Type string `yaml:"type" json:"type" mapstructure:"type"` + TrafficCapture struct { + Enabled bool `yaml:"enabled" json:"enabled" mapstructure:"enabled"` + Path string `yaml:"path" json:"path" mapstructure:"path"` + } `yaml:"traffic_capture" json:"traffic_capture" mapstructure:"traffic_capture"` } `yaml:"proxy" json:"proxy" mapstructure:"proxy"` - TrafficCapture struct { - Enabled bool `yaml:"enabled" json:"enabled" mapstructure:"enabled"` - Path string `yaml:"path" json:"path" mapstructure:"path"` - } `yaml:"traffic_capture" json:"traffic_capture" mapstructure:"traffic_capture"` } `yaml:"container" json:"container" mapstructure:"container"` } @@ -113,14 +111,14 @@ func AppCfg() *ApplicationCfg { func InitApplicationCfg() { v1 = viper.New() - configFile := path.Join("configs", "application.json") + configFile := path.Join(utils.ConfigsPath, "application.json") v1.SetConfigType("json") v1.SetConfigFile(configFile) if _, err := os.Stat(configFile); err != nil { zap.L().Warn("No configuration file found, default configuration file will be created.") // Read default configuration from files - defaultConfig, _err := files.FS.Open("configs/application.json") + defaultConfig, _err := files.F().Open("configs/application.json") if _err != nil { zap.L().Error("Unable to read default configuration file.") return @@ -154,22 +152,16 @@ func InitApplicationCfg() { } func Mkdirs() { - if AppCfg().Container.TrafficCapture.Enabled { - if _, err := os.Stat(AppCfg().Container.TrafficCapture.Path); err != nil { - if _err := os.MkdirAll(AppCfg().Container.TrafficCapture.Path, os.ModePerm); _err != nil { + if AppCfg().Container.Proxy.TrafficCapture.Enabled { + if _, err := os.Stat(utils.CapturesPath); err != nil { + if _err := os.MkdirAll(utils.CapturesPath, os.ModePerm); _err != nil { zap.L().Fatal("Unable to create directory for traffic capture.") } } } - if _, err := os.Stat(AppCfg().Gin.Paths.Assets); err != nil { - if _err := os.MkdirAll(AppCfg().Gin.Paths.Assets, os.ModePerm); _err != nil { - zap.L().Fatal("Unable to create directory for assets.") - } - } - - if _, err := os.Stat(AppCfg().Gin.Paths.Media); err != nil { - if _err := os.MkdirAll(AppCfg().Gin.Paths.Media, os.ModePerm); _err != nil { + if _, err := os.Stat(utils.MediaPath); err != nil { + if _err := os.MkdirAll(utils.MediaPath, os.ModePerm); _err != nil { zap.L().Fatal("Unable to create directory for media.") } } diff --git a/internal/app/config/config.go b/internal/app/config/config.go index 8780aa16..122b3c1a 100644 --- a/internal/app/config/config.go +++ b/internal/app/config/config.go @@ -1,6 +1,7 @@ package config import ( + "github.com/elabosak233/cloudsdale/internal/utils" "github.com/google/uuid" "os" ) @@ -14,11 +15,9 @@ func JwtSecretKey() string { } func InitConfig() { - configPath := "configs" - if _, err := os.Stat(configPath); os.IsNotExist(err) { - _ = os.Mkdir(configPath, os.ModePerm) + if _, err := os.Stat(utils.ConfigsPath); os.IsNotExist(err) { + _ = os.Mkdir(utils.ConfigsPath, os.ModePerm) } - InitApplicationCfg() InitPlatformCfg() jwtSecretKey = uuid.NewString() diff --git a/internal/app/config/platform.go b/internal/app/config/platform.go index a982451f..575f8275 100644 --- a/internal/app/config/platform.go +++ b/internal/app/config/platform.go @@ -2,6 +2,7 @@ package config import ( "github.com/elabosak233/cloudsdale/internal/extension/files" + "github.com/elabosak233/cloudsdale/internal/utils" "github.com/spf13/viper" "go.uber.org/zap" "io" @@ -36,14 +37,14 @@ func PltCfg() *PlatformCfg { func InitPlatformCfg() { v2 = viper.New() - configFile := path.Join("configs", "platform.json") + configFile := path.Join(utils.ConfigsPath, "platform.json") v2.SetConfigType("json") v2.SetConfigFile(configFile) if _, err := os.Stat(configFile); err != nil { zap.L().Warn("No configuration file found, default configuration file will be created.") // Read default configuration from files - defaultConfig, _err := files.FS.Open("configs/platform.json") + defaultConfig, _err := files.F().Open("configs/platform.json") if _err != nil { zap.L().Error("Unable to read default configuration file.") return diff --git a/internal/app/db/db.go b/internal/app/db/db.go index a97a7a6e..9a175ceb 100644 --- a/internal/app/db/db.go +++ b/internal/app/db/db.go @@ -69,7 +69,7 @@ func initDatabaseEngine() { ) db, err = gorm.Open(mysql.Open(dbInfo), &gorm.Config{}) case "sqlite": - dbInfo = config.AppCfg().DB.SQLite.Filename + dbInfo = config.AppCfg().DB.SQLite.Path db, err = gorm.Open(sqlite.Open(dbInfo), &gorm.Config{}) } if err != nil { diff --git a/internal/controller/media.go b/internal/controller/media.go index 370fbc7d..7c1d835a 100644 --- a/internal/controller/media.go +++ b/internal/controller/media.go @@ -1,8 +1,8 @@ package controller import ( - "github.com/elabosak233/cloudsdale/internal/app/config" "github.com/elabosak233/cloudsdale/internal/service" + "github.com/elabosak233/cloudsdale/internal/utils" "github.com/gin-gonic/gin" "net/http" "os" @@ -25,7 +25,7 @@ func NewMediaController(appService *service.Service) IMediaController { func (m *MediaController) GetFile(ctx *gin.Context) { a := ctx.Param("path") - p := path.Join(config.AppCfg().Gin.Paths.Media, a) + p := path.Join(utils.MediaPath, a) _, err := os.Stat(p) if os.IsNotExist(err) { ctx.Status(http.StatusNotFound) diff --git a/internal/controller/pod.go b/internal/controller/pod.go index 0db92cb7..1fe7a39b 100644 --- a/internal/controller/pod.go +++ b/internal/controller/pod.go @@ -108,7 +108,7 @@ func (c *PodController) Renew(ctx *gin.Context) { instanceRenewRequest.ID = convertor.ToUintD(ctx.Param("id"), 0) user := ctx.MustGet("user").(*model.User) instanceRenewRequest.UserID = user.ID - removedAt, err := c.podService.Renew(instanceRenewRequest) + err = c.podService.Renew(instanceRenewRequest) if err != nil { ctx.JSON(http.StatusBadRequest, gin.H{ "code": http.StatusBadRequest, @@ -118,8 +118,7 @@ func (c *PodController) Renew(ctx *gin.Context) { } cache.C().DeleteByPrefix("pods") ctx.JSON(http.StatusOK, gin.H{ - "code": http.StatusOK, - "removed_at": removedAt, + "code": http.StatusOK, }) } @@ -142,15 +141,16 @@ func (c *PodController) Find(ctx *gin.Context) { } value, exist := cache.C().Get(fmt.Sprintf("pods:%s", utils.HashStruct(podFindRequest))) if !exist { - pods, _ := c.podService.Find(podFindRequest) + pods, total, _ := c.podService.Find(podFindRequest) value = gin.H{ - "code": http.StatusOK, - "data": pods, + "code": http.StatusOK, + "data": pods, + "total": total, } cache.C().Set( fmt.Sprintf("pods:%s", utils.HashStruct(podFindRequest)), value, - 5*time.Minute, + 2*time.Minute, ) } ctx.JSON(http.StatusOK, value) diff --git a/internal/extension/casbin/casbin.go b/internal/extension/casbin/casbin.go index 06b1b452..69f3055d 100644 --- a/internal/extension/casbin/casbin.go +++ b/internal/extension/casbin/casbin.go @@ -19,7 +19,7 @@ func InitCasbin() { &gormadapter.CasbinRule{}, "casbins", ) - cfg, err := files.FS.ReadFile("configs/casbin.conf") + cfg, err := files.F().ReadFile("configs/casbin.conf") md, _ := model.NewModelFromString(string(cfg)) Enforcer, err = casbin.NewEnforcer(md, adapter) if err != nil { diff --git a/internal/extension/container/manager/docker.go b/internal/extension/container/manager/docker.go index f86ef519..19b3ac6e 100644 --- a/internal/extension/container/manager/docker.go +++ b/internal/extension/container/manager/docker.go @@ -124,7 +124,7 @@ func (c *DockerManager) Setup() (nats []*model.Nat, err error) { for _, binding := range bindings { entries = append(entries, fmt.Sprintf( "%s:%d", - config.AppCfg().Container.Docker.Entry, + config.AppCfg().Container.Entry, convertor.ToIntD(binding.HostPort, 0), )) } @@ -147,7 +147,7 @@ func (c *DockerManager) Setup() (nats []*model.Nat, err error) { DstPort: convertor.ToIntD(binding.HostPort, 0), Entry: fmt.Sprintf( "%s:%d", - config.AppCfg().Container.Docker.Entry, + config.AppCfg().Container.Entry, convertor.ToIntD(binding.HostPort, 0), ), }) diff --git a/internal/extension/container/manager/k8s.go b/internal/extension/container/manager/k8s.go index ce9c469d..d4de2f23 100644 --- a/internal/extension/container/manager/k8s.go +++ b/internal/extension/container/manager/k8s.go @@ -139,7 +139,7 @@ func (c *K8sManager) Setup() (nats []*model.Nat, err error) { DstPort: int(servicePort.NodePort), Entry: fmt.Sprintf( "%s:%d", - config.AppCfg().Container.K8s.Entry, + config.AppCfg().Container.Entry, servicePort.NodePort, ), } diff --git a/internal/extension/container/provider/docker.go b/internal/extension/container/provider/docker.go index 1ec86ea1..965a362c 100644 --- a/internal/extension/container/provider/docker.go +++ b/internal/extension/container/provider/docker.go @@ -3,7 +3,6 @@ package provider import ( "context" "fmt" - "github.com/TwiN/go-color" "github.com/docker/docker/client" "github.com/elabosak233/cloudsdale/internal/app/config" "go.uber.org/zap" @@ -30,7 +29,7 @@ func InitDockerProvider() { zap.L().Info( fmt.Sprintf( "Docker client inits successfully, client version %s.", - color.InCyan(dockerClient.ClientVersion()), + dockerClient.ClientVersion(), ), ) dockerCli = dockerClient @@ -43,7 +42,7 @@ func InitDockerProvider() { zap.L().Info( fmt.Sprintf( "Docker remote server connects successfully, server version %s.", - color.InCyan(version.Version), + version.Version, ), ) } diff --git a/internal/extension/container/provider/k8s.go b/internal/extension/container/provider/k8s.go index 0ca35d69..a8010201 100644 --- a/internal/extension/container/provider/k8s.go +++ b/internal/extension/container/provider/k8s.go @@ -2,7 +2,6 @@ package provider import ( "fmt" - "github.com/TwiN/go-color" "github.com/elabosak233/cloudsdale/internal/app/config" "go.uber.org/zap" "k8s.io/client-go/kubernetes" @@ -20,7 +19,7 @@ func K8sCli() *kubernetes.Clientset { } func InitK8sProvider() { - k8sConfig := config.AppCfg().Container.K8s.Config + k8sConfig := config.AppCfg().Container.K8s.Config.Path checkK8sConfig(k8sConfig) cfg, err := clientcmd.BuildConfigFromFlags("", k8sConfig) if err != nil { @@ -35,7 +34,7 @@ func InitK8sProvider() { if err != nil { zap.L().Fatal("Kubernetes server connection failure.", zap.Error(err)) } - zap.L().Info(fmt.Sprintf("Kubernetes remote server connection successful, server version %s", color.InCyan(serverVersion))) + zap.L().Info(fmt.Sprintf("Kubernetes remote server connection successful, server version %s", serverVersion)) } func checkK8sConfig(k8sConfig string) { diff --git a/internal/extension/files/configs/application.json b/internal/extension/files/configs/application.json index 3beab0aa..36dd89bf 100644 --- a/internal/extension/files/configs/application.json +++ b/internal/extension/files/configs/application.json @@ -16,35 +16,31 @@ "db": 0 } }, - "paths": { - "media": "./media", - "assets": "./assets" - }, "host": "0.0.0.0", "port": 8888 }, "container": { "provider": "docker", + "entry": "127.0.0.1", "docker": { - "uri": "unix:///var/run/docker.sock", - "entry": "127.0.0.1" + "uri": "unix:///var/run/docker.sock" }, "k8s": { "namespace": "default", - "config": "./configs/k8s.yml", - "entry": "127.0.0.1" + "config": { + "path": "./configs/k8s.yml" + } }, "proxy": { "enabled": false, - "type": "ws" - }, - "traffic_capture": { - "enabled": false, - "path": "./captures" + "type": "ws", + "traffic_capture": { + "enabled": false + } } }, "db": { - "provider": "sqlite3", + "provider": "sqlite", "postgres": { "dbname": "cloudsdale", "host": "db", @@ -61,7 +57,7 @@ "port": 3306 }, "sqlite": { - "filename": "./db/cloudsdale.sqlite" + "path": "./db/db.sqlite" } }, "email": { diff --git a/internal/extension/files/files.go b/internal/extension/files/files.go index 172191fc..da116b95 100644 --- a/internal/extension/files/files.go +++ b/internal/extension/files/files.go @@ -1,11 +1,35 @@ -// Package files -// Everything in this package is embedded. -// Everything in this package is read-only. package files -import "embed" +import ( + "embed" + "github.com/elabosak233/cloudsdale/internal/utils" + "os" + "path" +) var ( //go:embed * statics/* templates/* - FS embed.FS + fs embed.FS ) + +func F() embed.FS { + return fs +} + +func ReadStaticFile(filename string) (data []byte, err error) { + if _, err = os.Stat(path.Join(utils.FilesPath, "statics", filename)); err == nil { + data, err = os.ReadFile(path.Join(utils.FilesPath, "statics", filename)) + } else { + data, err = F().ReadFile("statics/" + filename) + } + return data, err +} + +func ReadTemplateFile(filename string) (data []byte, err error) { + if _, err = os.Stat(path.Join(utils.FilesPath, "templates", filename)); err == nil { + data, err = os.ReadFile(path.Join(utils.FilesPath, "templates", filename)) + } else { + data, err = F().ReadFile("templates/" + filename) + } + return data, err +} diff --git a/internal/extension/proxy/tcp.go b/internal/extension/proxy/tcp.go index 8b19422c..dd4b10dd 100644 --- a/internal/extension/proxy/tcp.go +++ b/internal/extension/proxy/tcp.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "github.com/elabosak233/cloudsdale/internal/app/config" + "github.com/elabosak233/cloudsdale/internal/utils" "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/google/gopacket/pcapgo" @@ -51,7 +52,7 @@ func (t *TCPProxy) Setup() { zap.L().Error(fmt.Sprintf("Failed to accept connection: %v", err)) continue } - switch config.AppCfg().Container.TrafficCapture.Enabled { + switch config.AppCfg().Container.Proxy.TrafficCapture.Enabled { case true: go t.HandleInTrafficCapture(conn, t.Target) case false: @@ -103,7 +104,8 @@ func (t *TCPProxy) HandleInTrafficCapture(clientConn net.Conn, target string) { zap.L().Info(fmt.Sprintf("Proxying from %s to %s:%s", clientAddr, targetAddr, targetPort)) // Create a new pcap writer f, err := os.Create( - path.Join(config.AppCfg().Container.TrafficCapture.Path, + path.Join( + utils.CapturesPath, fmt.Sprintf( "%s-%s-%s-%s.pcap", clientAddr, diff --git a/internal/extension/proxy/ws.go b/internal/extension/proxy/ws.go index 281e644c..abceca21 100644 --- a/internal/extension/proxy/ws.go +++ b/internal/extension/proxy/ws.go @@ -3,6 +3,7 @@ package proxy import ( "fmt" "github.com/elabosak233/cloudsdale/internal/app/config" + "github.com/elabosak233/cloudsdale/internal/utils" "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/google/gopacket/pcapgo" @@ -44,7 +45,7 @@ func (w *WSProxy) Setup() { } func (w *WSProxy) Handle(conn *websocket.Conn) { - switch config.AppCfg().Container.TrafficCapture.Enabled { + switch config.AppCfg().Container.Proxy.TrafficCapture.Enabled { case true: w.handleInTrafficCapture(conn) case false: @@ -116,7 +117,7 @@ func (w *WSProxy) handleInTrafficCapture(conn *websocket.Conn) { targetPort := strings.Split(w.Target, ":")[1] f, err := os.Create( path.Join( - config.AppCfg().Container.TrafficCapture.Path, + utils.CapturesPath, fmt.Sprintf( "%s-%s-%s-%s.pcap", clientIP, diff --git a/internal/model/challenge.go b/internal/model/challenge.go index 484dfa43..46dddb76 100644 --- a/internal/model/challenge.go +++ b/internal/model/challenge.go @@ -2,7 +2,7 @@ package model import ( "fmt" - "github.com/elabosak233/cloudsdale/internal/app/config" + "github.com/elabosak233/cloudsdale/internal/utils" "gorm.io/gorm" "os" "path" @@ -43,7 +43,7 @@ func (c *Challenge) Simplify() { } func (c *Challenge) AfterFind(db *gorm.DB) (err error) { - p := path.Join(config.AppCfg().Gin.Paths.Media, "challenges", fmt.Sprintf("%d", c.ID)) + p := path.Join(utils.MediaPath, "challenges", fmt.Sprintf("%d", c.ID)) var name string var size int64 if files, _err := os.ReadDir(p); _err == nil { diff --git a/internal/model/game.go b/internal/model/game.go index ac3ea9a7..385f05ce 100644 --- a/internal/model/game.go +++ b/internal/model/game.go @@ -2,7 +2,7 @@ package model import ( "fmt" - "github.com/elabosak233/cloudsdale/internal/app/config" + "github.com/elabosak233/cloudsdale/internal/utils" "gorm.io/gorm" "os" "path" @@ -32,7 +32,7 @@ type Game struct { } func (g *Game) AfterFind(db *gorm.DB) (err error) { - p := path.Join(config.AppCfg().Gin.Paths.Media, "games", fmt.Sprintf("%d", g.ID), "poster") + p := path.Join(utils.MediaPath, "games", fmt.Sprintf("%d", g.ID), "poster") var name string var size int64 if files, _err := os.ReadDir(p); _err == nil { diff --git a/internal/model/pod.go b/internal/model/pod.go index 5e648640..bc2e5900 100644 --- a/internal/model/pod.go +++ b/internal/model/pod.go @@ -13,3 +13,9 @@ type Pod struct { RemovedAt int64 `json:"removed_at"` Nats []*Nat `json:"nats,omitempty"` } + +func (p *Pod) Simplify() { + if p.Challenge != nil { + p.Challenge.Simplify() + } +} diff --git a/internal/model/request/game_challenge_request.go b/internal/model/request/game_challenge_request.go index d85db88b..88d1b412 100644 --- a/internal/model/request/game_challenge_request.go +++ b/internal/model/request/game_challenge_request.go @@ -1,11 +1,11 @@ package request type GameChallengeFindRequest struct { - GameID uint `json:"game_id"` - ChallengeID uint `json:"challenge_id"` - TeamID uint `json:"team_id"` - IsEnabled *bool `json:"is_enabled"` - SubmissionQty int `json:"submission_qty"` + GameID uint `json:"game_id" form:"game_id"` + ChallengeID uint `json:"challenge_id" form:"challenge_id"` + TeamID uint `json:"team_id" form:"team_id"` + IsEnabled *bool `json:"is_enabled" form:"is_enabled"` + SubmissionQty int `json:"submission_qty" form:"submission_qty"` } type GameChallengeCreateRequest struct { diff --git a/internal/model/response/pod_response.go b/internal/model/response/pod_response.go deleted file mode 100644 index d7b41903..00000000 --- a/internal/model/response/pod_response.go +++ /dev/null @@ -1,21 +0,0 @@ -package response - -import ( - "github.com/elabosak233/cloudsdale/internal/model" -) - -type PodStatusResponse struct { - ID uint `json:"id"` - Challenge *model.Challenge `json:"challenge"` - Nats []*model.Nat `json:"nats"` - RemovedAt int64 `json:"removed_at"` - Status string `json:"status"` -} - -type PodResponse struct { - ID uint `json:"id"` - Challenge *model.Challenge `json:"challenge"` - Nats []*model.Nat `json:"nats"` - RemovedAt int64 `json:"removed_at"` - Status string `json:"status"` -} diff --git a/internal/model/team.go b/internal/model/team.go index 18059183..bf79fada 100644 --- a/internal/model/team.go +++ b/internal/model/team.go @@ -2,7 +2,7 @@ package model import ( "fmt" - "github.com/elabosak233/cloudsdale/internal/app/config" + "github.com/elabosak233/cloudsdale/internal/utils" "gorm.io/gorm" "os" "path" @@ -29,7 +29,7 @@ func (t *Team) Simplify() { } func (t *Team) AfterFind(db *gorm.DB) (err error) { - p := path.Join(config.AppCfg().Gin.Paths.Media, "teams", fmt.Sprintf("%d", t.ID)) + p := path.Join(utils.MediaPath, "teams", fmt.Sprintf("%d", t.ID)) var name string var size int64 if files, _err := os.ReadDir(p); _err == nil { diff --git a/internal/model/user.go b/internal/model/user.go index a707a9f3..66307609 100644 --- a/internal/model/user.go +++ b/internal/model/user.go @@ -2,7 +2,7 @@ package model import ( "fmt" - "github.com/elabosak233/cloudsdale/internal/app/config" + "github.com/elabosak233/cloudsdale/internal/utils" "gorm.io/gorm" "os" "path" @@ -28,7 +28,7 @@ func (u *User) Simplify() { } func (u *User) AfterFind(db *gorm.DB) (err error) { - p := path.Join(config.AppCfg().Gin.Paths.Media, "users", fmt.Sprintf("%d", u.ID)) + p := path.Join(utils.MediaPath, "users", fmt.Sprintf("%d", u.ID)) var name string var size int64 if files, _err := os.ReadDir(p); _err == nil { diff --git a/internal/service/media.go b/internal/service/media.go index 980a39d0..e7d764f8 100644 --- a/internal/service/media.go +++ b/internal/service/media.go @@ -2,7 +2,7 @@ package service import ( "fmt" - "github.com/elabosak233/cloudsdale/internal/app/config" + "github.com/elabosak233/cloudsdale/internal/utils" "io" "mime/multipart" "os" @@ -28,7 +28,7 @@ func NewMediaService() IMediaService { } func (m *MediaService) FindChallengeAttachment(id uint) (filename string, size int64, err error) { - p := path.Join(config.AppCfg().Gin.Paths.Media, "challenges", fmt.Sprintf("%d", id)) + p := path.Join(utils.MediaPath, "challenges", fmt.Sprintf("%d", id)) files, err := os.ReadDir(p) for _, file := range files { filename = file.Name() @@ -45,7 +45,7 @@ func (m *MediaService) SaveChallengeAttachment(id uint, fileHeader *multipart.Fi _ = file.Close() }(file) data, err := io.ReadAll(file) - p := path.Join(config.AppCfg().Gin.Paths.Media, "challenges", fmt.Sprintf("%d", id), fileHeader.Filename) + p := path.Join(utils.MediaPath, "challenges", fmt.Sprintf("%d", id), fileHeader.Filename) err = m.DeleteChallengeAttachment(id) dir := path.Dir(p) if _, err = os.Stat(dir); os.IsNotExist(err) { @@ -58,7 +58,7 @@ func (m *MediaService) SaveChallengeAttachment(id uint, fileHeader *multipart.Fi } func (m *MediaService) DeleteChallengeAttachment(id uint) (err error) { - p := path.Join(config.AppCfg().Gin.Paths.Media, "challenges", fmt.Sprintf("%d", id)) + p := path.Join(utils.MediaPath, "challenges", fmt.Sprintf("%d", id)) return os.RemoveAll(p) } @@ -68,7 +68,7 @@ func (m *MediaService) SaveGamePoster(id uint, fileHeader *multipart.FileHeader) _ = file.Close() }(file) data, err := io.ReadAll(file) - p := path.Join(config.AppCfg().Gin.Paths.Media, "games", fmt.Sprintf("%d", id), "poster", fileHeader.Filename) + p := path.Join(utils.MediaPath, "games", fmt.Sprintf("%d", id), "poster", fileHeader.Filename) err = m.DeleteGamePoster(id) dir := path.Dir(p) if _, err = os.Stat(dir); os.IsNotExist(err) { @@ -81,7 +81,7 @@ func (m *MediaService) SaveGamePoster(id uint, fileHeader *multipart.FileHeader) } func (m *MediaService) DeleteGamePoster(id uint) (err error) { - p := path.Join(config.AppCfg().Gin.Paths.Media, "games", fmt.Sprintf("%d", id), "poster") + p := path.Join(utils.MediaPath, "games", fmt.Sprintf("%d", id), "poster") return os.RemoveAll(p) } @@ -91,7 +91,7 @@ func (m *MediaService) SaveUserAvatar(id uint, fileHeader *multipart.FileHeader) _ = file.Close() }(file) data, err := io.ReadAll(file) - p := path.Join(config.AppCfg().Gin.Paths.Media, "users", fmt.Sprintf("%d", id), fileHeader.Filename) + p := path.Join(utils.MediaPath, "users", fmt.Sprintf("%d", id), fileHeader.Filename) err = m.DeleteUserAvatar(id) dir := path.Dir(p) if _, err = os.Stat(dir); os.IsNotExist(err) { @@ -104,7 +104,7 @@ func (m *MediaService) SaveUserAvatar(id uint, fileHeader *multipart.FileHeader) } func (m *MediaService) DeleteUserAvatar(id uint) (err error) { - p := path.Join(config.AppCfg().Gin.Paths.Media, "users", fmt.Sprintf("%d", id)) + p := path.Join(utils.MediaPath, "users", fmt.Sprintf("%d", id)) return os.RemoveAll(p) } @@ -114,7 +114,7 @@ func (m *MediaService) SaveTeamAvatar(id uint, fileHeader *multipart.FileHeader) _ = file.Close() }(file) data, err := io.ReadAll(file) - p := path.Join(config.AppCfg().Gin.Paths.Media, "teams", fmt.Sprintf("%d", id), fileHeader.Filename) + p := path.Join(utils.MediaPath, "teams", fmt.Sprintf("%d", id), fileHeader.Filename) err = m.DeleteTeamAvatar(id) dir := path.Dir(p) if _, err = os.Stat(dir); os.IsNotExist(err) { @@ -127,6 +127,6 @@ func (m *MediaService) SaveTeamAvatar(id uint, fileHeader *multipart.FileHeader) } func (m *MediaService) DeleteTeamAvatar(id uint) (err error) { - p := path.Join(config.AppCfg().Gin.Paths.Media, "teams", fmt.Sprintf("%d", id)) + p := path.Join(utils.MediaPath, "teams", fmt.Sprintf("%d", id)) return os.RemoveAll(p) } diff --git a/internal/service/pod.go b/internal/service/pod.go index c2cd0c9c..6491cd85 100644 --- a/internal/service/pod.go +++ b/internal/service/pod.go @@ -7,7 +7,6 @@ import ( "github.com/elabosak233/cloudsdale/internal/extension/container/manager" "github.com/elabosak233/cloudsdale/internal/model" "github.com/elabosak233/cloudsdale/internal/model/request" - "github.com/elabosak233/cloudsdale/internal/model/response" "github.com/elabosak233/cloudsdale/internal/repository" "github.com/elabosak233/cloudsdale/internal/utils" "github.com/elabosak233/cloudsdale/internal/utils/convertor" @@ -42,12 +41,10 @@ func SetUserInstanceRequestMap(userID uint, t int64) { } type IPodService interface { - Create(req request.PodCreateRequest) (res response.PodStatusResponse, err error) - Status(id uint) (rep response.PodStatusResponse, err error) - Renew(req request.PodRenewRequest) (removedAt int64, err error) - Remove(req request.PodRemoveRequest) (err error) - FindById(id uint) (rep response.PodResponse, err error) - Find(req request.PodFindRequest) (rep []response.PodResponse, err error) + Create(req request.PodCreateRequest) (model.Pod, error) + Renew(req request.PodRenewRequest) error + Remove(req request.PodRemoveRequest) error + Find(req request.PodFindRequest) ([]model.Pod, int64, error) } type PodService struct { @@ -118,10 +115,10 @@ func (t *PodService) ParallelLimit(req request.PodCreateRequest) { } } -func (t *PodService) Create(req request.PodCreateRequest) (res response.PodStatusResponse, err error) { +func (t *PodService) Create(req request.PodCreateRequest) (model.Pod, error) { remainder := t.IsLimited(req.UserID, int64(config.PltCfg().Container.RequestLimit)) if remainder != 0 { - return res, errors.New(fmt.Sprintf("请等待 %d 秒后再次请求", remainder)) + return model.Pod{}, errors.New(fmt.Sprintf("请等待 %d 秒后再次请求", remainder)) } SetUserInstanceRequestMap(req.UserID, time.Now().Unix()) challenges, _, _ := t.challengeRepository.Find(request.ChallengeFindRequest{ @@ -182,54 +179,34 @@ func (t *PodService) Create(req request.PodCreateRequest) (res response.PodStatu PodManagers[pod.ID] = ctnManager - return response.PodStatusResponse{ - ID: pod.ID, - Nats: pod.Nats, - RemovedAt: removedAt, - }, err -} + pod.Simplify() -func (t *PodService) Status(podID uint) (rep response.PodStatusResponse, err error) { - rep = response.PodStatusResponse{} - pod, err := t.podRepository.FindById(podID) - var ctn manager.IContainerManager - if PodManagers[podID] != nil { - ctn = PodManagers[podID] - rep.Status = "removed" - status, _ := ctn.Status() - if status != "removed" { - rep.Status = status - } - rep.ID = podID - rep.RemovedAt = pod.RemovedAt - return rep, nil - } - return rep, errors.New("获取失败") + return pod, err } -func (t *PodService) Renew(req request.PodRenewRequest) (removedAt int64, err error) { +func (t *PodService) Renew(req request.PodRenewRequest) error { remainder := t.IsLimited(req.UserID, int64(config.PltCfg().Container.RequestLimit)) if remainder != 0 { - return 0, errors.New(fmt.Sprintf("请等待 %d 秒后再次请求", remainder)) + return errors.New(fmt.Sprintf("请等待 %d 秒后再次请求", remainder)) } SetUserInstanceRequestMap(req.UserID, time.Now().Unix()) // 保存用户请求时间 pod, _ := t.podRepository.FindById(req.ID) ctn, ok := PodManagers[req.ID] if !ok { - return 0, errors.New("实例不存在") + return errors.New("实例不存在") } ctn.Renew(ctn.Duration()) pod.RemovedAt = time.Now().Add(ctn.Duration()).Unix() - err = t.podRepository.Update(pod) - return pod.RemovedAt, err + err := t.podRepository.Update(pod) + return err } -func (t *PodService) Remove(req request.PodRemoveRequest) (err error) { +func (t *PodService) Remove(req request.PodRemoveRequest) error { remainder := t.IsLimited(req.UserID, int64(config.PltCfg().Container.RequestLimit)) if remainder != 0 { return errors.New(fmt.Sprintf("请等待 %d 秒后再次请求", remainder)) } - _ = t.podRepository.Update(model.Pod{ + err := t.podRepository.Update(model.Pod{ ID: req.ID, RemovedAt: time.Now().Unix(), }) @@ -242,43 +219,15 @@ func (t *PodService) Remove(req request.PodRemoveRequest) (err error) { return err } -func (t *PodService) FindById(id uint) (rep response.PodResponse, err error) { - pod, _ := t.podRepository.FindById(id) - if PodManagers[id] != nil { - ctn := PodManagers[id] - status, _ := ctn.Status() - rep = response.PodResponse{ - ID: id, - RemovedAt: pod.RemovedAt, - Challenge: pod.Challenge, - Status: status, - } - return rep, nil - } - return rep, errors.New("获取失败") -} - -func (t *PodService) Find(req request.PodFindRequest) (pods []response.PodResponse, err error) { +func (t *PodService) Find(req request.PodFindRequest) ([]model.Pod, int64, error) { if req.TeamID != nil && req.GameID != nil { req.UserID = nil } - podResponse, _, err := t.podRepository.Find(req) - for _, pod := range podResponse { - status := "removed" - if PodManagers[pod.ID] != nil { - ctn := PodManagers[pod.ID] - s, _ := ctn.Status() - if s == "running" { - status = s - } - } - pods = append(pods, response.PodResponse{ - ID: pod.ID, - RemovedAt: pod.RemovedAt, - Nats: pod.Nats, - Challenge: pod.Challenge, - Status: status, - }) + pods, total, err := t.podRepository.Find(req) + + for i, pod := range pods { + pod.Simplify() + pods[i] = pod } - return pods, err + return pods, total, err } diff --git a/internal/utils/const.go b/internal/utils/const.go index eced4115..dbc5e2d9 100644 --- a/internal/utils/const.go +++ b/internal/utils/const.go @@ -6,3 +6,10 @@ var ( GitBranch = "N/A" GitTag = "N/A" ) + +const ( + ConfigsPath = "./configs" + MediaPath = "./media" + FilesPath = "./files" + CapturesPath = "./captures" +) diff --git a/web/src/components/widgets/admin/GameChallengeAccordion.tsx b/web/src/components/widgets/admin/GameChallengeAccordion.tsx index 253bf6aa..bd1ae929 100644 --- a/web/src/components/widgets/admin/GameChallengeAccordion.tsx +++ b/web/src/components/widgets/admin/GameChallengeAccordion.tsx @@ -133,7 +133,7 @@ export default function GameChallengeAccordion({ }, [gameChallenge]); return ( - +
{ @@ -187,7 +187,7 @@ function Page() { ); }} > - {category?.name} + {category?.name?.toUpperCase()} ) )}