mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-09-25 03:34:14 +00:00
add new upcloud cli deploy
This commit is contained in:
@@ -57,8 +57,8 @@ type ServerConfig struct {
|
||||
// Short name used in page titles and browser tabs.
|
||||
ClientShortName string `yaml:"client_short_name" comment:"Short name used in page titles and browser tabs."`
|
||||
|
||||
// Separate domain for OCI registry API.
|
||||
RegistryDomain string `yaml:"registry_domain" comment:"Separate domain for OCI registry API (e.g. \"buoy.cr\"). Browser visits redirect to BaseURL."`
|
||||
// Separate domains for OCI registry API. First entry is the primary (used for JWT service name and UI display).
|
||||
RegistryDomains []string `yaml:"registry_domains" comment:"Separate domains for OCI registry API (e.g. [\"buoy.cr\"]). First is primary. Browser visits redirect to BaseURL."`
|
||||
}
|
||||
|
||||
// UIConfig defines web UI settings
|
||||
@@ -145,7 +145,7 @@ func setDefaults(v *viper.Viper) {
|
||||
v.SetDefault("server.client_name", "AT Container Registry")
|
||||
v.SetDefault("server.client_short_name", "ATCR")
|
||||
v.SetDefault("server.oauth_key_path", "/var/lib/atcr/oauth/client.key")
|
||||
v.SetDefault("server.registry_domain", "")
|
||||
v.SetDefault("server.registry_domains", []string{})
|
||||
|
||||
// UI defaults
|
||||
v.SetDefault("ui.database_path", "/var/lib/atcr/ui.db")
|
||||
@@ -241,8 +241,8 @@ func LoadConfig(yamlPath string) (*Config, error) {
|
||||
|
||||
// deriveServiceName extracts the JWT service name from the config.
|
||||
func deriveServiceName(cfg *Config) string {
|
||||
if cfg.Server.RegistryDomain != "" {
|
||||
return cfg.Server.RegistryDomain
|
||||
if len(cfg.Server.RegistryDomains) > 0 {
|
||||
return cfg.Server.RegistryDomains[0]
|
||||
}
|
||||
return getServiceName(cfg.Server.BaseURL)
|
||||
}
|
||||
|
||||
@@ -87,6 +87,8 @@ func setupHoldTestDB(t *testing.T) *sql.DB {
|
||||
}
|
||||
// Limit to single connection to avoid race conditions in tests
|
||||
db.SetMaxOpenConns(1)
|
||||
// Clean slate: shared-cache in-memory DB may retain data from prior subtests
|
||||
db.Exec("DELETE FROM hold_captain_records")
|
||||
t.Cleanup(func() { db.Close() })
|
||||
return db
|
||||
}
|
||||
|
||||
+25
-9
@@ -240,10 +240,10 @@ func NewAppViewServer(cfg *Config, branding *BrandingOverrides) (*AppViewServer,
|
||||
mainRouter.Use(routes.CORSMiddleware())
|
||||
|
||||
// Registry domain redirect middleware
|
||||
if cfg.Server.RegistryDomain != "" {
|
||||
mainRouter.Use(RegistryDomainRedirect(cfg.Server.RegistryDomain, cfg.Server.BaseURL))
|
||||
if len(cfg.Server.RegistryDomains) > 0 {
|
||||
mainRouter.Use(RegistryDomainRedirect(cfg.Server.RegistryDomains, cfg.Server.BaseURL))
|
||||
slog.Info("Registry domain redirect enabled",
|
||||
"registry_domain", cfg.Server.RegistryDomain,
|
||||
"registry_domains", cfg.Server.RegistryDomains,
|
||||
"ui_base_url", cfg.Server.BaseURL)
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ func NewAppViewServer(cfg *Config, branding *BrandingOverrides) (*AppViewServer,
|
||||
OAuthStore: s.OAuthStore,
|
||||
Refresher: s.Refresher,
|
||||
BaseURL: baseURL,
|
||||
RegistryDomain: cfg.Server.RegistryDomain,
|
||||
RegistryDomain: primaryRegistryDomain(cfg.Server.RegistryDomains),
|
||||
DeviceStore: s.DeviceStore,
|
||||
HealthChecker: s.HealthChecker,
|
||||
ReadmeFetcher: s.ReadmeFetcher,
|
||||
@@ -499,7 +499,10 @@ func NewAppViewServer(cfg *Config, branding *BrandingOverrides) (*AppViewServer,
|
||||
mainRouter.Get("/health", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
|
||||
if err := json.NewEncoder(w).Encode(map[string]string{"status": "ok"}); err != nil {
|
||||
http.Error(w, "encode error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
// Register credential helper version API (public endpoint)
|
||||
@@ -577,10 +580,15 @@ func (s *AppViewServer) createTokenIssuer() (*token.Issuer, error) {
|
||||
)
|
||||
}
|
||||
|
||||
// RegistryDomainRedirect redirects all non-registry requests from the registry
|
||||
// domain to the UI domain. Only /v2 and /v2/* pass through for Docker clients.
|
||||
// RegistryDomainRedirect redirects all non-registry requests from registry
|
||||
// domains to the UI domain. Only /v2 and /v2/* pass through for Docker clients.
|
||||
// Uses 307 (Temporary Redirect) to preserve POST method/body.
|
||||
func RegistryDomainRedirect(registryDomain, uiBaseURL string) func(http.Handler) http.Handler {
|
||||
func RegistryDomainRedirect(registryDomains []string, uiBaseURL string) func(http.Handler) http.Handler {
|
||||
domains := make(map[string]bool, len(registryDomains))
|
||||
for _, d := range registryDomains {
|
||||
domains[d] = true
|
||||
}
|
||||
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
host := r.Host
|
||||
@@ -588,7 +596,7 @@ func RegistryDomainRedirect(registryDomain, uiBaseURL string) func(http.Handler)
|
||||
host = host[:idx]
|
||||
}
|
||||
|
||||
if host == registryDomain {
|
||||
if domains[host] {
|
||||
path := r.URL.Path
|
||||
if path == "/v2" || path == "/v2/" || strings.HasPrefix(path, "/v2/") {
|
||||
next.ServeHTTP(w, r)
|
||||
@@ -605,6 +613,14 @@ func RegistryDomainRedirect(registryDomain, uiBaseURL string) func(http.Handler)
|
||||
}
|
||||
}
|
||||
|
||||
// primaryRegistryDomain returns the first registry domain, or empty string if none.
|
||||
func primaryRegistryDomain(domains []string) string {
|
||||
if len(domains) > 0 {
|
||||
return domains[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// initializeJetstream initializes the Jetstream workers for real-time events and backfill.
|
||||
func (s *AppViewServer) initializeJetstream() {
|
||||
jetstreamURL := s.Config.Jetstream.URL
|
||||
|
||||
Reference in New Issue
Block a user