Updated static pages to use db

This commit is contained in:
2026-04-03 11:21:27 +02:00
parent 60461e49af
commit 12678f8241
7 changed files with 90 additions and 31 deletions
+19
View File
@@ -52,6 +52,25 @@ func (c *CachedSiteRepository) GetSite(id string) (*models.Site, error) {
return s, nil return s, nil
} }
func (c *CachedSiteRepository) GetSiteByDomain(domain string) (*models.Site, error) {
c.mu.RLock()
if s, ok := c.sites[domain]; ok {
c.mu.RUnlock()
return s, nil
}
c.mu.RUnlock()
s, err := c.inner.GetSiteByDomain(domain)
if err != nil {
return nil, err
}
c.mu.Lock()
c.sites[domain] = s
c.mu.Unlock()
return s, nil
}
func (c *CachedSiteRepository) ListSites() ([]models.Site, error) { func (c *CachedSiteRepository) ListSites() ([]models.Site, error) {
c.mu.RLock() c.mu.RLock()
if c.siteListValid { if c.siteListValid {
+36 -23
View File
@@ -4,15 +4,21 @@ import (
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"quay/internal/config" "quay/app/repository"
"regexp"
"github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3"
) )
func NewStaticHandler(storagePath string, siteMap map[string]config.SiteConfig) fiber.Handler { func NewStaticHandler(storagePath string, siteRepo repository.SiteRepository) fiber.Handler {
return func(c fiber.Ctx) error { return func(c fiber.Ctx) error {
site, ok := siteMap[c.Hostname()] domain := c.Hostname()
if !ok { site, err := siteRepo.GetSiteByDomain(domain)
if err != nil {
return c.Status(fiber.StatusInternalServerError).SendString("Failed to resolve site")
}
if site == nil {
return c.Status(fiber.StatusNotFound).SendString("Site not found") return c.Status(fiber.StatusNotFound).SendString("Site not found")
} }
@@ -22,25 +28,15 @@ func NewStaticHandler(storagePath string, siteMap map[string]config.SiteConfig)
urlPath := filepath.Clean(c.Path()) urlPath := filepath.Clean(c.Path())
for _, header := range site.CustomHeaders {
var matched bool
if header.Regex && header.Compiled != nil {
matched = header.Compiled.MatchString(urlPath)
} else {
matched, _ = path.Match(header.Source, urlPath)
}
if matched {
for key, value := range header.Headers {
c.Set(key, value)
}
}
}
for _, rule := range site.ForwardRules { for _, rule := range site.ForwardRules {
if rule.Regex && rule.Compiled != nil { if rule.Regex {
match := rule.Compiled.FindStringSubmatchIndex(urlPath) re, err := regexp.Compile(rule.Source)
if err != nil {
continue
}
match := re.FindStringSubmatchIndex(urlPath)
if match != nil { if match != nil {
dest := rule.Compiled.ExpandString([]byte{}, rule.Destination, urlPath, match) dest := re.ExpandString([]byte{}, rule.Destination, urlPath, match)
return c.Redirect().Status(rule.StatusCode).To(string(dest)) return c.Redirect().Status(rule.StatusCode).To(string(dest))
} }
} else if rule.Source == urlPath { } else if rule.Source == urlPath {
@@ -48,11 +44,28 @@ func NewStaticHandler(storagePath string, siteMap map[string]config.SiteConfig)
} }
} }
for _, customHeader := range site.CustomHeaders {
var matched bool
if customHeader.Regex {
re, err := regexp.Compile(customHeader.Source)
if err == nil {
matched = re.MatchString(urlPath)
}
} else {
matched, _ = path.Match(customHeader.Source, urlPath)
}
if matched {
for _, header := range customHeader.Headers {
c.Set(header.Key, header.Value)
}
}
}
if urlPath == "/" || urlPath == "." { if urlPath == "/" || urlPath == "." {
urlPath = "/index.html" urlPath = "/index.html"
} }
basePath := filepath.Join(storagePath, site.Name) basePath := filepath.Join(storagePath, site.ID)
filePath := filepath.Join(basePath, urlPath) filePath := filepath.Join(basePath, urlPath)
info, err := os.Stat(filePath) info, err := os.Stat(filePath)
@@ -67,7 +80,7 @@ func NewStaticHandler(storagePath string, siteMap map[string]config.SiteConfig)
} }
} }
if site.SPA { if site.Spa {
indexPath := filepath.Join(basePath, "index.html") indexPath := filepath.Join(basePath, "index.html")
if _, err := os.Stat(indexPath); err == nil { if _, err := os.Stat(indexPath); err == nil {
return c.SendFile(indexPath) return c.SendFile(indexPath)
+1
View File
@@ -17,6 +17,7 @@ type Header struct {
type CustomHeaders struct { type CustomHeaders struct {
ID string `json:"id"` ID string `json:"id"`
Source string `json:"source"` Source string `json:"source"`
Regex bool `json:"regex"`
Headers []Header `json:"headers"` Headers []Header `json:"headers"`
} }
+1
View File
@@ -4,6 +4,7 @@ import "quay/app/models"
type SiteRepository interface { type SiteRepository interface {
GetSite(id string) (*models.Site, error) GetSite(id string) (*models.Site, error)
GetSiteByDomain(domain string) (*models.Site, error)
ListSites() ([]models.Site, error) ListSites() ([]models.Site, error)
CreateSite(s *models.Site) error CreateSite(s *models.Site) error
UpdateSite(s *models.Site) error UpdateSite(s *models.Site) error
+1 -1
View File
@@ -66,5 +66,5 @@ func Register(app *fiber.App, cfg *config.Config, envCfg *envconfig.EnvConfig, d
siteMap[site.Domain] = site siteMap[site.Domain] = site
} }
app.Use(handlers.NewStaticHandler(storagePath, siteMap)) app.Use(handlers.NewStaticHandler(storagePath, siteRepository))
} }
+1
View File
@@ -34,6 +34,7 @@ CREATE TABLE IF NOT EXISTS custom_headers (
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
site_id TEXT NOT NULL, site_id TEXT NOT NULL,
source TEXT NOT NULL, source TEXT NOT NULL,
regex INTEGER NOT NULL DEFAULT 0,
FOREIGN KEY (site_id) REFERENCES sites(id) ON DELETE CASCADE FOREIGN KEY (site_id) REFERENCES sites(id) ON DELETE CASCADE
); );
+31 -7
View File
@@ -2,6 +2,7 @@ package database
import ( import (
"database/sql" "database/sql"
"errors"
"fmt" "fmt"
"quay/app/models" "quay/app/models"
"quay/app/repository" "quay/app/repository"
@@ -38,6 +39,25 @@ func (r *SQLiteSiteRepository) GetSite(id string) (*models.Site, error) {
return s, nil return s, nil
} }
func (r *SQLiteSiteRepository) GetSiteByDomain(domain string) (*models.Site, error) {
row := r.db.QueryRow(`
SELECT id, git_server, owner, repository, branch, domain, deploy_token, enabled, not_found_file
FROM sites WHERE domain = ?`, domain)
s, err := scanSite(row)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
}
return nil, fmt.Errorf("get site by domain: %w", err)
}
if err := r.populateSiteRelations(s); err != nil {
return nil, err
}
return s, nil
}
func (r *SQLiteSiteRepository) ListSites() ([]models.Site, error) { func (r *SQLiteSiteRepository) ListSites() ([]models.Site, error) {
rows, err := r.db.Query(` rows, err := r.db.Query(`
SELECT id, git_server, owner, repository, branch, domain, deploy_token, enabled, not_found_file SELECT id, git_server, owner, repository, branch, domain, deploy_token, enabled, not_found_file
@@ -205,10 +225,11 @@ func (r *SQLiteSiteRepository) DeleteForwardRule(id string) error {
// Custom Headers // Custom Headers
func (r *SQLiteSiteRepository) GetCustomHeaders(id string) (*models.CustomHeaders, error) { func (r *SQLiteSiteRepository) GetCustomHeaders(id string) (*models.CustomHeaders, error) {
row := r.db.QueryRow(`SELECT id, source FROM custom_headers WHERE id = ?`, id) row := r.db.QueryRow(`SELECT id, source, regex FROM custom_headers WHERE id = ?`, id)
var ch models.CustomHeaders var ch models.CustomHeaders
if err := row.Scan(&ch.ID, &ch.Source); err != nil { var regex int
if err := row.Scan(&ch.ID, &ch.Source, &regex); err != nil {
return nil, fmt.Errorf("get custom headers: %w", err) return nil, fmt.Errorf("get custom headers: %w", err)
} }
@@ -217,6 +238,7 @@ func (r *SQLiteSiteRepository) GetCustomHeaders(id string) (*models.CustomHeader
return nil, err return nil, err
} }
ch.Headers = headers ch.Headers = headers
ch.Regex = regex != 0
return &ch, nil return &ch, nil
} }
@@ -239,7 +261,7 @@ func (r *SQLiteSiteRepository) UpdateCustomHeaders(ch *models.CustomHeaders) err
} }
defer tx.Rollback() defer tx.Rollback()
if _, err := tx.Exec(`UPDATE custom_headers SET source=? WHERE id=?`, ch.Source, ch.ID); err != nil { if _, err := tx.Exec(`UPDATE custom_headers SET source=?, regex=? WHERE id=?`, ch.Source, ch.Regex, ch.ID); err != nil {
return fmt.Errorf("update custom headers: %w", err) return fmt.Errorf("update custom headers: %w", err)
} }
if _, err := tx.Exec(`DELETE FROM headers WHERE custom_header_id = ?`, ch.ID); err != nil { if _, err := tx.Exec(`DELETE FROM headers WHERE custom_header_id = ?`, ch.ID); err != nil {
@@ -360,7 +382,7 @@ func (r *SQLiteSiteRepository) listForwardRules(siteID string) ([]models.Forward
} }
func (r *SQLiteSiteRepository) listCustomHeaders(siteID string) ([]models.CustomHeaders, error) { func (r *SQLiteSiteRepository) listCustomHeaders(siteID string) ([]models.CustomHeaders, error) {
rows, err := r.db.Query(`SELECT id, source FROM custom_headers WHERE site_id = ?`, siteID) rows, err := r.db.Query(`SELECT id, source, regex FROM custom_headers WHERE site_id = ?`, siteID)
if err != nil { if err != nil {
return nil, fmt.Errorf("list custom headers: %w", err) return nil, fmt.Errorf("list custom headers: %w", err)
} }
@@ -368,10 +390,12 @@ func (r *SQLiteSiteRepository) listCustomHeaders(siteID string) ([]models.Custom
var result []models.CustomHeaders var result []models.CustomHeaders
for rows.Next() { for rows.Next() {
var ch models.CustomHeaders var ch models.CustomHeaders
if err := rows.Scan(&ch.ID, &ch.Source); err != nil { var regex int
if err := rows.Scan(&ch.ID, &ch.Source, &regex); err != nil {
rows.Close() rows.Close()
return nil, fmt.Errorf("list custom headers scan: %w", err) return nil, fmt.Errorf("list custom headers scan: %w", err)
} }
ch.Regex = regex != 0
result = append(result, ch) result = append(result, ch)
} }
rows.Close() rows.Close()
@@ -424,8 +448,8 @@ func insertForwardRule(tx *sql.Tx, siteID string, fr *models.ForwardRule) error
func insertCustomHeaders(tx *sql.Tx, siteID string, ch *models.CustomHeaders) error { func insertCustomHeaders(tx *sql.Tx, siteID string, ch *models.CustomHeaders) error {
ch.ID = uuid.NewString() ch.ID = uuid.NewString()
_, err := tx.Exec(` _, err := tx.Exec(`
INSERT INTO custom_headers (id, site_id, source) VALUES (?, ?, ?)`, INSERT INTO custom_headers (id, site_id, source, regex) VALUES (?, ?, ?, ?)`,
ch.ID, siteID, ch.Source, ch.ID, siteID, ch.Source, ch.Regex,
) )
if err != nil { if err != nil {
return fmt.Errorf("insert custom headers: %w", err) return fmt.Errorf("insert custom headers: %w", err)