2.0.1 dcf7d6b

config.go RawHistoryFolder

1package config
2
3import (
4	"fmt"
5	"strings"
6
7	"github.com/spf13/viper"
8)
9
10type Config struct {
11	Server       ServerConfig      `mapstructure:"server"`
12	RegistryAuth RegistryAuthConfig `mapstructure:"registry_auth"`
13	Routes       map[string]string `mapstructure:"routes"`
14}
15
16type ServerConfig struct {
17	Port           int    `mapstructure:"port"`
18	RegistryPort   int    `mapstructure:"registry_port"`
19	RegistryDomain string `mapstructure:"registry_domain"`
20	Runtime        string `mapstructure:"runtime"`
21	SocketPath     string `mapstructure:"socket_path"`
22	SSLEmail       string `mapstructure:"ssl_email"`
23	DataDir        string `mapstructure:"data_dir"`
24}
25
26type RegistryAuthConfig struct {
27	Enabled  bool   `mapstructure:"enabled"`
28	Username string `mapstructure:"username"`
29	Password string `mapstructure:"password"`
30}
31
32type Route struct {
33	Domain string
34	Image  string
35	HTTPS  bool
36}
37
38func Load() (*Config, error) {
39	var cfg Config
40	
41	// Set defaults
42	viper.SetDefault("server.port", 8080)
43	viper.SetDefault("server.registry_port", 5000)
44	viper.SetDefault("server.runtime", "auto")
45	viper.SetDefault("server.socket_path", "")
46	viper.SetDefault("server.data_dir", "./data")
47	viper.SetDefault("registry_auth.enabled", true)
48
49	// Handle the routes manually since Viper struggles with domain names
50	cfg.Routes = make(map[string]string)
51	
52	// Get server config first
53	if err := viper.UnmarshalKey("server", &cfg.Server); err != nil {
54		return nil, fmt.Errorf("unable to decode server config: %v", err)
55	}
56	
57	// Get registry auth config
58	if err := viper.UnmarshalKey("registry_auth", &cfg.RegistryAuth); err != nil {
59		return nil, fmt.Errorf("unable to decode registry auth config: %v", err)
60	}
61	
62	// Get routes manually from the raw config
63	routesRaw := viper.Get("routes")
64	if routesRaw != nil {
65		if routes, ok := routesRaw.(map[string]interface{}); ok {
66			for domain, image := range routes {
67				if imageStr, ok := image.(string); ok {
68					cfg.Routes[domain] = imageStr
69				}
70			}
71		}
72	}
73
74	// Validate required fields
75	if cfg.Server.SSLEmail == "" {
76		return nil, fmt.Errorf("server.ssl_email is required")
77	}
78
79	validRuntimes := []string{"auto", "docker", "podman", "podman-rootless"}
80	isValid := false
81	for _, valid := range validRuntimes {
82		if cfg.Server.Runtime == valid {
83			isValid = true
84			break
85		}
86	}
87	if !isValid {
88		return nil, fmt.Errorf("server.runtime must be one of: %s", strings.Join(validRuntimes, ", "))
89	}
90
91	// Validate registry auth config
92	if cfg.RegistryAuth.Enabled {
93		if cfg.RegistryAuth.Username == "" || cfg.RegistryAuth.Password == "" {
94			return nil, fmt.Errorf("Registry auth enabled but username/password not provided")
95		}
96	}
97
98	// Validate registry domain if provided
99	if cfg.Server.RegistryDomain != "" {
100		// Basic domain validation - should not contain protocol or paths
101		if strings.Contains(cfg.Server.RegistryDomain, "://") || strings.Contains(cfg.Server.RegistryDomain, "/") {
102			return nil, fmt.Errorf("registry_domain should be just the domain name (e.g. 'registry.example.com')")
103		}
104	}
105
106	return &cfg, nil
107}
108
109func (c *Config) GetRoutes() []Route {
110	var routes []Route
111	
112	for domain, image := range c.Routes {
113		route := Route{
114			Image: image,
115			HTTPS: true, // Default to HTTPS
116		}
117		
118		// Check if domain has http:// prefix
119		if strings.HasPrefix(domain, "http://") {
120			route.Domain = strings.TrimPrefix(domain, "http://")
121			route.HTTPS = false
122		} else {
123			route.Domain = domain
124		}
125		
126		routes = append(routes, route)
127	}
128	
129	return routes
130}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close