Added static serving
This commit is contained in:
@@ -1,2 +1,3 @@
|
|||||||
.env
|
.env
|
||||||
config.yaml
|
config.yaml
|
||||||
|
mydeploys
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
-1
@@ -1,6 +1,8 @@
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
|
"path/filepath"
|
||||||
"quay/app/handlers"
|
"quay/app/handlers"
|
||||||
"quay/internal/config"
|
"quay/internal/config"
|
||||||
|
|
||||||
@@ -9,6 +11,17 @@ import (
|
|||||||
|
|
||||||
func Register(app *fiber.App, cfg *config.Config) {
|
func Register(app *fiber.App, cfg *config.Config) {
|
||||||
api := app.Group("/api")
|
api := app.Group("/api")
|
||||||
|
|
||||||
api.Get("/health", handlers.HealthCheck)
|
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))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,10 +13,13 @@ type GlobalConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SiteConfig struct {
|
type SiteConfig struct {
|
||||||
|
Name string `yaml:"name"`
|
||||||
Repo string `yaml:"repo"`
|
Repo string `yaml:"repo"`
|
||||||
Owner string `yaml:"owner"`
|
Owner string `yaml:"owner"`
|
||||||
Branch string `yaml:"branch"`
|
Branch string `yaml:"branch"`
|
||||||
Domain string `yaml:"domain"`
|
Domain string `yaml:"domain"`
|
||||||
|
SPA bool `yaml:"spa"`
|
||||||
|
NotFoundFile string `yaml:"not_found_file"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@@ -44,6 +47,9 @@ func validateConfig(config *Config) error {
|
|||||||
return &Error{Field: "global.storage_path", Message: "Storage path is required"}
|
return &Error{Field: "global.storage_path", Message: "Storage path is required"}
|
||||||
}
|
}
|
||||||
for i, site := range config.Sites {
|
for i, site := range config.Sites {
|
||||||
|
if site.Name == "" {
|
||||||
|
return &Error{Field: "sites[" + string(i) + "].name", Message: "Name is required"}
|
||||||
|
}
|
||||||
if site.Repo == "" {
|
if site.Repo == "" {
|
||||||
return &Error{Field: "sites[" + string(i) + "].repo", Message: "Repo is required"}
|
return &Error{Field: "sites[" + string(i) + "].repo", Message: "Repo is required"}
|
||||||
}
|
}
|
||||||
@@ -60,6 +66,14 @@ func validateConfig(config *Config) error {
|
|||||||
return nil
|
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) {
|
func Load(path string) (*Config, error) {
|
||||||
f, err := os.ReadFile(path)
|
f, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -72,6 +86,8 @@ func Load(path string) (*Config, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applyDefaults(&config)
|
||||||
|
|
||||||
err = validateConfig(&config)
|
err = validateConfig(&config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Setup(app *fiber.App) {
|
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")
|
c.Set("Content-Type", "application/json")
|
||||||
return c.Next()
|
return c.Next()
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user