Merge branch 'main' into feat/mobile-ocr

This commit is contained in:
Yaros
2026-04-30 20:24:18 +02:00
143 changed files with 3088 additions and 2141 deletions
+54 -40
View File
@@ -8,8 +8,7 @@ import {
SyncAckDeleteDto,
SyncAckSetDto,
syncAlbumV2ToV1,
syncAssetFaceV2ToV1,
SyncAssetV1,
SyncAssetV2,
SyncItem,
SyncStreamDto,
} from 'src/dtos/sync.dto';
@@ -22,7 +21,7 @@ import { hexOrBufferToBase64 } from 'src/utils/bytes';
import { fromAck, serialize, SerializeOptions, toAck } from 'src/utils/sync';
type CheckpointMap = Partial<Record<SyncEntityType, SyncAck>>;
type AssetLike = Omit<SyncAssetV1, 'checksum' | 'thumbhash'> & {
type AssetLike = Omit<SyncAssetV2, 'checksum' | 'thumbhash'> & {
checksum: Buffer<ArrayBufferLike>;
thumbhash: Buffer<ArrayBufferLike> | null;
};
@@ -31,7 +30,7 @@ const COMPLETE_ID = 'complete';
const MAX_DAYS = 30;
const MAX_DURATION = Duration.fromObject({ days: MAX_DAYS });
const mapSyncAssetV1 = ({ checksum, thumbhash, ...data }: AssetLike): SyncAssetV1 => ({
const mapSyncAssetV2 = ({ checksum, thumbhash, ...data }: AssetLike): SyncAssetV2 => ({
...data,
checksum: hexOrBufferToBase64(checksum),
thumbhash: thumbhash ? hexOrBufferToBase64(thumbhash) : null,
@@ -56,10 +55,13 @@ export const SYNC_TYPES_ORDER = [
SyncRequestType.UsersV1,
SyncRequestType.PartnersV1,
SyncRequestType.AssetsV1,
SyncRequestType.AssetsV2,
SyncRequestType.StacksV1,
SyncRequestType.PartnerAssetsV1,
SyncRequestType.PartnerAssetsV2,
SyncRequestType.PartnerStacksV1,
SyncRequestType.AlbumAssetsV1,
SyncRequestType.AlbumAssetsV2,
SyncRequestType.AlbumsV1,
SyncRequestType.AlbumsV2,
SyncRequestType.AlbumUsersV1,
@@ -157,20 +159,26 @@ export class SyncService extends BaseService {
const options: SyncQueryOptions = { nowId, userId: auth.user.id };
const handlers: Record<SyncRequestType, () => Promise<void>> = {
// deprecated handlers
[SyncRequestType.AssetsV1]: () => this.syncAssetsV1(),
[SyncRequestType.AssetFacesV1]: () => this.syncAssetFacesV1(),
[SyncRequestType.PartnerAssetsV1]: () => this.syncPartnerAssetsV1(),
[SyncRequestType.AlbumAssetsV1]: () => this.syncAlbumAssetsV1(),
[SyncRequestType.AuthUsersV1]: () => this.syncAuthUsersV1(options, response, checkpointMap),
[SyncRequestType.UsersV1]: () => this.syncUsersV1(options, response, checkpointMap),
[SyncRequestType.PartnersV1]: () => this.syncPartnersV1(options, response, checkpointMap),
[SyncRequestType.AssetsV1]: () => this.syncAssetsV1(options, response, checkpointMap),
[SyncRequestType.AssetsV2]: () => this.syncAssetsV2(options, response, checkpointMap),
[SyncRequestType.AssetExifsV1]: () => this.syncAssetExifsV1(options, response, checkpointMap),
[SyncRequestType.AssetEditsV1]: () => this.syncAssetEditsV1(options, response, checkpointMap),
[SyncRequestType.PartnerAssetsV1]: () => this.syncPartnerAssetsV1(options, response, checkpointMap, session.id),
[SyncRequestType.PartnerAssetsV2]: () => this.syncPartnerAssetsV2(options, response, checkpointMap, session.id),
[SyncRequestType.AssetMetadataV1]: () => this.syncAssetMetadataV1(options, response, checkpointMap, auth),
[SyncRequestType.PartnerAssetExifsV1]: () =>
this.syncPartnerAssetExifsV1(options, response, checkpointMap, session.id),
[SyncRequestType.AlbumsV1]: () => this.syncAlbumsV1(options, response, checkpointMap),
[SyncRequestType.AlbumsV2]: () => this.syncAlbumsV2(options, response, checkpointMap),
[SyncRequestType.AlbumUsersV1]: () => this.syncAlbumUsersV1(options, response, checkpointMap, session.id),
[SyncRequestType.AlbumAssetsV1]: () => this.syncAlbumAssetsV1(options, response, checkpointMap, session.id),
[SyncRequestType.AlbumAssetsV2]: () => this.syncAlbumAssetsV2(options, response, checkpointMap, session.id),
[SyncRequestType.AlbumToAssetsV1]: () => this.syncAlbumToAssetsV1(options, response, checkpointMap, session.id),
[SyncRequestType.AlbumAssetExifsV1]: () =>
this.syncAlbumAssetExifsV1(options, response, checkpointMap, session.id),
@@ -179,14 +187,13 @@ export class SyncService extends BaseService {
[SyncRequestType.StacksV1]: () => this.syncStackV1(options, response, checkpointMap),
[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.AssetFacesV2]: () => this.syncAssetFacesV2(options, response, checkpointMap),
[SyncRequestType.UserMetadataV1]: () => this.syncUserMetadataV1(options, response, checkpointMap),
[SyncRequestType.AssetOcrV1]: () => this.syncAssetOcrV1(options, response, checkpointMap, auth),
};
} as const;
for (const type of SYNC_TYPES_ORDER.filter((type) => dto.types.includes(type))) {
const handler = handlers[type];
const handler = handlers[type as keyof typeof handlers];
await handler();
}
@@ -263,21 +270,31 @@ export class SyncService extends BaseService {
}
}
private async syncAssetsV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) {
private syncAssetsV1(): Promise<void> {
throw new BadRequestException('SyncRequestType.AssetsV1 is deprecated, use SyncRequestType.AssetsV2 instead');
}
private async syncAssetsV2(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) {
const deleteType = SyncEntityType.AssetDeleteV1;
const deletes = this.syncRepository.asset.getDeletes({ ...options, ack: checkpointMap[deleteType] });
for await (const { id, ...data } of deletes) {
send(response, { type: deleteType, ids: [id], data });
}
const upsertType = SyncEntityType.AssetV1;
const upsertType = SyncEntityType.AssetV2;
const upserts = this.syncRepository.asset.getUpserts({ ...options, ack: checkpointMap[upsertType] });
for await (const { updateId, ...data } of upserts) {
send(response, { type: upsertType, ids: [updateId], data: mapSyncAssetV1(data) });
send(response, { type: upsertType, ids: [updateId], data: mapSyncAssetV2(data) });
}
}
private async syncPartnerAssetsV1(
private syncPartnerAssetsV1(): Promise<void> {
throw new BadRequestException(
'SyncRequestType.PartnerAssetsV1 is deprecated, use SyncRequestType.PartnerAssetsV2 instead',
);
}
private async syncPartnerAssetsV2(
options: SyncQueryOptions,
response: Writable,
checkpointMap: CheckpointMap,
@@ -289,13 +306,13 @@ export class SyncService extends BaseService {
send(response, { type: deleteType, ids: [id], data });
}
const backfillType = SyncEntityType.PartnerAssetBackfillV1;
const backfillType = SyncEntityType.PartnerAssetBackfillV2;
const backfillCheckpoint = checkpointMap[backfillType];
const partners = await this.syncRepository.partner.getCreatedAfter({
...options,
afterCreateId: backfillCheckpoint?.updateId,
});
const upsertType = SyncEntityType.PartnerAssetV1;
const upsertType = SyncEntityType.PartnerAssetV2;
const upsertCheckpoint = checkpointMap[upsertType];
if (upsertCheckpoint) {
const endId = upsertCheckpoint.updateId;
@@ -316,7 +333,7 @@ export class SyncService extends BaseService {
send(response, {
type: backfillType,
ids: [createId, updateId],
data: mapSyncAssetV1(data),
data: mapSyncAssetV2(data),
});
}
@@ -332,7 +349,7 @@ export class SyncService extends BaseService {
const upserts = this.syncRepository.partnerAsset.getUpserts({ ...options, ack: checkpointMap[upsertType] });
for await (const { updateId, ...data } of upserts) {
send(response, { type: upsertType, ids: [updateId], data: mapSyncAssetV1(data) });
send(response, { type: upsertType, ids: [updateId], data: mapSyncAssetV2(data) });
}
}
@@ -493,20 +510,26 @@ export class SyncService extends BaseService {
}
}
private async syncAlbumAssetsV1(
private syncAlbumAssetsV1(): Promise<void> {
throw new BadRequestException(
'SyncRequestType.AlbumAssetsV1 is deprecated, use SyncRequestType.AlbumAssetsV2 instead',
);
}
private async syncAlbumAssetsV2(
options: SyncQueryOptions,
response: Writable,
checkpointMap: CheckpointMap,
sessionId: string,
) {
const backfillType = SyncEntityType.AlbumAssetBackfillV1;
const backfillType = SyncEntityType.AlbumAssetBackfillV2;
const backfillCheckpoint = checkpointMap[backfillType];
const albums = await this.syncRepository.album.getCreatedAfter({
...options,
afterCreateId: backfillCheckpoint?.updateId,
});
const updateType = SyncEntityType.AlbumAssetUpdateV1;
const createType = SyncEntityType.AlbumAssetCreateV1;
const updateType = SyncEntityType.AlbumAssetUpdateV2;
const createType = SyncEntityType.AlbumAssetCreateV2;
const updateCheckpoint = checkpointMap[updateType];
const createCheckpoint = checkpointMap[createType];
if (createCheckpoint) {
@@ -525,7 +548,7 @@ export class SyncService extends BaseService {
);
for await (const { updateId, ...data } of backfill) {
send(response, { type: backfillType, ids: [createId, updateId], data: mapSyncAssetV1(data) });
send(response, { type: backfillType, ids: [createId, updateId], data: mapSyncAssetV2(data) });
}
sendEntityBackfillCompleteAck(response, backfillType, createId);
@@ -544,7 +567,7 @@ export class SyncService extends BaseService {
createCheckpoint,
);
for await (const { updateId, ...data } of updates) {
send(response, { type: updateType, ids: [updateId], data: mapSyncAssetV1(data) });
send(response, { type: updateType, ids: [updateId], data: mapSyncAssetV2(data) });
}
}
@@ -555,12 +578,12 @@ export class SyncService extends BaseService {
send(response, {
type: SyncEntityType.SyncAckV1,
data: {},
ackType: SyncEntityType.AlbumAssetUpdateV1,
ackType: SyncEntityType.AlbumAssetUpdateV2,
ids: [options.nowId],
});
first = false;
}
send(response, { type: createType, ids: [updateId], data: mapSyncAssetV1(data) });
send(response, { type: createType, ids: [updateId], data: mapSyncAssetV2(data) });
}
}
@@ -805,19 +828,10 @@ export class SyncService extends BaseService {
}
}
private async syncAssetFacesV1(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.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 syncAssetFacesV1(): Promise<void> {
throw new BadRequestException(
'SyncRequestType.AssetFacesV1 is deprecated, use SyncRequestType.AssetFacesV2 instead',
);
}
private async syncAssetFacesV2(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) {