ultisuite-backend/internal/migration/project_columns.go
R3D347HR4Y 1ffd0817d8
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(migration): enhance migration API with roster and audit export features
- Added endpoints for listing and importing migration rosters.
- Introduced audit export functionality for migration jobs in CSV and NDJSON formats.
- Implemented tenant mismatch validation for Microsoft migration claims.
- Enhanced error handling for email claiming and migration processes.
- Added integration tests for roster import and claim workflows.
2026-06-13 13:11:30 +02:00

92 lines
2.2 KiB
Go

package migration
import (
"encoding/json"
"strings"
"github.com/ultisuite/ulti-backend/internal/mail/hosted"
)
func projectSelectSQL(tablePrefix string) string {
if tablePrefix != "" && !strings.HasSuffix(tablePrefix, ".") {
tablePrefix += "."
}
p := tablePrefix
cols := []string{
p + "id::text",
"COALESCE(" + p + "domain_id::text, '')",
p + "name",
p + "source_provider",
p + "auth_mode",
p + "status",
p + "cutover_at::text",
p + "delta_mode",
p + "shared_drive_mode",
p + "created_at::text",
"COALESCE(NULLIF(" + p + "microsoft_tenant_id, ''), '')",
p + "microsoft_admin_consent_at::text",
"COALESCE(NULLIF(" + p + "microsoft_admin_consent_error, ''), '')",
p + "cutover_dns_json",
}
return strings.Join(cols, ", ")
}
type projectScanner struct {
project Project
cutoverDNSRaw []byte
}
func newProjectScanner() *projectScanner {
return &projectScanner{}
}
func (s *projectScanner) targets() []any {
return []any{
&s.project.ID, &s.project.DomainID, &s.project.Name, &s.project.SourceProvider,
&s.project.AuthMode, &s.project.Status, &s.project.CutoverAt, &s.project.DeltaMode,
&s.project.SharedDriveMode, &s.project.CreatedAt, &s.project.MicrosoftTenantID, &s.project.MicrosoftAdminConsentAt,
&s.project.MicrosoftAdminConsentError, &s.cutoverDNSRaw,
}
}
func (s *projectScanner) result() Project {
applyCutoverDNS(&s.project, s.cutoverDNSRaw)
return s.project
}
func applyCutoverDNS(p *Project, raw []byte) {
p.CutoverDNS = nil
raw = bytesTrimSpace(raw)
if len(raw) == 0 || string(raw) == "{}" || string(raw) == "null" {
return
}
var report hosted.DNSCheckReport
if err := json.Unmarshal(raw, &report); err != nil {
return
}
if !dnsReportHasContent(report) {
return
}
p.CutoverDNS = &report
}
func dnsReportHasContent(r hosted.DNSCheckReport) bool {
if strings.TrimSpace(r.Domain) != "" {
return true
}
if len(r.Errors) > 0 || len(r.Warnings) > 0 {
return true
}
if len(r.MXRecords) > 0 || len(r.TXTRecords) > 0 || len(r.ExpectedMX) > 0 {
return true
}
if r.TXTVerified || r.MXVerified {
return true
}
return strings.TrimSpace(r.TXTExpected) != ""
}
func bytesTrimSpace(b []byte) []byte {
return []byte(strings.TrimSpace(string(b)))
}