Add frontend #1
Generated
-1
@@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="SqlDialectMappings">
|
||||
<file url="file://$PROJECT_DIR$/app/repository/site_repository.go" dialect="GenericSQL" />
|
||||
<file url="PROJECT" dialect="SQLite" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+1
@@ -2,5 +2,6 @@
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/frontend" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user