mirror of
https://github.com/immich-app/immich.git
synced 2025-12-29 01:11:52 +03:00
feat: pending sync reset flag (#19861)
This commit is contained in:
@@ -241,6 +241,7 @@ describe(AuthService.name, () => {
|
||||
const sessionWithToken = {
|
||||
id: session.id,
|
||||
updatedAt: session.updatedAt,
|
||||
isPendingSyncReset: false,
|
||||
user: factory.authUser(),
|
||||
pinExpiresAt: null,
|
||||
};
|
||||
@@ -255,7 +256,11 @@ describe(AuthService.name, () => {
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
user: sessionWithToken.user,
|
||||
session: { id: session.id, hasElevatedPermission: false },
|
||||
session: {
|
||||
id: session.id,
|
||||
hasElevatedPermission: false,
|
||||
isPendingSyncReset: session.isPendingSyncReset,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -366,6 +371,7 @@ describe(AuthService.name, () => {
|
||||
id: session.id,
|
||||
updatedAt: session.updatedAt,
|
||||
user: factory.authUser(),
|
||||
isPendingSyncReset: false,
|
||||
pinExpiresAt: null,
|
||||
};
|
||||
|
||||
@@ -379,7 +385,11 @@ describe(AuthService.name, () => {
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
user: sessionWithToken.user,
|
||||
session: { id: session.id, hasElevatedPermission: false },
|
||||
session: {
|
||||
id: session.id,
|
||||
hasElevatedPermission: false,
|
||||
isPendingSyncReset: session.isPendingSyncReset,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -389,6 +399,7 @@ describe(AuthService.name, () => {
|
||||
id: session.id,
|
||||
updatedAt: session.updatedAt,
|
||||
user: factory.authUser(),
|
||||
isPendingSyncReset: false,
|
||||
pinExpiresAt: null,
|
||||
};
|
||||
|
||||
@@ -409,6 +420,7 @@ describe(AuthService.name, () => {
|
||||
id: session.id,
|
||||
updatedAt: session.updatedAt,
|
||||
user: factory.authUser(),
|
||||
isPendingSyncReset: false,
|
||||
pinExpiresAt: null,
|
||||
};
|
||||
|
||||
|
||||
@@ -466,6 +466,7 @@ export class AuthService extends BaseService {
|
||||
user: session.user,
|
||||
session: {
|
||||
id: session.id,
|
||||
isPendingSyncReset: session.isPendingSyncReset,
|
||||
hasElevatedPermission,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -2,7 +2,13 @@ import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
import { DateTime } from 'luxon';
|
||||
import { OnJob } from 'src/decorators';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import { SessionCreateDto, SessionCreateResponseDto, SessionResponseDto, mapSession } from 'src/dtos/session.dto';
|
||||
import {
|
||||
SessionCreateDto,
|
||||
SessionCreateResponseDto,
|
||||
SessionResponseDto,
|
||||
SessionUpdateDto,
|
||||
mapSession,
|
||||
} from 'src/dtos/session.dto';
|
||||
import { JobName, JobStatus, Permission, QueueName } from 'src/enum';
|
||||
import { BaseService } from 'src/services/base.service';
|
||||
|
||||
@@ -44,6 +50,20 @@ export class SessionService extends BaseService {
|
||||
return sessions.map((session) => mapSession(session, auth.session?.id));
|
||||
}
|
||||
|
||||
async update(auth: AuthDto, id: string, dto: SessionUpdateDto): Promise<SessionResponseDto> {
|
||||
await this.requireAccess({ auth, permission: Permission.SESSION_UPDATE, ids: [id] });
|
||||
|
||||
if (Object.values(dto).filter((prop) => prop !== undefined).length === 0) {
|
||||
throw new BadRequestException('No fields to update');
|
||||
}
|
||||
|
||||
const session = await this.sessionRepository.update(id, {
|
||||
isPendingSyncReset: dto.isPendingSyncReset,
|
||||
});
|
||||
|
||||
return mapSession(session);
|
||||
}
|
||||
|
||||
async delete(auth: AuthDto, id: string): Promise<void> {
|
||||
await this.requireAccess({ auth, permission: Permission.AUTH_DEVICE_DELETE, ids: [id] });
|
||||
await this.sessionRepository.delete(id);
|
||||
|
||||
@@ -22,7 +22,7 @@ import { SyncAck } from 'src/types';
|
||||
import { getMyPartnerIds } from 'src/utils/asset.util';
|
||||
import { hexOrBufferToBase64 } from 'src/utils/bytes';
|
||||
import { setIsEqual } from 'src/utils/set';
|
||||
import { fromAck, serialize, SerializeOptions, toAck } from 'src/utils/sync';
|
||||
import { fromAck, mapJsonLine, serialize, SerializeOptions, toAck } from 'src/utils/sync';
|
||||
|
||||
type CheckpointMap = Partial<Record<SyncEntityType, SyncAck>>;
|
||||
type AssetLike = Omit<SyncAssetV1, 'checksum' | 'thumbhash'> & {
|
||||
@@ -118,30 +118,42 @@ export class SyncService extends BaseService {
|
||||
}
|
||||
|
||||
async stream(auth: AuthDto, response: Writable, dto: SyncStreamDto) {
|
||||
const sessionId = auth.session?.id;
|
||||
if (!sessionId) {
|
||||
const session = auth.session;
|
||||
if (!session) {
|
||||
return throwSessionRequired();
|
||||
}
|
||||
|
||||
const checkpoints = await this.syncCheckpointRepository.getAll(sessionId);
|
||||
if (dto.reset) {
|
||||
await this.sessionRepository.resetSyncProgress(session.id);
|
||||
session.isPendingSyncReset = false;
|
||||
}
|
||||
|
||||
if (session.isPendingSyncReset) {
|
||||
response.write(mapJsonLine({ type: SyncEntityType.SyncResetV1, data: {} }));
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
|
||||
const checkpoints = await this.syncCheckpointRepository.getAll(session.id);
|
||||
const checkpointMap: CheckpointMap = Object.fromEntries(checkpoints.map(({ type, ack }) => [type, fromAck(ack)]));
|
||||
|
||||
const handlers: Record<SyncRequestType, () => Promise<void>> = {
|
||||
[SyncRequestType.UsersV1]: () => this.syncUsersV1(response, checkpointMap),
|
||||
[SyncRequestType.PartnersV1]: () => this.syncPartnersV1(response, checkpointMap, auth),
|
||||
[SyncRequestType.AssetsV1]: () => this.syncAssetsV1(response, checkpointMap, auth),
|
||||
[SyncRequestType.AssetExifsV1]: () => this.syncAssetExifsV1(response, checkpointMap, auth),
|
||||
[SyncRequestType.PartnerAssetsV1]: () => this.syncPartnerAssetsV1(response, checkpointMap, auth, sessionId),
|
||||
[SyncRequestType.PartnerAssetsV1]: () => this.syncPartnerAssetsV1(response, checkpointMap, auth, session.id),
|
||||
[SyncRequestType.PartnerAssetExifsV1]: () =>
|
||||
this.syncPartnerAssetExifsV1(response, checkpointMap, auth, sessionId),
|
||||
this.syncPartnerAssetExifsV1(response, checkpointMap, auth, session.id),
|
||||
[SyncRequestType.AlbumsV1]: () => this.syncAlbumsV1(response, checkpointMap, auth),
|
||||
[SyncRequestType.AlbumUsersV1]: () => this.syncAlbumUsersV1(response, checkpointMap, auth, sessionId),
|
||||
[SyncRequestType.AlbumAssetsV1]: () => this.syncAlbumAssetsV1(response, checkpointMap, auth, sessionId),
|
||||
[SyncRequestType.AlbumToAssetsV1]: () => this.syncAlbumToAssetsV1(response, checkpointMap, auth, sessionId),
|
||||
[SyncRequestType.AlbumAssetExifsV1]: () => this.syncAlbumAssetExifsV1(response, checkpointMap, auth, sessionId),
|
||||
[SyncRequestType.AlbumUsersV1]: () => this.syncAlbumUsersV1(response, checkpointMap, auth, session.id),
|
||||
[SyncRequestType.AlbumAssetsV1]: () => this.syncAlbumAssetsV1(response, checkpointMap, auth, session.id),
|
||||
[SyncRequestType.AlbumToAssetsV1]: () => this.syncAlbumToAssetsV1(response, checkpointMap, auth, session.id),
|
||||
[SyncRequestType.AlbumAssetExifsV1]: () => this.syncAlbumAssetExifsV1(response, checkpointMap, auth, session.id),
|
||||
[SyncRequestType.MemoriesV1]: () => this.syncMemoriesV1(response, checkpointMap, auth),
|
||||
[SyncRequestType.MemoryToAssetsV1]: () => this.syncMemoryAssetsV1(response, checkpointMap, auth),
|
||||
[SyncRequestType.StacksV1]: () => this.syncStackV1(response, checkpointMap, auth),
|
||||
[SyncRequestType.PartnerStacksV1]: () => this.syncPartnerStackV1(response, checkpointMap, auth, sessionId),
|
||||
[SyncRequestType.PartnerStacksV1]: () => this.syncPartnerStackV1(response, checkpointMap, auth, session.id),
|
||||
[SyncRequestType.PeopleV1]: () => this.syncPeopleV1(response, checkpointMap, auth),
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user