package serve import ( "context" "database/sql" "errors" "log/slog" "net/http" "os" "time" "connectrpc.com/connect" "connectrpc.com/validate" sqldblogger "github.com/simukti/sqldb-logger" "github.com/spf13/cobra" "github.com/stephenafamo/bob" "github.com/tigorlazuardi/bluemage/go/api" "github.com/tigorlazuardi/bluemage/go/gen/proto/device/v1/v1connect" "github.com/tigorlazuardi/bluemage/go/pkg/errs" "github.com/tigorlazuardi/bluemage/go/pkg/log" "github.com/tigorlazuardi/bluemage/go/server" "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" ) var Cmd = &cobra.Command{ Use: "serve", RunE: func(cmd *cobra.Command, args []string) error { sqldb, err := sql.Open("sqlite3", "file:data.db") if err != nil { return errs.Wrapw(err, "failed to open database", "file", "data.db") } sqldb = sqldblogger.OpenDriver( "file:data.db", sqldb.Driver(), log.SQLLogger{}, sqldblogger.WithSQLQueryAsMessage(true), ) db := bob.New(sqldb) logOutput := log.WrapOsFile(os.Stderr) prettyHandler := log.NewPrettyHandler(logOutput, nil) slog.SetDefault(slog.New(prettyHandler)) api := &api.API{ DB: db, } handler := &server.Server{ DeviceHandler: server.DeviceHandler{ API: api, }, } validationInterceptor, err := validate.NewInterceptor() if err != nil { return errs.Wrap(err, "failed to create validation interceptor") } mux := http.NewServeMux() mux.Handle(v1connect.NewDeviceServiceHandler(handler, connect.WithInterceptors( validationInterceptor, server.LogInterceptor(), ))) server := &http.Server{ Addr: ":8080", Handler: h2c.NewHandler(server.WithCORS(mux), &http2.Server{}), } go func() { <-cmd.Context().Done() slog.Info("Exit signal received. Shutting down server") shutdownCtx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() _ = server.Shutdown(shutdownCtx) }() slog.Info("ConnectRPC server started", "addr", server.Addr) err = server.ListenAndServe() if err != nil && !errors.Is(err, http.ErrServerClosed) { return errs.Wrap(err, "failed to serve") } slog.Info("ConnectRPC server stopped") return errors.Join(sqldb.Close(), prettyHandler.Flush()) }, SilenceUsage: true, }