diff --git a/.gitignore b/.gitignore index 65cbde7..ca1caee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .env -config.yaml \ No newline at end of file +config.yaml +mydeploys \ No newline at end of file diff --git a/app/handlers/static.go b/app/handlers/static.go new file mode 100644 index 0000000..7f3b7a1 --- /dev/null +++ b/app/handlers/static.go @@ -0,0 +1,53 @@ +package handlers + +import ( + "os" + "path/filepath" + "quay/internal/config" + + "github.com/gofiber/fiber/v3" +) + +func NewStaticHandler(storagePath string, siteMap map[string]config.SiteConfig) fiber.Handler { + return func(c fiber.Ctx) error { + site, ok := siteMap[c.Hostname()] + if !ok { + return c.SendStatus(fiber.StatusNotFound) + } + + urlPath := filepath.Clean(c.Path()) + + // Serve index.html for root + if urlPath == "/" || urlPath == "." { + urlPath = "/index.html" + } + + filePath := filepath.Join(storagePath, site.Name, urlPath) + + // If it's a directory, try index.html inside it + if info, err := os.Stat(filePath); err == nil && info.IsDir() { + filePath = filepath.Join(filePath, "index.html") + } + + // SPA fallback + if site.SPA { + if _, err := os.Stat(filePath); os.IsNotExist(err) { + filePath = filepath.Join(storagePath, site.Name, "index.html") + } + } + + if _, err := os.Stat(filePath); os.IsNotExist(err) { + notFoundFilePath := filepath.Join(storagePath, site.Name, site.NotFoundFile) + if _, err := os.Stat(notFoundFilePath); err == nil { + return c.SendFile(notFoundFilePath) + } + + //log.Printf("File not found: %s (requested by host: %s, path: %s)", filePath, c.Hostname(), c.Path()) + return c.SendStatus(fiber.StatusNotFound) + } + + //log.Printf("Serving file: %s (requested by host: %s, path: %s)", filePath, c.Hostname(), c.Path()) + + return c.SendFile(filePath) + } +} diff --git a/app/routes/routes.go b/app/routes/routes.go index 97b9436..29c4ad0 100644 --- a/app/routes/routes.go +++ b/app/routes/routes.go @@ -1,6 +1,8 @@ package routes import ( + "log" + "path/filepath" "quay/app/handlers" "quay/internal/config" @@ -9,6 +11,17 @@ import ( func Register(app *fiber.App, cfg *config.Config) { api := app.Group("/api") - api.Get("/health", handlers.HealthCheck) + + storagePath, err := filepath.Abs(cfg.Global.StoragePath) + if err != nil { + log.Fatalf("Failed to resolve storage path: %v", err) + } + + siteMap := make(map[string]config.SiteConfig) + for _, site := range cfg.Sites { + siteMap[site.Domain] = site + } + + app.Use(handlers.NewStaticHandler(storagePath, siteMap)) } diff --git a/internal/config/config.go b/internal/config/config.go index 11ac9eb..5052900 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -13,10 +13,13 @@ type GlobalConfig struct { } type SiteConfig struct { - Repo string `yaml:"repo"` - Owner string `yaml:"owner"` - Branch string `yaml:"branch"` - Domain string `yaml:"domain"` + Name string `yaml:"name"` + Repo string `yaml:"repo"` + Owner string `yaml:"owner"` + Branch string `yaml:"branch"` + Domain string `yaml:"domain"` + SPA bool `yaml:"spa"` + NotFoundFile string `yaml:"not_found_file"` } type Config struct { @@ -44,6 +47,9 @@ func validateConfig(config *Config) error { return &Error{Field: "global.storage_path", Message: "Storage path is required"} } for i, site := range config.Sites { + if site.Name == "" { + return &Error{Field: "sites[" + string(i) + "].name", Message: "Name is required"} + } if site.Repo == "" { return &Error{Field: "sites[" + string(i) + "].repo", Message: "Repo is required"} } @@ -60,6 +66,14 @@ func validateConfig(config *Config) error { return nil } +func applyDefaults(config *Config) { + for i := range config.Sites { + if config.Sites[i].NotFoundFile == "" { + config.Sites[i].NotFoundFile = "404.html" + } + } +} + func Load(path string) (*Config, error) { f, err := os.ReadFile(path) if err != nil { @@ -72,6 +86,8 @@ func Load(path string) (*Config, error) { return nil, err } + applyDefaults(&config) + err = validateConfig(&config) if err != nil { return nil, err diff --git a/internal/fiberconfig/fiberconfig.go b/internal/fiberconfig/fiberconfig.go index 45370e1..beb6f20 100644 --- a/internal/fiberconfig/fiberconfig.go +++ b/internal/fiberconfig/fiberconfig.go @@ -6,7 +6,7 @@ import ( ) func Setup(app *fiber.App) { - app.Use(func(c fiber.Ctx) error { + app.Use("/api", func(c fiber.Ctx) error { c.Set("Content-Type", "application/json") return c.Next() })