feat: expand companion platform, device controls, and memoir workflows
ci / verify (push) Canceled after 0s

Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
This commit is contained in:
Codex Local
2026-10-08 10:06:44 +08:00
parent 98fb2965d0
commit bf1233d5df
861 changed files with 94614 additions and 3209 deletions
+35 -4
View File
@@ -329,11 +329,36 @@ func containsString(values []string, target string) bool {
}
func (s *Service) ListPrivacyRequests(ctx context.Context, principal identity.Principal, subjectPublicID string) ([]PrivacyRequest, error) {
access, _, err := resolveSubject(ctx, s.pool, principal, subjectPublicID, identity.CapabilityPrivacyRead, false)
// The retained subject row links deletion receipts to their household even
// after normal subject reads are disabled. Never expose the deleted profile.
var tenantID, subjectID, householdPublicID string
err := s.pool.QueryRow(ctx, `SELECT tenant_id::text,id::text,(SELECT public_id::text FROM household WHERE id=sp.household_id AND tenant_id=sp.tenant_id) FROM subject_profile sp WHERE sp.public_id=$1::uuid`, subjectPublicID).Scan(&tenantID, &subjectID, &householdPublicID)
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrAccessDenied
}
if err != nil {
return nil, err
}
rows, err := s.pool.Query(ctx, privacyRequestSelectSQL+` WHERE pr.tenant_id=$1::uuid AND pr.subject_id=$2::uuid ORDER BY pr.created_at DESC,pr.id DESC`, access.tenantID, access.subjectID)
access, err := resolveHouseholdAccess(ctx, s.pool, principal, householdPublicID, identity.CapabilityPrivacyRead, false)
if err != nil {
return nil, err
}
if access.tenantID != tenantID {
return nil, ErrAccessDenied
}
return s.listPrivacyRequests(ctx, `pr.tenant_id=$1::uuid AND pr.subject_id=$2::uuid`, access.tenantID, subjectID)
}
func (s *Service) ListHouseholdPrivacyRequests(ctx context.Context, principal identity.Principal, householdPublicID string) ([]PrivacyRequest, error) {
access, err := resolveHouseholdAccess(ctx, s.pool, principal, householdPublicID, identity.CapabilityPrivacyRead, false)
if err != nil {
return nil, err
}
return s.listPrivacyRequests(ctx, `pr.tenant_id=$1::uuid AND sp.household_id=$2::uuid`, access.tenantID, access.householdID)
}
func (s *Service) listPrivacyRequests(ctx context.Context, where string, tenantID, resourceID string) ([]PrivacyRequest, error) {
rows, err := s.pool.Query(ctx, privacyRequestSelectSQL+` WHERE `+where+` ORDER BY pr.created_at DESC,pr.id DESC`, tenantID, resourceID)
if err != nil {
return nil, err
}
@@ -352,6 +377,7 @@ func (s *Service) ListPrivacyRequests(ctx context.Context, principal identity.Pr
func (s *Service) GetPrivacyRequest(ctx context.Context, principal identity.Principal, publicID string) (PrivacyRequest, error) {
item, err := scanPrivacyRequest(s.pool.QueryRow(ctx, privacyRequestSelectSQL+`
JOIN household h ON h.id=sp.household_id AND h.tenant_id=sp.tenant_id AND h.status<>'deleted'
JOIN tenant t ON t.id=pr.tenant_id AND t.status='active'
JOIN household_member hm ON hm.household_id=h.id AND hm.tenant_id=h.tenant_id AND hm.status='active'
JOIN user_account ua ON ua.id=hm.user_id AND ua.tenant_id=h.tenant_id AND ua.status='active'
WHERE pr.public_id=$1::uuid AND ua.oidc_issuer=$2 AND ua.oidc_subject=$3`, publicID, principal.Issuer, principal.Subject))
@@ -362,7 +388,7 @@ func (s *Service) GetPrivacyRequest(ctx context.Context, principal identity.Prin
return PrivacyRequest{}, err
}
var role string
if err := s.pool.QueryRow(ctx, `SELECT hm.role FROM privacy_request pr JOIN subject_profile sp ON sp.id=pr.subject_id JOIN household_member hm ON hm.household_id=sp.household_id AND hm.status='active' JOIN user_account ua ON ua.id=hm.user_id AND ua.tenant_id=pr.tenant_id AND ua.status='active' WHERE pr.public_id=$1::uuid AND ua.oidc_issuer=$2 AND ua.oidc_subject=$3`, publicID, principal.Issuer, principal.Subject).Scan(&role); err != nil {
if err := s.pool.QueryRow(ctx, `SELECT hm.role FROM privacy_request pr JOIN subject_profile sp ON sp.id=pr.subject_id AND sp.tenant_id=pr.tenant_id JOIN household h ON h.id=sp.household_id AND h.tenant_id=pr.tenant_id AND h.status<>'deleted' JOIN tenant t ON t.id=pr.tenant_id AND t.status='active' JOIN household_member hm ON hm.household_id=h.id AND hm.tenant_id=pr.tenant_id AND hm.status='active' JOIN user_account ua ON ua.id=hm.user_id AND ua.tenant_id=pr.tenant_id AND ua.status='active' WHERE pr.public_id=$1::uuid AND ua.oidc_issuer=$2 AND ua.oidc_subject=$3`, publicID, principal.Issuer, principal.Subject).Scan(&role); err != nil {
return PrivacyRequest{}, ErrAccessDenied
}
if !identity.Can(identity.Role(role), identity.CapabilityPrivacyRead) {
@@ -374,7 +400,12 @@ func (s *Service) GetPrivacyRequest(ctx context.Context, principal identity.Prin
return item, nil
}
const privacyRequestSelectSQL = `SELECT pr.public_id::text,sp.public_id::text,pr.request_type,pr.scope,pr.status,pr.deadline_at,pr.legal_hold,pr.result_summary,pr.version,pr.created_at,pr.updated_at,pr.completed_at FROM privacy_request pr JOIN subject_profile sp ON sp.id=pr.subject_id AND sp.tenant_id=pr.tenant_id`
// A worker interruption can leave the request EXECUTING after recording a
// failed subtask. Expose that actionable state consistently in lists and detail
// without changing the durable receipt or restarting destructive work.
const privacyRequestSelectSQL = `SELECT pr.public_id::text,sp.public_id::text,pr.request_type,pr.scope,
CASE WHEN pr.status<>'COMPLETED' AND EXISTS (SELECT 1 FROM privacy_subtask failed WHERE failed.privacy_request_id=pr.id AND failed.status='FAILED') THEN 'PARTIAL_FAILED' ELSE pr.status END,
pr.deadline_at,pr.legal_hold,pr.result_summary,pr.version,pr.created_at,pr.updated_at,pr.completed_at FROM privacy_request pr JOIN subject_profile sp ON sp.id=pr.subject_id AND sp.tenant_id=pr.tenant_id`
func scanPrivacyRequest(row rowScanner) (PrivacyRequest, error) {
var item PrivacyRequest