larc r2

172 lines ยท 3.9 KB Raw
1 package server
2
3 import (
4 "fmt"
5 "os"
6
7 "gopkg.in/yaml.v3"
8 )
9
10 /* Server configuration loaded from YAML.
11 * Supports TLS, Basic Auth via htpasswd, and per-repo access control. */
12
13 // Config is the server configuration
14 type Config struct {
15 Server ServerConfig `yaml:"server"`
16 Auth AuthConfig `yaml:"auth"`
17 Storage StorageConfig `yaml:"storage"`
18 Logging LoggingConfig `yaml:"logging"`
19 Repos []RepoConfig `yaml:"repositories"`
20 }
21
22 // ServerConfig contains HTTP server settings
23 type ServerConfig struct {
24 Host string `yaml:"host"`
25 Port int `yaml:"port"`
26 BaseURL string `yaml:"base_url"`
27 TLS TLSConfig `yaml:"tls"`
28 }
29
30 // TLSConfig contains TLS settings
31 type TLSConfig struct {
32 Enabled bool `yaml:"enabled"`
33 Cert string `yaml:"cert"`
34 Key string `yaml:"key"`
35 }
36
37 // AuthConfig contains authentication settings
38 type AuthConfig struct {
39 Enabled bool `yaml:"enabled"`
40 HtpasswdFile string `yaml:"htpasswd_file"`
41 Realm string `yaml:"realm"`
42 }
43
44 // StorageConfig contains storage settings
45 type StorageConfig struct {
46 Root string `yaml:"root"`
47 MaxRepoSize string `yaml:"max_repo_size"`
48 MaxFileSize string `yaml:"max_file_size"`
49 }
50
51 // LoggingConfig contains logging settings
52 type LoggingConfig struct {
53 Level string `yaml:"level"` // debug, info, warn, error
54 Format string `yaml:"format"` // json, text
55 File string `yaml:"file"` // log file path (empty for stdout)
56 }
57
58 // RepoConfig is per-repository configuration
59 type RepoConfig struct {
60 Name string `yaml:"name"`
61 Path string `yaml:"path"` // relative to storage.root
62 Public bool `yaml:"public"` // allow anonymous read
63 AllowedUsers []string `yaml:"allowed_users"` // full access
64 ReadOnlyUsers []string `yaml:"read_only_users"` // read-only access
65 }
66
67 // LoadConfig loads configuration from YAML file
68 func LoadConfig(path string) (*Config, error) {
69 data, err := os.ReadFile(path)
70 if err != nil {
71 return nil, fmt.Errorf("read config: %w", err)
72 }
73
74 cfg := &Config{}
75 if err := yaml.Unmarshal(data, cfg); err != nil {
76 return nil, fmt.Errorf("parse config: %w", err)
77 }
78
79 /* apply defaults */
80 if cfg.Server.Host == "" {
81 cfg.Server.Host = "0.0.0.0"
82 }
83 if cfg.Server.Port == 0 {
84 cfg.Server.Port = 8080
85 }
86 if cfg.Auth.Realm == "" {
87 cfg.Auth.Realm = "Larc Repository"
88 }
89 if cfg.Logging.Level == "" {
90 cfg.Logging.Level = "info"
91 }
92 if cfg.Logging.Format == "" {
93 cfg.Logging.Format = "json"
94 }
95
96 return cfg, nil
97 }
98
99 // DefaultConfig returns default configuration
100 func DefaultConfig() *Config {
101 return &Config{
102 Server: ServerConfig{
103 Host: "0.0.0.0",
104 Port: 8080,
105 },
106 Auth: AuthConfig{
107 Enabled: false,
108 Realm: "Larc Repository",
109 },
110 Storage: StorageConfig{
111 Root: "./repos",
112 MaxRepoSize: "10GB",
113 MaxFileSize: "100MB",
114 },
115 Logging: LoggingConfig{
116 Level: "info",
117 Format: "json",
118 },
119 }
120 }
121
122 // Validate validates the configuration
123 func (c *Config) Validate() error {
124 if c.Auth.Enabled && c.Auth.HtpasswdFile == "" {
125 return fmt.Errorf("auth enabled but htpasswd_file not set")
126 }
127
128 if c.Server.TLS.Enabled {
129 if c.Server.TLS.Cert == "" || c.Server.TLS.Key == "" {
130 return fmt.Errorf("TLS enabled but cert or key not set")
131 }
132 }
133
134 return nil
135 }
136
137 // GetRepoConfig returns config for a specific repository
138 func (c *Config) GetRepoConfig(name string) *RepoConfig {
139 for i := range c.Repos {
140 if c.Repos[i].Name == name {
141 return &c.Repos[i]
142 }
143 }
144 return nil
145 }
146
147 // IsUserAllowed checks if user has access to repository
148 func (r *RepoConfig) IsUserAllowed(username string, writeAccess bool) bool {
149 /* public repos allow anonymous read */
150 if r.Public && !writeAccess && username == "" {
151 return true
152 }
153
154 /* check allowed users (full access) */
155 for _, u := range r.AllowedUsers {
156 if u == username {
157 return true
158 }
159 }
160
161 /* check read-only users */
162 if !writeAccess {
163 for _, u := range r.ReadOnlyUsers {
164 if u == username {
165 return true
166 }
167 }
168 }
169
170 return false
171 }
172