ultisuite-backend/internal/migration/onboarding.go
R3D347HR4Y 7143a36c19
Some checks are pending
CI / Go tests (push) Waiting to run
CI / Integration tests (push) Waiting to run
CI / DB migrations (push) Waiting to run
feat(mail): integrate Stalwart hosted mail and migration features
- Added configuration options for Stalwart hosted mail in .env.example.
- Updated Docker Compose to include Stalwart service with health checks.
- Introduced new API endpoints for managing mail domains and migration projects.
- Enhanced Authentik blueprints for user enrollment and post-migration security.
- Updated OAuth handling for Google and Microsoft migration processes.
- Improved error handling and response structures in the mail API.
- Added integration tests for email claiming and migration workflows.
2026-06-13 12:47:08 +02:00

94 lines
2.6 KiB
Go

package migration
import (
"context"
"strings"
)
// OnboardingHints guides the user-facing migration setup flow.
type OnboardingHints struct {
NeedsUserOAuth bool `json:"needs_user_oauth"`
OAuthProvider string `json:"oauth_provider,omitempty"`
WaitingForAdmin bool `json:"waiting_for_admin"`
WaitingReason string `json:"waiting_reason,omitempty"`
HasMigrationCredentials bool `json:"has_migration_credentials"`
NeedsMicrosoftAdminConsent bool `json:"needs_microsoft_admin_consent,omitempty"`
}
func (s *Service) BuildOnboardingHints(ctx context.Context, userID string, proj Project, invite Invite) OnboardingHints {
h := OnboardingHints{
OAuthProvider: proj.SourceProvider,
}
switch proj.Status {
case "active", "cutover":
// worker eligible
default:
h.WaitingForAdmin = true
if proj.Status == "draft" {
h.WaitingReason = "project_not_activated"
} else {
h.WaitingReason = "project_status_" + proj.Status
}
}
if UsesUserOAuth(proj.SourceProvider, proj.AuthMode) {
h.NeedsUserOAuth = true
if userID != "" {
hasCred, err := s.hasMigrationCredential(ctx, userID, proj.ID, proj.SourceProvider)
if err == nil {
h.HasMigrationCredentials = hasCred
}
}
}
if proj.SourceProvider == "microsoft" && proj.MicrosoftAdminConsentAt == nil {
h.NeedsMicrosoftAdminConsent = true
}
if invite.Status == "claimed" && h.WaitingForAdmin {
h.WaitingReason = waitingReasonMessage(h.WaitingReason)
}
return h
}
func waitingReasonMessage(code string) string {
switch code {
case "project_not_activated":
return "project_not_activated"
default:
return code
}
}
func (s *Service) hasMigrationCredential(ctx context.Context, userID, projectID, provider string) (bool, error) {
provider = strings.ToLower(strings.TrimSpace(provider))
if provider == "" {
return false, nil
}
var exists bool
err := s.db.QueryRow(ctx, `
SELECT EXISTS(
SELECT 1 FROM migration_credentials
WHERE user_id = $1::uuid AND project_id = $2::uuid AND provider = $3 AND revoked_at IS NULL
)
`, userID, projectID, provider).Scan(&exists)
return exists, err
}
func (s *Service) BuildInviteOnboardingHints(proj Project, invite Invite) OnboardingHints {
h := OnboardingHints{OAuthProvider: proj.SourceProvider}
if invite.Status == "claimed" {
h.NeedsUserOAuth = UsesUserOAuth(proj.SourceProvider, proj.AuthMode)
return h
}
if UsesUserOAuth(proj.SourceProvider, proj.AuthMode) {
h.NeedsUserOAuth = true
}
if proj.SourceProvider == "microsoft" && proj.MicrosoftAdminConsentAt == nil {
h.NeedsMicrosoftAdminConsent = true
}
return h
}