From 29ee01afba49f7334186a002503b9b6d3cc385b4 Mon Sep 17 00:00:00 2001 From: KartoffelChips <104089082+KartoffelChipss@users.noreply.github.com> Date: Fri, 3 Apr 2026 11:51:36 +0200 Subject: [PATCH] Added frontend --- .idea/sqldialects.xml | 1 - .idea/vcs.xml | 1 + .gitignore => backend/.gitignore | 0 .../app}/cachedrepo/cached_site_repository.go | 0 {app => backend/app}/github/github.go | 0 {app => backend/app}/handlers/health.go | 0 {app => backend/app}/handlers/site.go | 0 .../app}/handlers/site_relations.go | 0 {app => backend/app}/handlers/static.go | 0 {app => backend/app}/handlers/update.go | 0 .../app}/middleware/api_host_guard.go | 0 {app => backend/app}/models/api_error.go | 0 {app => backend/app}/models/site.go | 0 .../app}/repository/site_repository.go | 0 {app => backend/app}/routes/routes.go | 0 .../config.example.yaml | 0 {docs => backend/docs}/docs.go | 0 {docs => backend/docs}/swagger.json | 0 {docs => backend/docs}/swagger.yaml | 0 go.mod => backend/go.mod | 0 go.sum => backend/go.sum | 0 .../internal}/database/init_sqlite.go | 0 .../internal}/database/site_sqlite.go | 0 .../internal}/database/sqlite.go | 0 .../internal}/envconfig/envconfig.go | 0 .../internal}/fiberconfig/fiberconfig.go | 0 .../internal}/security/deploy_token.go | 0 main.go => backend/main.go | 0 internal/config/config.go | 163 ----------------- theme.css | 173 ------------------ 30 files changed, 1 insertion(+), 337 deletions(-) rename .gitignore => backend/.gitignore (100%) rename {app => backend/app}/cachedrepo/cached_site_repository.go (100%) rename {app => backend/app}/github/github.go (100%) rename {app => backend/app}/handlers/health.go (100%) rename {app => backend/app}/handlers/site.go (100%) rename {app => backend/app}/handlers/site_relations.go (100%) rename {app => backend/app}/handlers/static.go (100%) rename {app => backend/app}/handlers/update.go (100%) rename {app => backend/app}/middleware/api_host_guard.go (100%) rename {app => backend/app}/models/api_error.go (100%) rename {app => backend/app}/models/site.go (100%) rename {app => backend/app}/repository/site_repository.go (100%) rename {app => backend/app}/routes/routes.go (100%) rename config.example.yaml => backend/config.example.yaml (100%) rename {docs => backend/docs}/docs.go (100%) rename {docs => backend/docs}/swagger.json (100%) rename {docs => backend/docs}/swagger.yaml (100%) rename go.mod => backend/go.mod (100%) rename go.sum => backend/go.sum (100%) rename {internal => backend/internal}/database/init_sqlite.go (100%) rename {internal => backend/internal}/database/site_sqlite.go (100%) rename {internal => backend/internal}/database/sqlite.go (100%) rename {internal => backend/internal}/envconfig/envconfig.go (100%) rename {internal => backend/internal}/fiberconfig/fiberconfig.go (100%) rename {internal => backend/internal}/security/deploy_token.go (100%) rename main.go => backend/main.go (100%) delete mode 100644 internal/config/config.go delete mode 100644 theme.css diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml index 12103cf..c0e01ca 100644 --- a/.idea/sqldialects.xml +++ b/.idea/sqldialects.xml @@ -1,7 +1,6 @@ - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..317ebc3 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/.gitignore b/backend/.gitignore similarity index 100% rename from .gitignore rename to backend/.gitignore diff --git a/app/cachedrepo/cached_site_repository.go b/backend/app/cachedrepo/cached_site_repository.go similarity index 100% rename from app/cachedrepo/cached_site_repository.go rename to backend/app/cachedrepo/cached_site_repository.go diff --git a/app/github/github.go b/backend/app/github/github.go similarity index 100% rename from app/github/github.go rename to backend/app/github/github.go diff --git a/app/handlers/health.go b/backend/app/handlers/health.go similarity index 100% rename from app/handlers/health.go rename to backend/app/handlers/health.go diff --git a/app/handlers/site.go b/backend/app/handlers/site.go similarity index 100% rename from app/handlers/site.go rename to backend/app/handlers/site.go diff --git a/app/handlers/site_relations.go b/backend/app/handlers/site_relations.go similarity index 100% rename from app/handlers/site_relations.go rename to backend/app/handlers/site_relations.go diff --git a/app/handlers/static.go b/backend/app/handlers/static.go similarity index 100% rename from app/handlers/static.go rename to backend/app/handlers/static.go diff --git a/app/handlers/update.go b/backend/app/handlers/update.go similarity index 100% rename from app/handlers/update.go rename to backend/app/handlers/update.go diff --git a/app/middleware/api_host_guard.go b/backend/app/middleware/api_host_guard.go similarity index 100% rename from app/middleware/api_host_guard.go rename to backend/app/middleware/api_host_guard.go diff --git a/app/models/api_error.go b/backend/app/models/api_error.go similarity index 100% rename from app/models/api_error.go rename to backend/app/models/api_error.go diff --git a/app/models/site.go b/backend/app/models/site.go similarity index 100% rename from app/models/site.go rename to backend/app/models/site.go diff --git a/app/repository/site_repository.go b/backend/app/repository/site_repository.go similarity index 100% rename from app/repository/site_repository.go rename to backend/app/repository/site_repository.go diff --git a/app/routes/routes.go b/backend/app/routes/routes.go similarity index 100% rename from app/routes/routes.go rename to backend/app/routes/routes.go diff --git a/config.example.yaml b/backend/config.example.yaml similarity index 100% rename from config.example.yaml rename to backend/config.example.yaml diff --git a/docs/docs.go b/backend/docs/docs.go similarity index 100% rename from docs/docs.go rename to backend/docs/docs.go diff --git a/docs/swagger.json b/backend/docs/swagger.json similarity index 100% rename from docs/swagger.json rename to backend/docs/swagger.json diff --git a/docs/swagger.yaml b/backend/docs/swagger.yaml similarity index 100% rename from docs/swagger.yaml rename to backend/docs/swagger.yaml diff --git a/go.mod b/backend/go.mod similarity index 100% rename from go.mod rename to backend/go.mod diff --git a/go.sum b/backend/go.sum similarity index 100% rename from go.sum rename to backend/go.sum diff --git a/internal/database/init_sqlite.go b/backend/internal/database/init_sqlite.go similarity index 100% rename from internal/database/init_sqlite.go rename to backend/internal/database/init_sqlite.go diff --git a/internal/database/site_sqlite.go b/backend/internal/database/site_sqlite.go similarity index 100% rename from internal/database/site_sqlite.go rename to backend/internal/database/site_sqlite.go diff --git a/internal/database/sqlite.go b/backend/internal/database/sqlite.go similarity index 100% rename from internal/database/sqlite.go rename to backend/internal/database/sqlite.go diff --git a/internal/envconfig/envconfig.go b/backend/internal/envconfig/envconfig.go similarity index 100% rename from internal/envconfig/envconfig.go rename to backend/internal/envconfig/envconfig.go diff --git a/internal/fiberconfig/fiberconfig.go b/backend/internal/fiberconfig/fiberconfig.go similarity index 100% rename from internal/fiberconfig/fiberconfig.go rename to backend/internal/fiberconfig/fiberconfig.go diff --git a/internal/security/deploy_token.go b/backend/internal/security/deploy_token.go similarity index 100% rename from internal/security/deploy_token.go rename to backend/internal/security/deploy_token.go diff --git a/main.go b/backend/main.go similarity index 100% rename from main.go rename to backend/main.go diff --git a/internal/config/config.go b/internal/config/config.go deleted file mode 100644 index b292b8b..0000000 --- a/internal/config/config.go +++ /dev/null @@ -1,163 +0,0 @@ -package config - -import ( - "fmt" - "os" - "path" - "regexp" - "strconv" - - "gopkg.in/yaml.v3" -) - -type ForwardRule struct { - Source string `yaml:"source"` - Destination string `yaml:"destination"` - StatusCode int `yaml:"status_code"` - Regex bool `yaml:"regex"` - Compiled *regexp.Regexp -} - -type CustomHeader struct { - Source string `yaml:"source"` - Regex bool `yaml:"regex"` - Headers map[string]string `yaml:"headers"` - Compiled *regexp.Regexp -} - -type SiteConfig struct { - Name string `yaml:"name"` - Repo string `yaml:"repo"` - Owner string `yaml:"owner"` - Branch string `yaml:"branch"` - Domain string `yaml:"domain"` - Enabled bool `yaml:"enabled"` - SPA bool `yaml:"spa"` - NotFoundFile string `yaml:"not_found_file"` - DeployToken string `yaml:"deploy_token"` - ForwardRules []ForwardRule `yaml:"forward_rules"` - CustomHeaders []CustomHeader `yaml:"custom_headers"` -} - -type Config struct { - Sites []SiteConfig `yaml:"sites"` -} - -type Error struct { - Field string - Message string -} - -func (c Error) Error() string { - return "Config error: " + c.Field + " - " + c.Message -} - -func validateConfig(config *Config) error { - for i, site := range config.Sites { - if site.Name == "" { - return &Error{Field: "sites[" + strconv.Itoa(i) + "].name", Message: "Name is required"} - } - if site.Repo == "" { - return &Error{Field: "sites[" + strconv.Itoa(i) + "].repo", Message: "Repo is required"} - } - if site.Owner == "" { - return &Error{Field: "sites[" + strconv.Itoa(i) + "].owner", Message: "Owner is required"} - } - if site.Branch == "" { - return &Error{Field: "sites[" + strconv.Itoa(i) + "].branch", Message: "Branch is required"} - } - if site.Domain == "" { - return &Error{Field: "sites[" + strconv.Itoa(i) + "].domain", Message: "Domain is required"} - } - for j, rule := range site.ForwardRules { - if rule.Source == "" { - return &Error{Field: "sites[" + strconv.Itoa(i) + "].forward_rules[" + strconv.Itoa(j) + "].source", Message: "Source is required"} - } - if rule.Destination == "" { - return &Error{Field: "sites[" + strconv.Itoa(i) + "].forward_rules[" + strconv.Itoa(j) + "].destination", Message: "Destination is required"} - } - if rule.StatusCode == 0 { - return &Error{Field: "sites[" + strconv.Itoa(i) + "].forward_rules[" + strconv.Itoa(j) + "].status_code", Message: "Status code is required"} - } - if rule.StatusCode < 300 || rule.StatusCode >= 400 { - return &Error{Field: "sites[" + strconv.Itoa(i) + "].forward_rules[" + strconv.Itoa(j) + "].status_code", Message: "Status code must be a valid redirect code (300-399)"} - } - if rule.Regex { - re, err := regexp.Compile(rule.Source) - if err != nil { - return &Error{ - Field: fmt.Sprintf("sites[%d].forward_rules[%d].source", i, j), - Message: "Invalid regex: " + err.Error(), - } - } - config.Sites[i].ForwardRules[j].Compiled = re - } - } - for k, header := range site.CustomHeaders { - if header.Source == "" { - return &Error{Field: fmt.Sprintf("sites[%d].custom_headers[%d].source", i, k), Message: "Source is required"} - } - if len(header.Headers) == 0 { - return &Error{Field: fmt.Sprintf("sites[%d].custom_headers[%d].headers", i, k), Message: "At least one header is required"} - } - if header.Regex { - re, err := regexp.Compile(header.Source) - if err != nil { - return &Error{ - Field: fmt.Sprintf("sites[%d].custom_headers[%d].source", i, k), - Message: "Invalid regex: " + err.Error(), - } - } - config.Sites[i].CustomHeaders[k].Compiled = re - } else { - if _, err := path.Match(header.Source, ""); err != nil { - return &Error{ - Field: fmt.Sprintf("sites[%d].custom_headers[%d].source", i, k), - Message: "Invalid glob pattern: " + err.Error(), - } - } - } - } - } - return nil -} - -func applyDefaults(config *Config) { - for i := range config.Sites { - if config.Sites[i].NotFoundFile == "" { - config.Sites[i].NotFoundFile = "404.html" - } - for j := range config.Sites[i].ForwardRules { - if config.Sites[i].ForwardRules[j].StatusCode == 0 { - config.Sites[i].ForwardRules[j].StatusCode = 302 - } - } - } -} - -func Load(path string) (*Config, error) { - f, err := os.ReadFile(path) - if err != nil { - return nil, err - } - expanded := os.Expand(string(f), func(key string) string { - val, ok := os.LookupEnv(key) - if !ok { - return "${" + key + "}" // env vars that aren't set are left as is to not cause errors with regex patterns - } - return val - }) - var config Config - err = yaml.Unmarshal([]byte(expanded), &config) - if err != nil { - return nil, err - } - - applyDefaults(&config) - - err = validateConfig(&config) - if err != nil { - return nil, err - } - return &config, nil -} diff --git a/theme.css b/theme.css deleted file mode 100644 index 7841966..0000000 --- a/theme.css +++ /dev/null @@ -1,173 +0,0 @@ -@import "tailwindcss"; - -@custom-variant dark (&:is(.dark *)); - -:root { - --background: oklch(1.0000 0 0); - --foreground: oklch(0.3211 0 0); - --card: oklch(1.0000 0 0); - --card-foreground: oklch(0.3211 0 0); - --popover: oklch(1.0000 0 0); - --popover-foreground: oklch(0.3211 0 0); - --primary: oklch(0.6225 0.2041 259.9027); - --primary-foreground: oklch(1.0000 0 0); - --secondary: oklch(0.9665 0.0045 258.3247); - --secondary-foreground: oklch(0.4419 0.0375 257.2811); - --muted: oklch(0.9846 0.0017 247.8389); - --muted-foreground: oklch(0.5471 0.0321 263.2921); - --accent: oklch(0.9510 0.0267 237.5723); - --accent-foreground: oklch(0.3742 0.1844 263.9420); - --destructive: oklch(0.6496 0.2362 26.9032); - --destructive-foreground: oklch(1.0000 0 0); - --border: oklch(0.9271 0.0075 260.7315); - --input: oklch(0.9271 0.0075 260.7315); - --ring: oklch(0.6225 0.2041 259.9027); - --chart-1: oklch(0.6225 0.2041 259.9027); - --chart-2: oklch(0.5469 0.2507 262.8085); - --chart-3: oklch(0.4902 0.2693 263.7106); - --chart-4: oklch(0.4234 0.2370 263.9162); - --chart-5: oklch(0.3742 0.1844 263.9420); - --sidebar: oklch(0.9846 0.0017 247.8389); - --sidebar-foreground: oklch(0.3211 0 0); - --sidebar-primary: oklch(0.6225 0.2041 259.9027); - --sidebar-primary-foreground: oklch(1.0000 0 0); - --sidebar-accent: oklch(0.9510 0.0267 237.5723); - --sidebar-accent-foreground: oklch(0.3742 0.1844 263.9420); - --sidebar-border: oklch(0.9271 0.0075 260.7315); - --sidebar-ring: oklch(0.6225 0.2041 259.9027); - --font-sans: Inter, sans-serif; - --font-serif: Source Serif 4, serif; - --font-mono: JetBrains Mono, monospace; - --radius: 1.15rem; - --shadow-x: 0; - --shadow-y: 1px; - --shadow-blur: 3px; - --shadow-spread: 0px; - --shadow-opacity: 0.1; - --shadow-color: oklch(0 0 0); - --shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); - --shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); - --shadow-sm: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); - --shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); - --shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10); - --shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10); - --shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10); - --shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25); - --tracking-normal: 0em; - --spacing: 0.25rem; -} - -.dark { - --background: oklch(0.2046 0 0); - --foreground: oklch(0.9219 0 0); - --card: oklch(0.2686 0 0); - --card-foreground: oklch(0.9219 0 0); - --popover: oklch(0.2686 0 0); - --popover-foreground: oklch(0.9219 0 0); - --primary: oklch(0.6225 0.2041 259.9027); - --primary-foreground: oklch(1.0000 0 0); - --secondary: oklch(0.2686 0 0); - --secondary-foreground: oklch(0.9219 0 0); - --muted: oklch(0.2393 0 0); - --muted-foreground: oklch(0.7155 0 0); - --accent: oklch(0.5802 0.1915 259.7416); - --accent-foreground: oklch(0.8820 0.0588 253.9688); - --destructive: oklch(0.6496 0.2362 26.9032); - --destructive-foreground: oklch(1.0000 0 0); - --border: oklch(0.3715 0 0); - --input: oklch(0.3715 0 0); - --ring: oklch(0.6225 0.2041 259.9027); - --chart-1: oklch(0.7122 0.1526 254.9868); - --chart-2: oklch(0.6225 0.2041 259.9027); - --chart-3: oklch(0.5739 0.2334 262.7735); - --chart-4: oklch(0.6225 0.2041 259.9027); - --chart-5: oklch(0.6225 0.2041 259.9027); - --sidebar: oklch(0.2046 0 0); - --sidebar-foreground: oklch(0.9219 0 0); - --sidebar-primary: oklch(0.6225 0.2041 259.9027); - --sidebar-primary-foreground: oklch(1.0000 0 0); - --sidebar-accent: oklch(0.6225 0.2041 259.9027); - --sidebar-accent-foreground: oklch(0.8820 0.0588 253.9688); - --sidebar-border: oklch(0.3715 0 0); - --sidebar-ring: oklch(0.6225 0.2041 259.9027); - --font-sans: Inter, sans-serif; - --font-serif: Source Serif 4, serif; - --font-mono: JetBrains Mono, monospace; - --radius: 1.15rem; - --shadow-x: 0; - --shadow-y: 1px; - --shadow-blur: 3px; - --shadow-spread: 0px; - --shadow-opacity: 0.1; - --shadow-color: oklch(0 0 0); - --shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); - --shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); - --shadow-sm: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); - --shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); - --shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10); - --shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10); - --shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10); - --shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25); -} - -@theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --color-card: var(--card); - --color-card-foreground: var(--card-foreground); - --color-popover: var(--popover); - --color-popover-foreground: var(--popover-foreground); - --color-primary: var(--primary); - --color-primary-foreground: var(--primary-foreground); - --color-secondary: var(--secondary); - --color-secondary-foreground: var(--secondary-foreground); - --color-muted: var(--muted); - --color-muted-foreground: var(--muted-foreground); - --color-accent: var(--accent); - --color-accent-foreground: var(--accent-foreground); - --color-destructive: var(--destructive); - --color-destructive-foreground: var(--destructive-foreground); - --color-border: var(--border); - --color-input: var(--input); - --color-ring: var(--ring); - --color-chart-1: var(--chart-1); - --color-chart-2: var(--chart-2); - --color-chart-3: var(--chart-3); - --color-chart-4: var(--chart-4); - --color-chart-5: var(--chart-5); - --color-sidebar: var(--sidebar); - --color-sidebar-foreground: var(--sidebar-foreground); - --color-sidebar-primary: var(--sidebar-primary); - --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); - --color-sidebar-accent: var(--sidebar-accent); - --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); - --color-sidebar-border: var(--sidebar-border); - --color-sidebar-ring: var(--sidebar-ring); - - --font-sans: var(--font-sans); - --font-mono: var(--font-mono); - --font-serif: var(--font-serif); - - --radius-sm: calc(var(--radius) - 4px); - --radius-md: calc(var(--radius) - 2px); - --radius-lg: var(--radius); - --radius-xl: calc(var(--radius) + 4px); - - --shadow-2xs: var(--shadow-2xs); - --shadow-xs: var(--shadow-xs); - --shadow-sm: var(--shadow-sm); - --shadow: var(--shadow); - --shadow-md: var(--shadow-md); - --shadow-lg: var(--shadow-lg); - --shadow-xl: var(--shadow-xl); - --shadow-2xl: var(--shadow-2xl); -} - -@layer base { - * { - @apply border-border outline-ring/50; - } - body { - @apply bg-background text-foreground; - } -}