From 55b9ca427c9841c0484bbfb80841846a9c542854 Mon Sep 17 00:00:00 2001 From: bagas Date: Fri, 1 May 2026 12:58:40 +0700 Subject: [PATCH] refactor: get env from config and delegate startup to bootstrap --- internal/server/server.go | 18 ++++---- main.go | 93 +++------------------------------------ 2 files changed, 17 insertions(+), 94 deletions(-) diff --git a/internal/server/server.go b/internal/server/server.go index 4b6a0b4..52e0d32 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -12,8 +12,8 @@ import ( "sync" "time" - "git.fossy.my.id/bagas/tunnel-please-controller/db/sqlc/repository" "git.fossy.my.id/bagas/tunnel-please-controller/internal/config" + "git.fossy.my.id/bagas/tunnel-please-controller/internal/db/sqlc/repository" proto "git.fossy.my.id/bagas/tunnel-please-grpc/gen" "github.com/lestrrat-go/jwx/v3/jwk" "github.com/lestrrat-go/jwx/v3/jwt" @@ -42,19 +42,19 @@ type Server struct { Database *repository.Queries Subscribers map[string]*Subscriber mu *sync.RWMutex - authToken string jwkCache *jwk.Cache + config config.Config proto.UnimplementedEventServiceServer proto.UnimplementedUserServiceServer } -func New(database *repository.Queries, authToken string, jwkCache *jwk.Cache) *Server { +func New(database *repository.Queries, jwkCache *jwk.Cache, config config.Config) *Server { return &Server{ Database: database, Subscribers: make(map[string]*Subscriber), mu: new(sync.RWMutex), - authToken: authToken, jwkCache: jwkCache, + config: config, } } @@ -94,7 +94,7 @@ func (s *Server) Subscribe(event grpc.BidiStreamingServer[proto.Node, proto.Even return status.Error(codes.InvalidArgument, "missing identity") } token := payload.AuthEvent.AuthToken - if token != s.authToken { + if token != s.config.AuthToken() { return status.Error(codes.Unauthenticated, "invalid auth token") } @@ -332,7 +332,7 @@ func (s *Server) StartAPI(ctx context.Context, Addr string) error { WriteTimeout: 15 * time.Second, IdleTimeout: 60 * time.Second, } - jwkURL := config.Getenv("JWKS_URL", "") + jwkURL := s.config.JwksURL() if jwkURL != "" { registerCtx, cancel := context.WithTimeout(ctx, jwkRegisterTimeout) defer cancel() @@ -629,6 +629,7 @@ func (s *Server) StartAPI(ctx context.Context, Addr string) error { errCh := make(chan error, 1) go func() { + log.Printf("Listening api on %s", httpServer.Addr) errCh <- httpServer.ListenAndServe() }() @@ -697,18 +698,19 @@ func (s *Server) StartController(ctx context.Context, Addr string) error { serveErr := make(chan error, 1) go func() { + log.Printf("Listening controller on %s", s.config.ControllerAddr()) serveErr <- grpcServer.Serve(listener) }() select { case <-ctx.Done(): grpcServer.GracefulStop() - err := <-serveErr + err = <-serveErr if err != nil { return err } return ctx.Err() - case err := <-serveErr: + case err = <-serveErr: return err } } diff --git a/main.go b/main.go index b0275e7..dcadff3 100644 --- a/main.go +++ b/main.go @@ -1,104 +1,25 @@ package main import ( - "context" - "crypto/rand" - "encoding/base64" - "errors" "log" "os" - "os/signal" - "syscall" - "git.fossy.my.id/bagas/tunnel-please-controller/db/sqlc/repository" - "git.fossy.my.id/bagas/tunnel-please-controller/server" - "github.com/jackc/pgx/v5" - "github.com/joho/godotenv" - "github.com/lestrrat-go/httprc/v3" - "github.com/lestrrat-go/jwx/v3/jwk" + "git.fossy.my.id/bagas/tunnel-please-controller/internal/bootstrap" + "git.fossy.my.id/bagas/tunnel-please-controller/internal/config" ) func main() { - if _, err := os.Stat(".env"); err == nil { - if err = godotenv.Load(".env"); err != nil { - log.Printf("Warning: Failed to load .env file: %s", err) - } - } - - ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) - defer stop() - log.SetOutput(os.Stdout) log.SetFlags(log.LstdFlags | log.Lshortfile) - dbURL := os.Getenv("DATABASE_URL") - if dbURL == "" { - log.Fatal("DATABASE_URL is required") - } - - controllerAddr := getenv("CONTROLLER_ADDR", ":8080") - apiAddr := getenv("API_ADDR", ":8081") - authToken := getenv("AUTH_TOKEN", "") - if authToken == "" { - authToken = generateAuthToken() - log.Printf("No AUTH_TOKEN provided. Generated token: %s", authToken) - } - - connect, err := pgx.Connect(ctx, dbURL) + conf, err := config.MustLoad() if err != nil { - panic(err) + log.Fatalf("Config load error: %v", err) } - defer func(connect *pgx.Conn, ctx context.Context) { - err = connect.Close(ctx) - if err != nil { - panic(err) - } - }(connect, ctx) - repo := repository.New(connect) - client := httprc.NewClient() - jwkCache, err := jwk.NewCache(ctx, client) - if err != nil { - log.Printf("failed to initialize jwk cache : %s", err) - } - s := server.New(repo, authToken, jwkCache) + boot := bootstrap.New(conf) - log.Printf("Listening controller on %s", controllerAddr) - log.Printf("Listening api on %s", apiAddr) - - errCh := make(chan error, 2) - - go func() { - if err = s.StartAPI(ctx, apiAddr); err != nil && !errors.Is(err, context.Canceled) { - errCh <- err - } - }() - - go func() { - if err = s.StartController(ctx, controllerAddr); err != nil && !errors.Is(err, context.Canceled) { - errCh <- err - } - }() - - select { - case <-ctx.Done(): - log.Printf("shutting down: %v", ctx.Err()) - case err = <-errCh: - log.Fatalf("server error: %v", err) + if err = boot.Run(); err != nil { + log.Fatalf("Application error: %v", err) } } - -func getenv(key, def string) string { - if val := os.Getenv(key); val != "" { - return val - } - return def -} - -func generateAuthToken() string { - buf := make([]byte, 32) - if _, err := rand.Read(buf); err != nil { - panic(err) - } - return base64.StdEncoding.EncodeToString(buf) -}