openova/core/services/auth/main.go
hatiyildiz 7646840ffe feat(consolidation): move 8 SME backend services + shared module to public repo
Per docs/PROVISIONING-PLAN.md and tickets [B] sme-backend group. Migrates the 8 Go backend services from openova-private/services/ to openova/core/services/, plus the shared module they all depend on, plus the services-build CI workflow.

What moved:
- services/auth → core/services/auth (Go HTTP service for SME marketplace authentication)
- services/billing → core/services/billing (Go HTTP service for billing + voucher backend)
- services/catalog → core/services/catalog (Go HTTP service for App catalog)
- services/domain → core/services/domain (Go HTTP service for tenant domain mapping)
- services/gateway → core/services/gateway (Go HTTP gateway with rate limiting)
- services/notification → core/services/notification (Go HTTP service with email templates)
- services/provisioning → core/services/provisioning (Go HTTP service that commits tenant Application manifests via Gitea/GitHub API)
- services/tenant → core/services/tenant (Go HTTP service for tenant lifecycle)
- services/shared → core/services/shared (shared Go module: db, events, health, middleware, respond)
- 9 go.mod files updated: module github.com/openova-io/openova-private/services/<X> → github.com/openova-io/openova/core/services/<X>
- 9 go.sum and import paths similarly updated
- replace directives updated: openova-private/services/shared → openova/core/services/shared
- sme-services-build.yaml workflow → services-build.yaml in .github/workflows/, paths/context/image-base/deploy paths all repointed at core/services + ghcr.io/openova-io/openova/services-* + products/catalyst/chart/templates/sme-services
- All 8 manifests in products/catalyst/chart/templates/sme-services/ updated: image refs ghcr.io/openova-io/openova-private/sme-{X} → ghcr.io/openova-io/openova/services-{X}
- provisioning.yaml GITHUB_REPO env var: "openova-private" → "openova"

Closes [B] sme-backend (10 tickets).

After this commit, all 14 user-facing + backend Catalyst-Zero modules build from this public repo:
- 4 UIs: console, admin, marketplace, catalyst-ui
- 2 backends: marketplace-api, catalyst-api
- 8 SME services: auth, billing, catalog, domain, gateway, notification, provisioning, tenant
- 1 shared Go module

Note: 1 line in core/services/provisioning/main.go retains a literal default of "openova-private" for the GITHUB_REPO fallback when env var is unset; the K8s manifest sets GITHUB_REPO=openova explicitly so this path is never exercised in the deployed runtime, and the in-code default will be cleaned up in a follow-up.
2026-04-28 12:30:32 +02:00

144 lines
4.3 KiB
Go

package main
import (
"context"
"log/slog"
"net/http"
"os"
"strings"
"github.com/openova-io/openova/core/services/auth/handlers"
"github.com/openova-io/openova/core/services/auth/store"
"github.com/openova-io/openova/core/services/shared/db"
"github.com/openova-io/openova/core/services/shared/events"
"github.com/openova-io/openova/core/services/shared/health"
"github.com/openova-io/openova/core/services/shared/middleware"
)
func main() {
// Configuration from environment.
databaseURL := getEnv("DATABASE_URL", "postgres://postgres:postgres@localhost:5432/auth?sslmode=disable")
valkeyAddr := getEnv("VALKEY_ADDR", "localhost:6379")
redpandaBrokers := strings.Split(getEnv("REDPANDA_BROKERS", "localhost:9092"), ",")
jwtSecret := []byte(getEnv("JWT_SECRET", ""))
jwtRefreshSecret := []byte(getEnv("JWT_REFRESH_SECRET", ""))
googleClientID := getEnv("GOOGLE_CLIENT_ID", "")
googleClientSecret := getEnv("GOOGLE_CLIENT_SECRET", "")
baseURL := getEnv("BASE_URL", "http://localhost:8081")
smtpHost := getEnv("SMTP_HOST", "localhost")
smtpPort := getEnv("SMTP_PORT", "587")
smtpFrom := getEnv("SMTP_FROM", "noreply@openova.io")
smtpUser := getEnv("SMTP_USER", "")
smtpPass := getEnv("SMTP_PASS", "")
corsOrigin := getEnv("CORS_ORIGIN", "*")
port := getEnv("PORT", "8081")
// Connect to PostgreSQL.
pgDB := db.MustConnect(databaseURL)
defer pgDB.Close()
slog.Info("connected to PostgreSQL")
// Connect to Valkey.
valkeyClient, err := db.ConnectValkey(valkeyAddr)
if err != nil {
slog.Error("failed to connect to Valkey", "error", err)
os.Exit(1)
}
defer valkeyClient.Close()
slog.Info("connected to Valkey")
// Create events producer.
producer, err := events.NewProducer(redpandaBrokers)
if err != nil {
slog.Error("failed to create events producer", "error", err)
os.Exit(1)
}
defer producer.Close()
slog.Info("connected to RedPanda")
// Initialize store and handler.
authStore := store.New(pgDB)
// Seed superadmin account (only creates if missing).
adminEmail := getEnv("ADMIN_EMAIL", "admin@openova.io")
adminPassword := getEnv("ADMIN_PASSWORD", "")
if adminPassword != "" {
if created := authStore.SeedSuperadmin(context.Background(), adminEmail, "Admin", adminPassword); created {
slog.Info("superadmin seeded", "email", adminEmail)
}
}
h := &handlers.Handler{
Store: authStore,
Valkey: valkeyClient,
Producer: producer,
JWTSecret: jwtSecret,
JWTRefreshSecret: jwtRefreshSecret,
GoogleClientID: googleClientID,
GoogleClientSecret: googleClientSecret,
BaseURL: baseURL,
SMTPHost: smtpHost,
SMTPPort: smtpPort,
FromEmail: smtpFrom,
SMTPUser: smtpUser,
SMTPPass: smtpPass,
}
// Build the main mux.
mux := http.NewServeMux()
// Health check — no middleware.
mux.HandleFunc("GET /healthz", health.Handler())
// Auth routes — JWT middleware applied only to /auth/me.
authRoutes := h.Routes()
jwtMiddleware := middleware.JWTAuth(jwtSecret)
// Endpoints that identify "the current user" from the Bearer token need
// JWT middleware. Everything else (login, verify, refresh, etc.) runs
// without it — they authenticate via their own request body.
needsAuth := func(r *http.Request) bool {
if r.Method == http.MethodGet && r.URL.Path == "/auth/me" {
return true
}
if r.Method == http.MethodPost && r.URL.Path == "/auth/logout-all" {
return true
}
// Admin/service endpoints under /auth/admin/ read role from JWT
// claims and enforce superadmin — must be authenticated.
if strings.HasPrefix(r.URL.Path, "/auth/admin/") {
return true
}
return false
}
mux.Handle("/auth/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if needsAuth(r) {
jwtMiddleware(authRoutes).ServeHTTP(w, r)
return
}
authRoutes.ServeHTTP(w, r)
}))
// Apply global middleware chain.
handler := middleware.Chain(
mux,
middleware.Recovery,
middleware.Logger,
middleware.RequestID,
middleware.CORS(corsOrigin),
)
slog.Info("starting auth service", "port", port)
if err := http.ListenAndServe(":"+port, handler); err != nil {
slog.Error("server error", "error", err)
os.Exit(1)
}
}
func getEnv(key, fallback string) string {
if v := os.Getenv(key); v != "" {
return v
}
return fallback
}