package inboxserver import ( "context" "net/http" "path/filepath" "sync" "inbox/internal/app/leaderloop" "inbox/internal/app/runtimeconfig" "inbox/internal/app/workspaceprovision" "inbox/internal/app/workspaceruntime" "inbox/internal/base/timeutil" httpapi "inbox/internal/httpapi" "inbox/internal/inboxapp" sqlitestore "inbox/internal/store/sqlite" ) type App struct { store *sqlitestore.Store handler http.Handler cancel context.CancelFunc wg sync.WaitGroup } func Open(projectRoot, workspacesDir string, serverPort int, clock timeutil.Clock) (*App, error) { store, err := sqlitestore.Open(filepath.Join(projectRoot, ".runtime", "inbox.db"), clock) if err != nil { return nil, err } runtime := workspaceruntime.NewService(store, projectRoot, workspacesDir, serverPort, clock, nil) provision := workspaceprovision.NewService(store, workspacesDir, runtime) handler := httpapi.NewHandlerWithServices(inboxapp.BuildServices(store, clock, inboxapp.Options{ WorkspaceProvision: provision, WorkspaceRuntime: runtime, })) ctx, cancel := context.WithCancel(context.Background()) leader := leaderloop.NewService(store, runtimeconfig.NewService(store, store, clock), runtime, nil, clock, projectRoot) app := &App{ store: store, handler: handler, cancel: cancel, } app.wg.Add(1) go func() { defer app.wg.Done() _ = leader.Run(ctx) }() return app, nil } func (a *App) Handler() http.Handler { if a == nil { return nil } return a.handler } func (a *App) Close() error { if a == nil { return nil } if a.cancel != nil { a.cancel() } a.wg.Wait() if a.store == nil { return nil } return a.store.Close() }