Overhaul: real metrics, Git integration UI, open-source hygiene, root cleanup

Backend:
- GET /services/:id/metrics with real Docker container stats
- DELETE /git/providers/:id and GET repo branches endpoint

Frontend:
- Service detail shows real telemetry or honest empty states
- Live status distinguishes idle/polling/reconnecting
- Git providers section in Settings; repo+branch pickers in Add Service
- Real logo, compact Projects header, canvas fitView fix
- index.css deduplicated (1725 -> 1384 lines)
- Fixed smoke tests, lint errors, ToastProvider/context unification

Repo hygiene:
- Added LICENSE (MIT), CONTRIBUTING.md, SECURITY.md; rewrote README
- Untracked .env.prod and data/letsencrypt secrets; fixed .gitignore
- Removed legacy root src/internal/cmd and stale configs/docs/artifacts
- Moved guides and design references under docs/

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-15 18:59:40 +02:00
1 parent 355a97bab4
commit 9f3b196032
499 files changed
+4471 -94235

No files matched your search

+54
View File
@@ -146,6 +146,9 @@ func Load() *Config {
cfg.CORSMethods = getenvSlice("CORS_METHODS", []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"})
cfg.CORSHeaders = getenvSlice("CORS_HEADERS", []string{"Origin", "Content-Type", "Accept", "Authorization"})
cfg.CORSCredentials = getenvBool("CORS_CREDENTIALS", true)
if cfg.IsDevelopment() {
cfg.CORSOrigins = expandDevelopmentCORSOrigins(cfg.CORSOrigins)
}
cfg.APIKeyHeader = getenv("API_KEY_HEADER", "X-API-Key")
cfg.ServiceTokenHeader = getenv("SERVICE_TOKEN_HEADER", "X-Service-Token")
@@ -331,6 +334,57 @@ func getenvSliceWithAliases(keys []string, defaultValue []string) []string {
return defaultValue
}
func expandDevelopmentCORSOrigins(origins []string) []string {
hasLocalOrigin := false
seen := make(map[string]struct{}, len(origins))
expanded := make([]string, 0, len(origins)+12)
for _, origin := range origins {
trimmed := strings.TrimSpace(origin)
if trimmed == "" {
continue
}
if _, ok := seen[trimmed]; ok {
continue
}
seen[trimmed] = struct{}{}
expanded = append(expanded, trimmed)
if strings.HasPrefix(trimmed, "http://localhost") ||
strings.HasPrefix(trimmed, "http://127.0.0.1") ||
strings.HasPrefix(trimmed, "http://0.0.0.0") {
hasLocalOrigin = true
}
}
if !hasLocalOrigin {
return expanded
}
for _, origin := range []string{
"http://localhost:3000",
"http://localhost:5173",
"http://localhost:5174",
"http://localhost:5175",
"http://127.0.0.1:3000",
"http://127.0.0.1:5173",
"http://127.0.0.1:5174",
"http://127.0.0.1:5175",
"http://0.0.0.0:3000",
"http://0.0.0.0:5173",
"http://0.0.0.0:5174",
"http://0.0.0.0:5175",
} {
if _, ok := seen[origin]; ok {
continue
}
seen[origin] = struct{}{}
expanded = append(expanded, origin)
}
return expanded
}
// encodeDatabasePassword URL-encodes the password in a database connection string
func encodeDatabasePassword(databaseURL string) string {
// Find the password part between :@ and encode it
+32 -3
View File
@@ -12,9 +12,14 @@ func TestLoadUsesCORSAllowedOriginsAlias(t *testing.T) {
t.Setenv("CORS_ALLOWED_ORIGINS", "http://localhost:3000,https://app.example.com")
cfg := Load()
want := []string{"http://localhost:3000", "https://app.example.com"}
if !reflect.DeepEqual(cfg.CORSOrigins, want) {
t.Fatalf("unexpected CORS origins: got %v want %v", cfg.CORSOrigins, want)
if !containsAll(cfg.CORSOrigins, []string{
"http://localhost:3000",
"http://localhost:5173",
"http://localhost:5174",
"http://127.0.0.1:5174",
"https://app.example.com",
}) {
t.Fatalf("unexpected CORS origins: got %v", cfg.CORSOrigins)
}
}
@@ -180,3 +185,27 @@ func TestLoadUsesNewStartupDefaults(t *testing.T) {
t.Fatalf("expected positive MAX_REQUEST_BODY_BYTES, got %d", cfg.MaxRequestBody)
}
}
func TestLoadDoesNotExpandNonLocalDevelopmentCORSOrigins(t *testing.T) {
t.Setenv("ENVIRONMENT", "development")
t.Setenv("CORS_ORIGINS", "https://preferred.example.com")
cfg := Load()
want := []string{"https://preferred.example.com"}
if !reflect.DeepEqual(cfg.CORSOrigins, want) {
t.Fatalf("unexpected CORS origins: got %v want %v", cfg.CORSOrigins, want)
}
}
func containsAll(haystack []string, needles []string) bool {
seen := make(map[string]struct{}, len(haystack))
for _, item := range haystack {
seen[item] = struct{}{}
}
for _, needle := range needles {
if _, ok := seen[needle]; !ok {
return false
}
}
return true
}