0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-02-11 01:18:24 -05:00

refactor(server): partner ids (#10321)

This commit is contained in:
Jason Rasmussen 2024-06-14 18:29:32 -04:00 committed by GitHub
parent c896fe393f
commit 78f600ebce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 57 additions and 35 deletions

View file

@ -39,6 +39,7 @@ import { ILoggerRepository } from 'src/interfaces/logger.interface';
import { IPartnerRepository } from 'src/interfaces/partner.interface'; import { IPartnerRepository } from 'src/interfaces/partner.interface';
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
import { IUserRepository } from 'src/interfaces/user.interface'; import { IUserRepository } from 'src/interfaces/user.interface';
import { getMyPartnerIds } from 'src/utils/asset.util';
import { usePagination } from 'src/utils/pagination'; import { usePagination } from 'src/utils/pagination';
export class AssetService { export class AssetService {
@ -62,18 +63,16 @@ export class AssetService {
} }
async getMemoryLane(auth: AuthDto, dto: MemoryLaneDto): Promise<MemoryLaneResponseDto[]> { async getMemoryLane(auth: AuthDto, dto: MemoryLaneDto): Promise<MemoryLaneResponseDto[]> {
const currentYear = new Date().getFullYear(); const partnerIds = await getMyPartnerIds({
userId: auth.user.id,
// get partners id repository: this.partnerRepository,
const userIds: string[] = [auth.user.id]; timelineEnabled: true,
const partners = await this.partnerRepository.getAll(auth.user.id); });
const partnersIds = partners const userIds = [auth.user.id, ...partnerIds];
.filter((partner) => partner.sharedBy && partner.inTimeline)
.map((partner) => partner.sharedById);
userIds.push(...partnersIds);
const assets = await this.assetRepository.getByDayOfYear(userIds, dto); const assets = await this.assetRepository.getByDayOfYear(userIds, dto);
const groups: Record<number, AssetEntity[]> = {}; const groups: Record<number, AssetEntity[]> = {};
const currentYear = new Date().getFullYear();
for (const asset of assets) { for (const asset of assets) {
const yearsAgo = currentYear - asset.localDateTime.getFullYear(); const yearsAgo = currentYear - asset.localDateTime.getFullYear();
if (!groups[yearsAgo]) { if (!groups[yearsAgo]) {

View file

@ -7,6 +7,7 @@ import { ILoggerRepository } from 'src/interfaces/logger.interface';
import { IMapRepository } from 'src/interfaces/map.interface'; import { IMapRepository } from 'src/interfaces/map.interface';
import { IPartnerRepository } from 'src/interfaces/partner.interface'; import { IPartnerRepository } from 'src/interfaces/partner.interface';
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
import { getMyPartnerIds } from 'src/utils/asset.util';
export class MapService { export class MapService {
private configCore: SystemConfigCore; private configCore: SystemConfigCore;
@ -23,14 +24,10 @@ export class MapService {
} }
async getMapMarkers(auth: AuthDto, options: MapMarkerDto): Promise<MapMarkerResponseDto[]> { async getMapMarkers(auth: AuthDto, options: MapMarkerDto): Promise<MapMarkerResponseDto[]> {
const userIds: string[] = [auth.user.id]; const userIds = [auth.user.id];
// TODO convert to SQL join
if (options.withPartners) { if (options.withPartners) {
const partners = await this.partnerRepository.getAll(auth.user.id); const partnerIds = await getMyPartnerIds({ userId: auth.user.id, repository: this.partnerRepository });
const partnersIds = partners userIds.push(...partnerIds);
.filter((partner) => partner.sharedBy && partner.sharedWith && partner.sharedById != auth.user.id)
.map((partner) => partner.sharedById);
userIds.push(...partnersIds);
} }
// TODO convert to SQL join // TODO convert to SQL join

View file

@ -24,6 +24,7 @@ import { IPartnerRepository } from 'src/interfaces/partner.interface';
import { IPersonRepository } from 'src/interfaces/person.interface'; import { IPersonRepository } from 'src/interfaces/person.interface';
import { ISearchRepository, SearchExploreItem } from 'src/interfaces/search.interface'; import { ISearchRepository, SearchExploreItem } from 'src/interfaces/search.interface';
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
import { getMyPartnerIds } from 'src/utils/asset.util';
import { isSmartSearchEnabled } from 'src/utils/misc'; import { isSmartSearchEnabled } from 'src/utils/misc';
@Injectable() @Injectable()
@ -140,13 +141,12 @@ export class SearchService {
} }
private async getUserIdsToSearch(auth: AuthDto): Promise<string[]> { private async getUserIdsToSearch(auth: AuthDto): Promise<string[]> {
const userIds: string[] = [auth.user.id]; const partnerIds = await getMyPartnerIds({
const partners = await this.partnerRepository.getAll(auth.user.id); userId: auth.user.id,
const partnersIds = partners repository: this.partnerRepository,
.filter((partner) => partner.sharedBy && partner.inTimeline) timelineEnabled: true,
.map((partner) => partner.sharedById); });
userIds.push(...partnersIds); return [auth.user.id, ...partnerIds];
return userIds;
} }
private mapResponse(assets: AssetEntity[], nextPage: string | null): SearchResponseDto { private mapResponse(assets: AssetEntity[], nextPage: string | null): SearchResponseDto {

View file

@ -10,6 +10,7 @@ import { IAccessRepository } from 'src/interfaces/access.interface';
import { IAssetRepository } from 'src/interfaces/asset.interface'; import { IAssetRepository } from 'src/interfaces/asset.interface';
import { IAuditRepository } from 'src/interfaces/audit.interface'; import { IAuditRepository } from 'src/interfaces/audit.interface';
import { IPartnerRepository } from 'src/interfaces/partner.interface'; import { IPartnerRepository } from 'src/interfaces/partner.interface';
import { getMyPartnerIds } from 'src/utils/asset.util';
import { setIsEqual } from 'src/utils/set'; import { setIsEqual } from 'src/utils/set';
const FULL_SYNC = { needsFullSync: true, deleted: [], upserted: [] }; const FULL_SYNC = { needsFullSync: true, deleted: [], upserted: [] };
@ -46,11 +47,9 @@ export class SyncService {
return FULL_SYNC; return FULL_SYNC;
} }
const authUserId = auth.user.id;
// app does not have the correct partners synced // app does not have the correct partners synced
const partner = await this.partnerRepository.getAll(authUserId); const partnerIds = await getMyPartnerIds({ userId: auth.user.id, repository: this.partnerRepository });
const userIds = [authUserId, ...partner.filter((p) => p.sharedWithId == auth.user.id).map((p) => p.sharedById)]; const userIds = [auth.user.id, ...partnerIds];
if (!setIsEqual(new Set(userIds), new Set(dto.userIds))) { if (!setIsEqual(new Set(userIds), new Set(dto.userIds))) {
return FULL_SYNC; return FULL_SYNC;
} }
@ -81,7 +80,7 @@ export class SyncService {
auth, auth,
stripMetadata: false, stripMetadata: false,
// ignore stacks for non partner users // ignore stacks for non partner users
withStack: a.ownerId === authUserId, withStack: a.ownerId === auth.user.id,
}), }),
), ),
deleted, deleted,

View file

@ -6,6 +6,7 @@ import { TimeBucketAssetDto, TimeBucketDto, TimeBucketResponseDto } from 'src/dt
import { IAccessRepository } from 'src/interfaces/access.interface'; import { IAccessRepository } from 'src/interfaces/access.interface';
import { IAssetRepository, TimeBucketOptions } from 'src/interfaces/asset.interface'; import { IAssetRepository, TimeBucketOptions } from 'src/interfaces/asset.interface';
import { IPartnerRepository } from 'src/interfaces/partner.interface'; import { IPartnerRepository } from 'src/interfaces/partner.interface';
import { getMyPartnerIds } from 'src/utils/asset.util';
export class TimelineService { export class TimelineService {
private accessCore: AccessCore; private accessCore: AccessCore;
@ -43,14 +44,9 @@ export class TimelineService {
if (userId) { if (userId) {
userIds = [userId]; userIds = [userId];
if (dto.withPartners) { if (dto.withPartners) {
const partners = await this.partnerRepository.getAll(auth.user.id); const partnerIds = await getMyPartnerIds({ userId: auth.user.id, repository: this.partnerRepository });
const partnersIds = partners userIds.push(...partnerIds);
.filter((partner) => partner.sharedBy && partner.sharedWith && partner.inTimeline)
.map((partner) => partner.sharedById);
userIds.push(...partnersIds);
} }
} }

View file

@ -2,6 +2,7 @@ import { AccessCore, Permission } from 'src/cores/access.core';
import { BulkIdErrorReason, BulkIdResponseDto } from 'src/dtos/asset-ids.response.dto'; import { BulkIdErrorReason, BulkIdResponseDto } from 'src/dtos/asset-ids.response.dto';
import { AuthDto } from 'src/dtos/auth.dto'; import { AuthDto } from 'src/dtos/auth.dto';
import { IAccessRepository } from 'src/interfaces/access.interface'; import { IAccessRepository } from 'src/interfaces/access.interface';
import { IPartnerRepository } from 'src/interfaces/partner.interface';
import { setDifference, setUnion } from 'src/utils/set'; import { setDifference, setUnion } from 'src/utils/set';
export interface IBulkAsset { export interface IBulkAsset {
@ -91,3 +92,33 @@ export const removeAssets = async (
return results; return results;
}; };
export type PartnerIdOptions = {
userId: string;
repository: IPartnerRepository;
/** only include partners with `inTimeline: true` */
timelineEnabled?: boolean;
};
export const getMyPartnerIds = async ({ userId, repository, timelineEnabled }: PartnerIdOptions) => {
const partnerIds = new Set<string>();
const partners = await repository.getAll(userId);
for (const partner of partners) {
// ignore deleted users
if (!partner.sharedBy || !partner.sharedWith) {
continue;
}
// wrong direction
if (partner.sharedWithId !== userId) {
continue;
}
if (timelineEnabled && !partner.inTimeline) {
continue;
}
partnerIds.add(partner.sharedById);
}
return [...partnerIds];
};