- Docker Hub / GHCR / GitHub reverse proxy - Web admin panel with link management - Dynamic admin path, user and password config - Rate limiting per link (dual/single auth mode) - Docker and docker-compose deployment support - GitHub Actions workflow for auto-publish to GHCR
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ListenAddr string `json:"listen_addr"`
|
||||
AdminPath string `json:"admin_path"`
|
||||
AdminUser string `json:"admin_user"`
|
||||
AdminPass string `json:"admin_pass"`
|
||||
DockerHubHost string `json:"docker_hub_host"`
|
||||
GHCRCacheEnabled bool `json:"ghcr_cache_enabled"`
|
||||
CacheDir string `json:"cache_dir"`
|
||||
MaxCacheSize int64 `json:"max_cache_size"`
|
||||
Links map[string]*Link `json:"links"`
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
type Link struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Token string `json:"token"`
|
||||
Type string `json:"type"` // docker, ghcr, github
|
||||
AuthMode string `json:"auth_mode"` // single, dual
|
||||
Enabled bool `json:"enabled"`
|
||||
RateLimit int `json:"rate_limit"` // requests per minute
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
AccessCount int64 `json:"access_count"`
|
||||
LastAccess int64 `json:"last_access"`
|
||||
}
|
||||
|
||||
var (
|
||||
cfg *Config
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
func Load(path string) (*Config, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
cfg = defaultConfig()
|
||||
return cfg, cfg.Save(path)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
cfg = defaultConfig()
|
||||
if err := json.Unmarshal(data, cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cfg.Links == nil {
|
||||
cfg.Links = make(map[string]*Link)
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func defaultConfig() *Config {
|
||||
return &Config{
|
||||
ListenAddr: ":8080",
|
||||
AdminPath: "/admin",
|
||||
AdminUser: "admin",
|
||||
AdminPass: "admin123",
|
||||
DockerHubHost: "registry-1.docker.io",
|
||||
GHCRCacheEnabled: true,
|
||||
CacheDir: "./cache",
|
||||
MaxCacheSize: 10 * 1024 * 1024 * 1024, // 10GB
|
||||
Links: make(map[string]*Link),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) Save(path string) error {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
data, err := json.MarshalIndent(c, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path, data, 0644)
|
||||
}
|
||||
|
||||
func (c *Config) GetLink(id string) (*Link, bool) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
link, ok := c.Links[id]
|
||||
return link, ok
|
||||
}
|
||||
|
||||
func (c *Config) GetLinkByToken(token string) (*Link, bool) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
for _, link := range c.Links {
|
||||
if link.Token == token {
|
||||
return link, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (c *Config) SetLink(link *Link) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.Links[link.ID] = link
|
||||
}
|
||||
|
||||
func (c *Config) DeleteLink(id string) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
delete(c.Links, id)
|
||||
}
|
||||
|
||||
func (c *Config) ListLinks() []*Link {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
links := make([]*Link, 0, len(c.Links))
|
||||
for _, l := range c.Links {
|
||||
links = append(links, l)
|
||||
}
|
||||
return links
|
||||
}
|
||||
|
||||
func Get() *Config {
|
||||
return cfg
|
||||
}
|
||||
Reference in New Issue
Block a user