fix: missing deletedAt and isVisible columns on mobile (#26414)

* feat: SyncAssetV2

* feat: mobile sync handling

* feat: request correct sync object based on server version

* fix: mobile queries

* chore: sync sql

* fix: test

* chore: switch to mapper

* fix: sql sync
This commit is contained in:
Brandon Wees
2026-02-23 08:50:54 -06:00
committed by GitHub
parent a07d7b0c82
commit e633bc3f24
28 changed files with 9803 additions and 92 deletions

View File

@@ -422,6 +422,20 @@ export class SyncAssetFaceV1 {
sourceType!: string;
}
@ExtraModel()
export class SyncAssetFaceV2 extends SyncAssetFaceV1 {
@ApiProperty({ description: 'Face deleted at' })
deletedAt!: Date | null;
@ApiProperty({ description: 'Is the face visible in the asset' })
isVisible!: boolean;
}
export function syncAssetFaceV2ToV1(faceV2: SyncAssetFaceV2): SyncAssetFaceV1 {
const { deletedAt: _, isVisible: __, ...faceV1 } = faceV2;
return faceV1;
}
@ExtraModel()
export class SyncAssetFaceDeleteV1 {
@ApiProperty({ description: 'Asset face ID' })
@@ -497,6 +511,7 @@ export type SyncItem = {
[SyncEntityType.PersonV1]: SyncPersonV1;
[SyncEntityType.PersonDeleteV1]: SyncPersonDeleteV1;
[SyncEntityType.AssetFaceV1]: SyncAssetFaceV1;
[SyncEntityType.AssetFaceV2]: SyncAssetFaceV2;
[SyncEntityType.AssetFaceDeleteV1]: SyncAssetFaceDeleteV1;
[SyncEntityType.UserMetadataV1]: SyncUserMetadataV1;
[SyncEntityType.UserMetadataDeleteV1]: SyncUserMetadataDeleteV1;

View File

@@ -732,6 +732,7 @@ export enum SyncRequestType {
UsersV1 = 'UsersV1',
PeopleV1 = 'PeopleV1',
AssetFacesV1 = 'AssetFacesV1',
AssetFacesV2 = 'AssetFacesV2',
UserMetadataV1 = 'UserMetadataV1',
}
@@ -790,6 +791,7 @@ export enum SyncEntityType {
PersonDeleteV1 = 'PersonDeleteV1',
AssetFaceV1 = 'AssetFaceV1',
AssetFaceV2 = 'AssetFaceV2',
AssetFaceDeleteV1 = 'AssetFaceDeleteV1',
UserMetadataV1 = 'UserMetadataV1',

View File

@@ -540,6 +540,8 @@ select
"boundingBoxX2",
"boundingBoxY2",
"sourceType",
"isVisible",
"asset_face"."deletedAt",
"asset_face"."updateId"
from
"asset_face" as "asset_face"

View File

@@ -479,6 +479,8 @@ class AssetFaceSync extends BaseSync {
'boundingBoxX2',
'boundingBoxY2',
'sourceType',
'isVisible',
'asset_face.deletedAt',
'asset_face.updateId',
])
.leftJoin('asset', 'asset.id', 'asset_face.assetId')

View File

@@ -12,6 +12,7 @@ import {
AssetFullSyncDto,
SyncAckDeleteDto,
SyncAckSetDto,
syncAssetFaceV2ToV1,
SyncAssetV1,
SyncItem,
SyncStreamDto,
@@ -85,6 +86,7 @@ export const SYNC_TYPES_ORDER = [
SyncRequestType.MemoryToAssetsV1,
SyncRequestType.PeopleV1,
SyncRequestType.AssetFacesV1,
SyncRequestType.AssetFacesV2,
SyncRequestType.UserMetadataV1,
SyncRequestType.AssetMetadataV1,
];
@@ -189,6 +191,7 @@ export class SyncService extends BaseService {
[SyncRequestType.PartnerStacksV1]: () => this.syncPartnerStackV1(options, response, checkpointMap, session.id),
[SyncRequestType.PeopleV1]: () => this.syncPeopleV1(options, response, checkpointMap),
[SyncRequestType.AssetFacesV1]: async () => this.syncAssetFacesV1(options, response, checkpointMap),
[SyncRequestType.AssetFacesV2]: async () => this.syncAssetFacesV2(options, response, checkpointMap),
[SyncRequestType.UserMetadataV1]: () => this.syncUserMetadataV1(options, response, checkpointMap),
};
@@ -789,6 +792,21 @@ export class SyncService extends BaseService {
const upsertType = SyncEntityType.AssetFaceV1;
const upserts = this.syncRepository.assetFace.getUpserts({ ...options, ack: checkpointMap[upsertType] });
for await (const { updateId, ...data } of upserts) {
const v1 = syncAssetFaceV2ToV1(data);
send(response, { type: upsertType, ids: [updateId], data: v1 });
}
}
private async syncAssetFacesV2(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) {
const deleteType = SyncEntityType.AssetFaceDeleteV1;
const deletes = this.syncRepository.assetFace.getDeletes({ ...options, ack: checkpointMap[deleteType] });
for await (const { id, ...data } of deletes) {
send(response, { type: deleteType, ids: [id], data });
}
const upsertType = SyncEntityType.AssetFaceV2;
const upserts = this.syncRepository.assetFace.getUpserts({ ...options, ack: checkpointMap[upsertType] });
for await (const { updateId, ...data } of upserts) {
send(response, { type: upsertType, ids: [updateId], data });
}