chore: establish local platform baseline

This commit is contained in:
Codex Local
2026-08-26 06:41:54 +08:00
commit 29bdf4900f
575 changed files with 95644 additions and 0 deletions
+107
View File
@@ -0,0 +1,107 @@
package saas
import (
"context"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"time"
"data-terminal-cloud/internal/audit"
"data-terminal-cloud/internal/database"
"data-terminal-cloud/internal/identity"
"github.com/jackc/pgx/v5"
)
type DeviceClaimResult struct {
BindingID string `json:"binding_id"`
DeviceID string `json:"device_id"`
HouseholdID string `json:"household_id"`
SubjectID string `json:"subject_id"`
BoundAt time.Time `json:"bound_at"`
}
func (s *Service) ClaimDevice(ctx context.Context, principal identity.Principal, claimCode, householdPublicID, subjectPublicID, requestID string) (DeviceClaimResult, bool, error) {
var result DeviceClaimResult
var replayed bool
digestBytes := sha256.Sum256([]byte(claimCode))
digest := hex.EncodeToString(digestBytes[:])
err := database.Transact(ctx, s.pool, func(tx pgx.Tx) error {
access, err := resolveHouseholdAccess(ctx, tx, principal, householdPublicID, identity.CapabilityDeviceManage, true)
if err != nil {
return err
}
var subjectID string
if err := tx.QueryRow(ctx, `
SELECT id::text FROM subject_profile
WHERE public_id=$1::uuid AND tenant_id=$2::uuid AND household_id=$3::uuid AND status <> 'deleted'
FOR SHARE`, subjectPublicID, access.tenantID, access.householdID).Scan(&subjectID); errors.Is(err, pgx.ErrNoRows) {
return ErrAccessDenied
} else if err != nil {
return fmt.Errorf("resolve claim subject: %w", err)
}
var claimID, deviceID, devicePublicID string
var expiresAt time.Time
var revokedAt, usedAt *time.Time
var usedBindingID *string
if err := tx.QueryRow(ctx, `
SELECT dc.id::text,dc.device_id::text,d.public_id::text,dc.expires_at,dc.revoked_at,dc.used_at,dc.used_binding_id::text
FROM device_claim dc JOIN device d ON d.id=dc.device_id AND d.tenant_id=dc.tenant_id
WHERE dc.token_digest=$1 AND dc.tenant_id=$2::uuid AND d.status='registered'
FOR UPDATE OF dc`, digest, access.tenantID).Scan(&claimID, &deviceID, &devicePublicID, &expiresAt, &revokedAt, &usedAt, &usedBindingID); errors.Is(err, pgx.ErrNoRows) {
return ErrDeviceClaimInvalid
} else if err != nil {
return fmt.Errorf("resolve device claim: %w", err)
}
if revokedAt != nil || !expiresAt.After(time.Now()) {
return ErrDeviceClaimInvalid
}
if usedAt != nil {
if usedBindingID == nil {
return ErrDeviceClaimInvalid
}
if err := tx.QueryRow(ctx, `
SELECT b.public_id::text,d.public_id::text,h.public_id::text,sp.public_id::text,b.bound_at
FROM device_binding b
JOIN device d ON d.id=b.device_id AND d.tenant_id=b.tenant_id
JOIN household h ON h.id=b.household_id AND h.tenant_id=b.tenant_id
JOIN subject_profile sp ON sp.id=b.subject_id AND sp.tenant_id=b.tenant_id
WHERE b.id=$1::uuid`, *usedBindingID).Scan(&result.BindingID, &result.DeviceID, &result.HouseholdID, &result.SubjectID, &result.BoundAt); err != nil {
return fmt.Errorf("read used device claim binding: %w", err)
}
if result.HouseholdID != householdPublicID || result.SubjectID != subjectPublicID {
return ErrDeviceClaimUsed
}
replayed = true
return nil
}
var activeBinding bool
if err := tx.QueryRow(ctx, `SELECT EXISTS(SELECT 1 FROM device_binding WHERE device_id=$1::uuid AND unbound_at IS NULL)`, deviceID).Scan(&activeBinding); err != nil {
return fmt.Errorf("check active device binding: %w", err)
}
if activeBinding {
return ErrDeviceAlreadyBound
}
var bindingInternalID string
if err := tx.QueryRow(ctx, `
INSERT INTO device_binding (tenant_id,device_id,household_id,subject_id,bound_by,reason)
VALUES ($1::uuid,$2::uuid,$3::uuid,$4::uuid,$5::uuid,'one_time_claim')
RETURNING id::text,public_id::text,$6::text,$7::text,$8::text,bound_at`,
access.tenantID, deviceID, access.householdID, subjectID, access.userID,
devicePublicID, householdPublicID, subjectPublicID).Scan(&bindingInternalID, &result.BindingID, &result.DeviceID, &result.HouseholdID, &result.SubjectID, &result.BoundAt); err != nil {
return fmt.Errorf("insert claimed device binding: %w", err)
}
if _, err := tx.Exec(ctx, `UPDATE device_claim SET used_at=now(),used_binding_id=$1::uuid WHERE id=$2::uuid`, bindingInternalID, claimID); err != nil {
return fmt.Errorf("mark device claim used: %w", err)
}
return s.audit.Record(ctx, tx, audit.Event{
TenantID: strPtr(access.tenantID), ActorType: "user", ActorPublicID: strPtr(access.userPublicID),
Action: "device.claim", ResourceType: "device_binding", ResourceID: strPtr(result.BindingID),
RequestID: requestID, Result: "success", Detail: map[string]any{"device_id": devicePublicID, "household_id": householdPublicID, "subject_id": subjectPublicID},
})
})
return result, replayed, err
}