feat: add bootstrap package to centralize app startup logic

This commit is contained in:
2026-05-01 12:56:26 +07:00
parent e863bc7f10
commit a0ec37e4ed
+96
View File
@@ -1 +1,97 @@
package bootstrap
import (
"context"
"errors"
"fmt"
"log"
"os"
"os/signal"
"syscall"
"git.fossy.my.id/bagas/tunnel-please-controller/internal/config"
"git.fossy.my.id/bagas/tunnel-please-controller/internal/db/sqlc/repository"
"git.fossy.my.id/bagas/tunnel-please-controller/internal/server"
"github.com/jackc/pgx/v5"
"github.com/lestrrat-go/httprc/v3"
"github.com/lestrrat-go/jwx/v3/jwk"
)
type bootstrap struct {
config config.Config
errChan chan error
signalChan chan os.Signal
}
type Bootstrap interface {
Run() error
}
func New(config config.Config) Bootstrap {
errChan := make(chan error, 5)
signalChan := make(chan os.Signal, 1)
return &bootstrap{
config: config,
errChan: errChan,
signalChan: signalChan,
}
}
func (b *bootstrap) Run() error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
signal.Notify(b.signalChan, os.Interrupt, syscall.SIGTERM)
dbURL := b.config.DatabaseURL()
repo, err := startDatabase(ctx, dbURL)
if err != nil {
return err
}
client := httprc.NewClient()
jwkCache, err := jwk.NewCache(ctx, client)
if err != nil {
return err
}
s := server.New(repo, jwkCache, b.config)
b.errChan = make(chan error, 2)
go func() {
if err = s.StartAPI(ctx, b.config.ApiAddr()); err != nil && !errors.Is(err, context.Canceled) {
b.errChan <- err
}
}()
go func() {
if err = s.StartController(ctx, b.config.ControllerAddr()); err != nil && !errors.Is(err, context.Canceled) {
b.errChan <- err
}
}()
select {
case err = <-b.errChan:
return fmt.Errorf("service error: %w", err)
case sig := <-b.signalChan:
log.Printf("Received signal %s, initiating graceful shutdown", sig)
cancel()
return nil
}
}
func startDatabase(ctx context.Context, dbURL string) (*repository.Queries, error) {
connect, err := pgx.Connect(ctx, dbURL)
if err != nil {
return nil, err
}
defer func(connect *pgx.Conn, ctx context.Context) {
err = connect.Close(ctx)
if err != nil {
panic(err)
}
}(connect, ctx)
return repository.New(connect), nil
}