refactor: get env from config and delegate startup to bootstrap

This commit is contained in:
2026-05-01 12:58:40 +07:00
parent 8bb6ce7dc9
commit 55b9ca427c
2 changed files with 17 additions and 94 deletions
+10 -8
View File
@@ -12,8 +12,8 @@ import (
"sync" "sync"
"time" "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/config"
"git.fossy.my.id/bagas/tunnel-please-controller/internal/db/sqlc/repository"
proto "git.fossy.my.id/bagas/tunnel-please-grpc/gen" proto "git.fossy.my.id/bagas/tunnel-please-grpc/gen"
"github.com/lestrrat-go/jwx/v3/jwk" "github.com/lestrrat-go/jwx/v3/jwk"
"github.com/lestrrat-go/jwx/v3/jwt" "github.com/lestrrat-go/jwx/v3/jwt"
@@ -42,19 +42,19 @@ type Server struct {
Database *repository.Queries Database *repository.Queries
Subscribers map[string]*Subscriber Subscribers map[string]*Subscriber
mu *sync.RWMutex mu *sync.RWMutex
authToken string
jwkCache *jwk.Cache jwkCache *jwk.Cache
config config.Config
proto.UnimplementedEventServiceServer proto.UnimplementedEventServiceServer
proto.UnimplementedUserServiceServer 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{ return &Server{
Database: database, Database: database,
Subscribers: make(map[string]*Subscriber), Subscribers: make(map[string]*Subscriber),
mu: new(sync.RWMutex), mu: new(sync.RWMutex),
authToken: authToken,
jwkCache: jwkCache, 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") return status.Error(codes.InvalidArgument, "missing identity")
} }
token := payload.AuthEvent.AuthToken token := payload.AuthEvent.AuthToken
if token != s.authToken { if token != s.config.AuthToken() {
return status.Error(codes.Unauthenticated, "invalid auth token") 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, WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second, IdleTimeout: 60 * time.Second,
} }
jwkURL := config.Getenv("JWKS_URL", "") jwkURL := s.config.JwksURL()
if jwkURL != "" { if jwkURL != "" {
registerCtx, cancel := context.WithTimeout(ctx, jwkRegisterTimeout) registerCtx, cancel := context.WithTimeout(ctx, jwkRegisterTimeout)
defer cancel() defer cancel()
@@ -629,6 +629,7 @@ func (s *Server) StartAPI(ctx context.Context, Addr string) error {
errCh := make(chan error, 1) errCh := make(chan error, 1)
go func() { go func() {
log.Printf("Listening api on %s", httpServer.Addr)
errCh <- httpServer.ListenAndServe() errCh <- httpServer.ListenAndServe()
}() }()
@@ -697,18 +698,19 @@ func (s *Server) StartController(ctx context.Context, Addr string) error {
serveErr := make(chan error, 1) serveErr := make(chan error, 1)
go func() { go func() {
log.Printf("Listening controller on %s", s.config.ControllerAddr())
serveErr <- grpcServer.Serve(listener) serveErr <- grpcServer.Serve(listener)
}() }()
select { select {
case <-ctx.Done(): case <-ctx.Done():
grpcServer.GracefulStop() grpcServer.GracefulStop()
err := <-serveErr err = <-serveErr
if err != nil { if err != nil {
return err return err
} }
return ctx.Err() return ctx.Err()
case err := <-serveErr: case err = <-serveErr:
return err return err
} }
} }
+7 -86
View File
@@ -1,104 +1,25 @@
package main package main
import ( import (
"context"
"crypto/rand"
"encoding/base64"
"errors"
"log" "log"
"os" "os"
"os/signal"
"syscall"
"git.fossy.my.id/bagas/tunnel-please-controller/db/sqlc/repository" "git.fossy.my.id/bagas/tunnel-please-controller/internal/bootstrap"
"git.fossy.my.id/bagas/tunnel-please-controller/server" "git.fossy.my.id/bagas/tunnel-please-controller/internal/config"
"github.com/jackc/pgx/v5"
"github.com/joho/godotenv"
"github.com/lestrrat-go/httprc/v3"
"github.com/lestrrat-go/jwx/v3/jwk"
) )
func main() { 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.SetOutput(os.Stdout)
log.SetFlags(log.LstdFlags | log.Lshortfile) log.SetFlags(log.LstdFlags | log.Lshortfile)
dbURL := os.Getenv("DATABASE_URL") conf, err := config.MustLoad()
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)
if err != nil { 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) boot := bootstrap.New(conf)
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)
log.Printf("Listening controller on %s", controllerAddr) if err = boot.Run(); err != nil {
log.Printf("Listening api on %s", apiAddr) log.Fatalf("Application error: %v", err)
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)
} }
} }
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)
}