mirror of
https://github.com/immich-app/immich.git
synced 2026-02-11 19:38:54 +03:00
expose albumPermissions on the API, deprecate sharedUsers
This commit is contained in:
@@ -83,6 +83,11 @@ export class AlbumCountResponseDto {
|
||||
notShared!: number;
|
||||
}
|
||||
|
||||
export class AlbumPermissionResponseDto {
|
||||
user!: UserResponseDto;
|
||||
readonly!: boolean;
|
||||
}
|
||||
|
||||
export class AlbumResponseDto {
|
||||
id!: string;
|
||||
ownerId!: string;
|
||||
@@ -92,7 +97,9 @@ export class AlbumResponseDto {
|
||||
updatedAt!: Date;
|
||||
albumThumbnailAssetId!: string | null;
|
||||
shared!: boolean;
|
||||
@ApiProperty({ deprecated: true, description: 'Deprecated in favor of albumPermissions' })
|
||||
sharedUsers!: UserResponseDto[];
|
||||
albumPermissions!: AlbumPermissionResponseDto[];
|
||||
hasSharedLink!: boolean;
|
||||
assets!: AssetResponseDto[];
|
||||
owner!: UserResponseDto;
|
||||
@@ -109,10 +116,15 @@ export class AlbumResponseDto {
|
||||
|
||||
export const mapAlbum = (entity: AlbumEntity, withAssets: boolean, auth?: AuthDto): AlbumResponseDto => {
|
||||
const sharedUsers: UserResponseDto[] = [];
|
||||
const albumPermissions: AlbumPermissionResponseDto[] = [];
|
||||
|
||||
if (entity.sharedUsers) {
|
||||
for (const user of entity.sharedUsers) {
|
||||
sharedUsers.push(mapUser(user));
|
||||
if (entity.albumPermissions) {
|
||||
for (const permission of entity.albumPermissions) {
|
||||
sharedUsers.push(mapUser(permission.users));
|
||||
albumPermissions.push({
|
||||
user: mapUser(permission.users),
|
||||
readonly: permission.readonly,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,6 +150,7 @@ export const mapAlbum = (entity: AlbumEntity, withAssets: boolean, auth?: AuthDt
|
||||
ownerId: entity.ownerId,
|
||||
owner: mapUser(entity.owner),
|
||||
sharedUsers,
|
||||
albumPermissions,
|
||||
shared: hasSharedUser || hasSharedLink,
|
||||
hasSharedLink,
|
||||
startDate,
|
||||
|
||||
@@ -53,13 +53,6 @@ export class AlbumEntity {
|
||||
@Column({ comment: 'Asset ID to be used as thumbnail', nullable: true })
|
||||
albumThumbnailAssetId!: string | null;
|
||||
|
||||
@ManyToMany(() => UserEntity)
|
||||
@JoinTable({
|
||||
name: 'albums_shared_users_users',
|
||||
synchronize: false, // Table is managed by AlbumPermissionEntity
|
||||
})
|
||||
sharedUsers!: UserEntity[];
|
||||
|
||||
@OneToMany(() => AlbumPermissionEntity, (permission) => permission.albums)
|
||||
albumPermissions!: AlbumPermissionEntity[];
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { AssetEntity } from 'src/entities/asset.entity';
|
||||
import { AlbumAsset, AlbumAssetCount, AlbumInfoOptions, IAlbumRepository } from 'src/interfaces/album.interface';
|
||||
import { Instrumentation } from 'src/utils/instrumentation';
|
||||
import { setUnion } from 'src/utils/set';
|
||||
import { DataSource, FindOptionsOrder, FindOptionsRelations, In, IsNull, Not, Repository } from 'typeorm';
|
||||
import { DataSource, Equal, FindOptionsOrder, FindOptionsRelations, In, IsNull, Not, Repository } from 'typeorm';
|
||||
|
||||
@Instrumentation()
|
||||
@Injectable()
|
||||
@@ -23,7 +23,7 @@ export class AlbumRepository implements IAlbumRepository {
|
||||
getById(id: string, options: AlbumInfoOptions): Promise<AlbumEntity | null> {
|
||||
const relations: FindOptionsRelations<AlbumEntity> = {
|
||||
owner: true,
|
||||
sharedUsers: true,
|
||||
albumPermissions: { users: true },
|
||||
assets: false,
|
||||
sharedLinks: true,
|
||||
};
|
||||
@@ -52,7 +52,7 @@ export class AlbumRepository implements IAlbumRepository {
|
||||
},
|
||||
relations: {
|
||||
owner: true,
|
||||
sharedUsers: true,
|
||||
albumPermissions: { users: true },
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -62,9 +62,9 @@ export class AlbumRepository implements IAlbumRepository {
|
||||
return this.repository.find({
|
||||
where: [
|
||||
{ ownerId, assets: { id: assetId } },
|
||||
{ sharedUsers: { id: ownerId }, assets: { id: assetId } },
|
||||
{ albumPermissions: { users: Equal(ownerId) }, assets: { id: assetId } },
|
||||
],
|
||||
relations: { owner: true, sharedUsers: true },
|
||||
relations: { owner: true, albumPermissions: { users: true } },
|
||||
order: { createdAt: 'DESC' },
|
||||
});
|
||||
}
|
||||
@@ -129,7 +129,7 @@ export class AlbumRepository implements IAlbumRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getOwned(ownerId: string): Promise<AlbumEntity[]> {
|
||||
return this.repository.find({
|
||||
relations: { sharedUsers: true, sharedLinks: true, owner: true },
|
||||
relations: { albumPermissions: { users: true }, sharedLinks: true, owner: true },
|
||||
where: { ownerId },
|
||||
order: { createdAt: 'DESC' },
|
||||
});
|
||||
@@ -141,11 +141,11 @@ export class AlbumRepository implements IAlbumRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getShared(ownerId: string): Promise<AlbumEntity[]> {
|
||||
return this.repository.find({
|
||||
relations: { sharedUsers: true, sharedLinks: true, owner: true },
|
||||
relations: { albumPermissions: { users: true }, sharedLinks: true, owner: true },
|
||||
where: [
|
||||
{ sharedUsers: { id: ownerId } },
|
||||
{ albumPermissions: { users: Equal(ownerId) } },
|
||||
{ sharedLinks: { userId: ownerId } },
|
||||
{ ownerId, sharedUsers: { id: Not(IsNull()) } },
|
||||
{ ownerId, albumPermissions: { users: Not(IsNull()) } },
|
||||
],
|
||||
order: { createdAt: 'DESC' },
|
||||
});
|
||||
@@ -157,8 +157,8 @@ export class AlbumRepository implements IAlbumRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getNotShared(ownerId: string): Promise<AlbumEntity[]> {
|
||||
return this.repository.find({
|
||||
relations: { sharedUsers: true, sharedLinks: true, owner: true },
|
||||
where: { ownerId, sharedUsers: { id: IsNull() }, sharedLinks: { id: IsNull() } },
|
||||
relations: { albumPermissions: true, sharedLinks: true, owner: true },
|
||||
where: { ownerId, albumPermissions: { users: IsNull() }, sharedLinks: { id: IsNull() } },
|
||||
order: { createdAt: 'DESC' },
|
||||
});
|
||||
}
|
||||
@@ -282,7 +282,7 @@ export class AlbumRepository implements IAlbumRepository {
|
||||
where: { id },
|
||||
relations: {
|
||||
owner: true,
|
||||
sharedUsers: true,
|
||||
albumPermissions: { users: true },
|
||||
sharedLinks: true,
|
||||
assets: true,
|
||||
},
|
||||
|
||||
@@ -14,9 +14,9 @@ import {
|
||||
} from 'src/dtos/album.dto';
|
||||
import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import { AlbumPermissionEntity } from 'src/entities/album-permission.entity';
|
||||
import { AlbumEntity } from 'src/entities/album.entity';
|
||||
import { AssetEntity } from 'src/entities/asset.entity';
|
||||
import { UserEntity } from 'src/entities/user.entity';
|
||||
import { IAccessRepository } from 'src/interfaces/access.interface';
|
||||
import { AlbumAssetCount, AlbumInfoOptions, IAlbumRepository } from 'src/interfaces/album.interface';
|
||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
||||
@@ -123,7 +123,8 @@ export class AlbumService {
|
||||
ownerId: auth.user.id,
|
||||
albumName: dto.albumName,
|
||||
description: dto.description,
|
||||
sharedUsers: dto.sharedWithUserIds?.map((value) => ({ id: value }) as UserEntity) ?? [],
|
||||
albumPermissions:
|
||||
dto.sharedWithUserIds?.map((userId) => ({ users: { id: userId } }) as AlbumPermissionEntity) ?? [],
|
||||
assets: (dto.assetIds || []).map((id) => ({ id }) as AssetEntity),
|
||||
albumThumbnailAssetId: dto.assetIds?.[0] || null,
|
||||
});
|
||||
@@ -216,7 +217,7 @@ export class AlbumService {
|
||||
throw new BadRequestException('Cannot be shared with owner');
|
||||
}
|
||||
|
||||
const exists = album.sharedUsers.find((user) => user.id === userId);
|
||||
const exists = album.albumPermissions.find(({ users: { id } }) => id === userId);
|
||||
if (exists) {
|
||||
throw new BadRequestException('User already added');
|
||||
}
|
||||
@@ -226,14 +227,14 @@ export class AlbumService {
|
||||
throw new BadRequestException('User not found');
|
||||
}
|
||||
|
||||
album.sharedUsers.push({ id: userId } as UserEntity);
|
||||
album.albumPermissions.push({ users: { id: userId } } as AlbumPermissionEntity);
|
||||
}
|
||||
|
||||
return this.albumRepository
|
||||
.update({
|
||||
id: album.id,
|
||||
updatedAt: new Date(),
|
||||
sharedUsers: album.sharedUsers,
|
||||
albumPermissions: album.albumPermissions,
|
||||
})
|
||||
.then(mapAlbumWithoutAssets);
|
||||
}
|
||||
@@ -249,7 +250,7 @@ export class AlbumService {
|
||||
throw new BadRequestException('Cannot remove album owner');
|
||||
}
|
||||
|
||||
const exists = album.sharedUsers.find((user) => user.id === userId);
|
||||
const exists = album.albumPermissions.find(({ users: { id } }) => id === userId);
|
||||
if (!exists) {
|
||||
throw new BadRequestException('Album not shared with user');
|
||||
}
|
||||
@@ -262,7 +263,7 @@ export class AlbumService {
|
||||
await this.albumRepository.update({
|
||||
id: album.id,
|
||||
updatedAt: new Date(),
|
||||
sharedUsers: album.sharedUsers.filter((user) => user.id !== userId),
|
||||
albumPermissions: album.albumPermissions.filter(({ users: { id } }) => id !== userId),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user