mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
fix(server): stacked assets for full sync, userIds as array for delta sync (#9100)
* fix(server): stacked assets for full sync, userIds as array for delta sync * refactor(server): sync * fix getDeltaSync after partner removal --------- Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
This commit is contained in:
parent
fc2e709ad4
commit
32e7cfea3d
22 changed files with 817 additions and 386 deletions
|
|
@ -1,5 +1,4 @@
|
|||
import { Inject } from '@nestjs/common';
|
||||
import _ from 'lodash';
|
||||
import { DateTime } from 'luxon';
|
||||
import { AUDIT_LOG_MAX_DURATION } from 'src/constants';
|
||||
import { AccessCore, Permission } from 'src/cores/access.core';
|
||||
|
|
@ -11,6 +10,9 @@ import { IAccessRepository } from 'src/interfaces/access.interface';
|
|||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
||||
import { IAuditRepository } from 'src/interfaces/audit.interface';
|
||||
import { IPartnerRepository } from 'src/interfaces/partner.interface';
|
||||
import { setIsEqual } from 'src/utils/set';
|
||||
|
||||
const FULL_SYNC = { needsFullSync: true, deleted: [], upserted: [] };
|
||||
|
||||
export class SyncService {
|
||||
private access: AccessCore;
|
||||
|
|
@ -24,52 +26,69 @@ export class SyncService {
|
|||
this.access = AccessCore.create(accessRepository);
|
||||
}
|
||||
|
||||
async getAllAssetsForUserFullSync(auth: AuthDto, dto: AssetFullSyncDto): Promise<AssetResponseDto[]> {
|
||||
async getFullSync(auth: AuthDto, dto: AssetFullSyncDto): Promise<AssetResponseDto[]> {
|
||||
// mobile implementation is faster if this is a single id
|
||||
const userId = dto.userId || auth.user.id;
|
||||
await this.access.requirePermission(auth, Permission.TIMELINE_READ, userId);
|
||||
const assets = await this.assetRepository.getAllForUserFullSync({
|
||||
ownerId: userId,
|
||||
// no archived assets for partner user
|
||||
isArchived: userId === auth.user.id ? undefined : false,
|
||||
// no stack for partner user
|
||||
withStacked: userId === auth.user.id ? true : undefined,
|
||||
lastCreationDate: dto.lastCreationDate,
|
||||
updatedUntil: dto.updatedUntil,
|
||||
lastId: dto.lastId,
|
||||
limit: dto.limit,
|
||||
});
|
||||
const options = { auth, stripMetadata: false, withStack: true };
|
||||
return assets.map((a) => mapAsset(a, options));
|
||||
return assets.map((a) => mapAsset(a, { auth, stripMetadata: false, withStack: true }));
|
||||
}
|
||||
|
||||
async getChangesForDeltaSync(auth: AuthDto, dto: AssetDeltaSyncDto): Promise<AssetDeltaSyncResponseDto> {
|
||||
await this.access.requirePermission(auth, Permission.TIMELINE_READ, dto.userIds);
|
||||
const partner = await this.partnerRepository.getAll(auth.user.id);
|
||||
const userIds = [auth.user.id, ...partner.filter((p) => p.sharedWithId == auth.user.id).map((p) => p.sharedById)];
|
||||
userIds.sort();
|
||||
dto.userIds.sort();
|
||||
async getDeltaSync(auth: AuthDto, dto: AssetDeltaSyncDto): Promise<AssetDeltaSyncResponseDto> {
|
||||
// app has not synced in the last 100 days
|
||||
const duration = DateTime.now().diff(DateTime.fromJSDate(dto.updatedAfter));
|
||||
|
||||
if (!_.isEqual(userIds, dto.userIds) || duration > AUDIT_LOG_MAX_DURATION) {
|
||||
// app does not have the correct partners synced
|
||||
// or app has not synced in the last 100 days
|
||||
return { needsFullSync: true, deleted: [], upserted: [] };
|
||||
if (duration > AUDIT_LOG_MAX_DURATION) {
|
||||
return FULL_SYNC;
|
||||
}
|
||||
|
||||
const authUserId = auth.user.id;
|
||||
|
||||
// app does not have the correct partners synced
|
||||
const partner = await this.partnerRepository.getAll(authUserId);
|
||||
const userIds = [authUserId, ...partner.filter((p) => p.sharedWithId == auth.user.id).map((p) => p.sharedById)];
|
||||
if (!setIsEqual(new Set(userIds), new Set(dto.userIds))) {
|
||||
return FULL_SYNC;
|
||||
}
|
||||
|
||||
await this.access.requirePermission(auth, Permission.TIMELINE_READ, dto.userIds);
|
||||
|
||||
const limit = 10_000;
|
||||
const upserted = await this.assetRepository.getChangedDeltaSync({ limit, updatedAfter: dto.updatedAfter, userIds });
|
||||
|
||||
// too many changes, need to do a full sync
|
||||
if (upserted.length === limit) {
|
||||
// too many changes -> do a full sync (paginated) instead
|
||||
return { needsFullSync: true, deleted: [], upserted: [] };
|
||||
return FULL_SYNC;
|
||||
}
|
||||
|
||||
const deleted = await this.auditRepository.getAfter(dto.updatedAfter, {
|
||||
userIds: userIds,
|
||||
userIds,
|
||||
entityType: EntityType.ASSET,
|
||||
action: DatabaseAction.DELETE,
|
||||
});
|
||||
|
||||
const options = { auth, stripMetadata: false, withStack: true };
|
||||
const result = {
|
||||
needsFullSync: false,
|
||||
upserted: upserted.map((a) => mapAsset(a, options)),
|
||||
upserted: upserted
|
||||
// do not return archived assets for partner users
|
||||
.filter((a) => a.ownerId === auth.user.id || (a.ownerId !== auth.user.id && !a.isArchived))
|
||||
.map((a) =>
|
||||
mapAsset(a, {
|
||||
auth,
|
||||
stripMetadata: false,
|
||||
// ignore stacks for non partner users
|
||||
withStack: a.ownerId === authUserId,
|
||||
}),
|
||||
),
|
||||
deleted,
|
||||
};
|
||||
return result;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue