feat: expand companion platform, device controls, and memoir workflows
ci / verify (push) Canceled after 0s
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:
@@ -30,6 +30,7 @@ var (
|
||||
ErrResetCounterConflict = errors.New("device factory reset counter is stale")
|
||||
ErrFirmwareInProgress = errors.New("device firmware update is in progress")
|
||||
ErrPendingTranscripts = errors.New("device has pending transcripts")
|
||||
ErrCallInProgress = errors.New("device has an active call")
|
||||
)
|
||||
|
||||
type Input struct {
|
||||
@@ -108,6 +109,11 @@ func (s *Service) Reset(ctx context.Context, identity deviceauth.Identity, idemp
|
||||
return Response{}, false, err
|
||||
}
|
||||
guard := func(ctx context.Context, tx pgx.Tx, cached *idempotency.Result) error {
|
||||
// Match device control, OTA and ownership writers: twin before device.
|
||||
var lockedDevice string
|
||||
if err := tx.QueryRow(ctx, `SELECT device_id::text FROM device_twin WHERE device_id=$1 AND tenant_id=$2 FOR UPDATE`, identity.ID, identity.TenantID).Scan(&lockedDevice); err != nil {
|
||||
return err
|
||||
}
|
||||
var mode string
|
||||
var generation, counter int64
|
||||
if err := tx.QueryRow(ctx, `SELECT enrollment_mode,binding_generation,factory_reset_counter FROM device WHERE id=$1::uuid FOR UPDATE`, identity.ID).Scan(&mode, &generation, &counter); err != nil {
|
||||
@@ -182,14 +188,26 @@ func (s *Service) reset(ctx context.Context, tx pgx.Tx, identity deviceauth.Iden
|
||||
|
||||
var activeFirmware int
|
||||
if err := tx.QueryRow(ctx, `
|
||||
SELECT count(*) FROM firmware_assignment
|
||||
WHERE device_id=$1::uuid AND tenant_id=$2::uuid AND status IN ('ASSIGNED','DOWNLOADING','INSTALLING')`,
|
||||
SELECT (SELECT count(*) FROM firmware_assignment
|
||||
WHERE device_id=$1::uuid AND tenant_id=$2::uuid AND status IN ('PENDING','ASSIGNED','COMMAND_QUEUED','DOWNLOADING','INSTALLING'))
|
||||
+ (SELECT count(*) FROM fleet_ota WHERE device_id=$1::uuid AND status IN ('pending','accepted'))`,
|
||||
identity.ID, identity.TenantID).Scan(&activeFirmware); err != nil {
|
||||
return Response{}, fmt.Errorf("check active firmware update: %w", err)
|
||||
}
|
||||
if activeFirmware > 0 {
|
||||
return Response{}, ErrFirmwareInProgress
|
||||
}
|
||||
// A physical-confirm device must not clear a live direct-call session.
|
||||
if identity.EnrollmentMode == "physical_confirm" {
|
||||
var busy bool
|
||||
if err := tx.QueryRow(ctx, `SELECT EXISTS(SELECT 1 FROM call_session WHERE device_id=$1 AND call_state IN ('PREPARING','STARTING','TALKING','STOPPING'))
|
||||
OR EXISTS(SELECT 1 FROM conversation WHERE device_id=$1 AND status NOT IN ('COMPLETED','FAILED','EXPIRED'))`, identity.ID).Scan(&busy); err != nil {
|
||||
return Response{}, err
|
||||
}
|
||||
if busy {
|
||||
return Response{}, ErrCallInProgress
|
||||
}
|
||||
}
|
||||
|
||||
now := time.Now().UTC()
|
||||
endedConversations, err := endActiveConversations(ctx, tx, identity, requestID, now)
|
||||
@@ -255,7 +273,7 @@ func (s *Service) reset(ctx context.Context, tx pgx.Tx, identity deviceauth.Iden
|
||||
var configRevision int64
|
||||
if err := tx.QueryRow(ctx, `
|
||||
UPDATE device_twin
|
||||
SET desired_revision=desired_revision+1,reported_revision=NULL,reported_config='{}'::jsonb,
|
||||
SET desired_revision=desired_revision+1,desired_config='{}'::jsonb,reported_revision=NULL,reported_config='{}'::jsonb,
|
||||
capabilities='{}'::jsonb,updated_at=$3
|
||||
WHERE device_id=$1::uuid AND tenant_id=$2::uuid
|
||||
RETURNING desired_revision`, identity.ID, identity.TenantID, now).Scan(&configRevision); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user