feat: add album asset sync (#19503)

wip: fix album asset exif and some other refactorings

feat: add album assets sync

feat: album to assets relation sync

Co-authored-by: Zack Pollard <zackpollard@ymail.com>
This commit is contained in:
Jason Rasmussen
2025-06-25 12:10:31 -04:00
committed by GitHub
parent b001ba44f5
commit 881a96cdf9
25 changed files with 1706 additions and 90 deletions

View File

@@ -57,11 +57,14 @@ export const SYNC_TYPES_ORDER = [
SyncRequestType.UsersV1,
SyncRequestType.PartnersV1,
SyncRequestType.AssetsV1,
SyncRequestType.AssetExifsV1,
SyncRequestType.PartnerAssetsV1,
SyncRequestType.PartnerAssetExifsV1,
SyncRequestType.AlbumsV1,
SyncRequestType.AlbumUsersV1,
SyncRequestType.AlbumAssetsV1,
SyncRequestType.AlbumToAssetsV1,
SyncRequestType.AssetExifsV1,
SyncRequestType.AlbumAssetExifsV1,
SyncRequestType.PartnerAssetExifsV1,
];
const throwSessionRequired = () => {
@@ -164,6 +167,21 @@ export class SyncService extends BaseService {
break;
}
case SyncRequestType.AlbumAssetsV1: {
await this.syncAlbumAssetsV1(response, checkpointMap, auth, sessionId);
break;
}
case SyncRequestType.AlbumToAssetsV1: {
await this.syncAlbumToAssetsV1(response, checkpointMap, auth, sessionId);
break;
}
case SyncRequestType.AlbumAssetExifsV1: {
await this.syncAlbumAssetExifsV1(response, checkpointMap, auth, sessionId);
break;
}
default: {
this.logger.warn(`Unsupported sync type: ${type}`);
break;
@@ -380,6 +398,147 @@ export class SyncService extends BaseService {
}
}
private async syncAlbumAssetsV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto, sessionId: string) {
const backfillType = SyncEntityType.AlbumAssetBackfillV1;
const upsertType = SyncEntityType.AlbumAssetV1;
const backfillCheckpoint = checkpointMap[backfillType];
const upsertCheckpoint = checkpointMap[upsertType];
const albums = await this.syncRepository.getAlbumBackfill(auth.user.id, backfillCheckpoint?.updateId);
if (upsertCheckpoint) {
const endId = upsertCheckpoint.updateId;
for (const album of albums) {
const createId = album.createId;
if (isEntityBackfillComplete(createId, backfillCheckpoint)) {
continue;
}
const startId = getStartId(createId, backfillCheckpoint);
const backfill = this.syncRepository.getAlbumAssetsBackfill(album.id, startId, endId);
for await (const { updateId, ...data } of backfill) {
send(response, { type: backfillType, ids: [createId, updateId], data: mapSyncAssetV1(data) });
}
sendEntityBackfillCompleteAck(response, backfillType, createId);
}
} else if (albums.length > 0) {
await this.upsertBackfillCheckpoint({
type: backfillType,
sessionId,
createId: albums.at(-1)!.createId,
});
}
const upserts = this.syncRepository.getAlbumAssetsUpserts(auth.user.id, checkpointMap[upsertType]);
for await (const { updateId, ...data } of upserts) {
send(response, { type: upsertType, ids: [updateId], data: mapSyncAssetV1(data) });
}
}
private async syncAlbumAssetExifsV1(
response: Writable,
checkpointMap: CheckpointMap,
auth: AuthDto,
sessionId: string,
) {
const backfillType = SyncEntityType.AlbumAssetExifBackfillV1;
const upsertType = SyncEntityType.AlbumAssetExifV1;
const backfillCheckpoint = checkpointMap[backfillType];
const upsertCheckpoint = checkpointMap[upsertType];
const albums = await this.syncRepository.getAlbumBackfill(auth.user.id, backfillCheckpoint?.updateId);
if (upsertCheckpoint) {
const endId = upsertCheckpoint.updateId;
for (const album of albums) {
const createId = album.createId;
if (isEntityBackfillComplete(createId, backfillCheckpoint)) {
continue;
}
const startId = getStartId(createId, backfillCheckpoint);
const backfill = this.syncRepository.getAlbumAssetExifsBackfill(album.id, startId, endId);
for await (const { updateId, ...data } of backfill) {
send(response, { type: backfillType, ids: [createId, updateId], data });
}
sendEntityBackfillCompleteAck(response, backfillType, createId);
}
} else if (albums.length > 0) {
await this.upsertBackfillCheckpoint({
type: backfillType,
sessionId,
createId: albums.at(-1)!.createId,
});
}
const upserts = this.syncRepository.getAlbumAssetExifsUpserts(auth.user.id, checkpointMap[upsertType]);
for await (const { updateId, ...data } of upserts) {
send(response, { type: upsertType, ids: [updateId], data });
}
}
private async syncAlbumToAssetsV1(
response: Writable,
checkpointMap: CheckpointMap,
auth: AuthDto,
sessionId: string,
) {
const backfillType = SyncEntityType.AlbumToAssetBackfillV1;
const upsertType = SyncEntityType.AlbumToAssetV1;
const backfillCheckpoint = checkpointMap[backfillType];
const upsertCheckpoint = checkpointMap[upsertType];
const deletes = this.syncRepository.getAlbumToAssetDeletes(
auth.user.id,
checkpointMap[SyncEntityType.AlbumToAssetDeleteV1],
);
for await (const { id, ...data } of deletes) {
send(response, { type: SyncEntityType.AlbumToAssetDeleteV1, ids: [id], data });
}
const albums = await this.syncRepository.getAlbumBackfill(auth.user.id, backfillCheckpoint?.updateId);
if (upsertCheckpoint) {
const endId = upsertCheckpoint.updateId;
for (const album of albums) {
const createId = album.createId;
if (isEntityBackfillComplete(createId, backfillCheckpoint)) {
continue;
}
const startId = getStartId(createId, backfillCheckpoint);
const backfill = this.syncRepository.getAlbumToAssetBackfill(album.id, startId, endId);
for await (const { updateId, ...data } of backfill) {
send(response, { type: backfillType, ids: [createId, updateId], data });
}
sendEntityBackfillCompleteAck(response, backfillType, createId);
}
} else if (albums.length > 0) {
await this.upsertBackfillCheckpoint({
type: backfillType,
sessionId,
createId: albums.at(-1)!.createId,
});
}
const upserts = this.syncRepository.getAlbumToAssetUpserts(auth.user.id, checkpointMap[upsertType]);
for await (const { updateId, ...data } of upserts) {
send(response, { type: upsertType, ids: [updateId], data });
}
}
private async upsertBackfillCheckpoint(item: { type: SyncEntityType; sessionId: string; createId: string }) {
const { type, sessionId, createId } = item;
await this.syncRepository.upsertCheckpoints([