481359ddb2
Co-authored-by: Kandev Agent <agent@kandev.dev> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
79 lines
2.3 KiB
Go
79 lines
2.3 KiB
Go
package backendapp
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/kandev/kandev/internal/common/logger"
|
|
"github.com/kandev/kandev/internal/db"
|
|
"github.com/kandev/kandev/internal/orgunit"
|
|
tasksqlite "github.com/kandev/kandev/internal/task/repository/sqlite"
|
|
"github.com/kandev/kandev/internal/user/store"
|
|
)
|
|
|
|
// buildOrgUnitService creates the unit tree, wires its occupancy seam, and runs
|
|
// the one-shot placement so no workspace is left without a home.
|
|
//
|
|
// It runs after the tenancy migration, which is what assigns organization ids:
|
|
// placing a workspace before it knows its organization would put it under the
|
|
// wrong root.
|
|
func buildOrgUnitService(
|
|
pool *db.Pool,
|
|
taskRepo *tasksqlite.Repository,
|
|
accounts store.AccountRepository,
|
|
log *logger.Logger,
|
|
) (*orgunit.Service, error) {
|
|
unitStore, err := orgunit.NewStore(pool)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
svc := orgunit.NewService(unitStore, log)
|
|
svc.SetWorkspaceCounter(taskRepo)
|
|
|
|
if _, err := svc.Backfill(
|
|
context.Background(),
|
|
unitAccountAdapter{accounts: accounts},
|
|
unitPlacementAdapter{repo: taskRepo},
|
|
); err != nil {
|
|
return nil, fmt.Errorf("organization unit backfill: %w", err)
|
|
}
|
|
return svc, nil
|
|
}
|
|
|
|
// unitAccountAdapter narrows the user repository to what a personal unit needs.
|
|
type unitAccountAdapter struct{ accounts store.AccountRepository }
|
|
|
|
func (a unitAccountAdapter) ListUnitUsers(ctx context.Context) ([]orgunit.UserRef, error) {
|
|
users, err := a.accounts.ListUsers(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]orgunit.UserRef, 0, len(users))
|
|
for _, u := range users {
|
|
if u == nil {
|
|
continue
|
|
}
|
|
out = append(out, orgunit.UserRef{ID: u.ID, OrgID: u.OrgID, DisplayName: u.DisplayName})
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// unitPlacementAdapter narrows the task repository to workspace placement.
|
|
type unitPlacementAdapter struct{ repo *tasksqlite.Repository }
|
|
|
|
func (a unitPlacementAdapter) UnplacedWorkspaces(ctx context.Context) ([]orgunit.WorkspaceRef, error) {
|
|
rows, err := a.repo.UnplacedWorkspaces(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]orgunit.WorkspaceRef, 0, len(rows))
|
|
for _, r := range rows {
|
|
out = append(out, orgunit.WorkspaceRef{ID: r.ID, OrgID: r.OrgID, OwnerID: r.OwnerID})
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (a unitPlacementAdapter) PlaceWorkspace(ctx context.Context, workspaceID, unitID string) error {
|
|
return a.repo.PlaceWorkspace(ctx, workspaceID, unitID)
|
|
}
|