diff --git a/server/src/dtos/album.dto.ts b/server/src/dtos/album.dto.ts index 82db90c9c0..6649340cab 100644 --- a/server/src/dtos/album.dto.ts +++ b/server/src/dtos/album.dto.ts @@ -125,9 +125,9 @@ export const mapAlbum = (entity: AlbumEntity, withAssets: boolean, auth?: AuthDt if (entity.sharedUsers) { for (const permission of entity.sharedUsers) { - sharedUsers.push(mapUser(permission.users)); + sharedUsers.push(mapUser(permission.user)); sharedUsersV2.push({ - user: mapUser(permission.users), + user: mapUser(permission.user), readonly: permission.readonly, }); } diff --git a/server/src/entities/album-user.entity.ts b/server/src/entities/album-user.entity.ts index 87cff2e2a5..7a41f2ee7f 100644 --- a/server/src/entities/album-user.entity.ts +++ b/server/src/entities/album-user.entity.ts @@ -1,20 +1,22 @@ import { AlbumEntity } from 'src/entities/album.entity'; import { UserEntity } from 'src/entities/user.entity'; -import { Column, Entity, Index, ManyToOne, PrimaryColumn } from 'typeorm'; +import { Column, Entity, Index, JoinColumn, ManyToOne, PrimaryColumn } from 'typeorm'; @Entity('albums_shared_users_users') -// Indices for JoinTable -@Index('IDX_427c350ad49bd3935a50baab73', ['albums']) -@Index('IDX_f48513bf9bccefd6ff3ad30bd0', ['users']) +// Pre-existing indices from original album <--> user ManyToMany mapping +@Index('IDX_427c350ad49bd3935a50baab73', ['album']) +@Index('IDX_f48513bf9bccefd6ff3ad30bd0', ['user']) export class AlbumUserEntity { @PrimaryColumn({ type: 'uuid', name: 'albumsId' }) + @JoinColumn({ name: 'albumsId' }) @ManyToOne(() => AlbumEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false }) - albums!: AlbumEntity; + album!: AlbumEntity; @PrimaryColumn({ type: 'uuid', name: 'usersId' }) + @JoinColumn({ name: 'usersId' }) @ManyToOne(() => UserEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false }) - users!: UserEntity; + user!: UserEntity; - @Column({ default: true }) + @Column({ default: false }) readonly!: boolean; } diff --git a/server/src/entities/album.entity.ts b/server/src/entities/album.entity.ts index ce8492e6b4..da362e13c2 100644 --- a/server/src/entities/album.entity.ts +++ b/server/src/entities/album.entity.ts @@ -53,7 +53,7 @@ export class AlbumEntity { @Column({ comment: 'Asset ID to be used as thumbnail', nullable: true }) albumThumbnailAssetId!: string | null; - @OneToMany(() => AlbumUserEntity, (permission) => permission.albums) + @OneToMany(() => AlbumUserEntity, (permission) => permission.album) sharedUsers!: AlbumUserEntity[]; @ManyToMany(() => AssetEntity, (asset) => asset.albums) diff --git a/server/src/migrations/1712905931092-SetReadonlyDefault.ts b/server/src/migrations/1712905931092-SetReadonlyDefault.ts deleted file mode 100644 index 821b1ca196..0000000000 --- a/server/src/migrations/1712905931092-SetReadonlyDefault.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class SetReadonlyDefault1712905931092 implements MigrationInterface { - name = 'SetReadonlyDefault1712905931092' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ALTER COLUMN "readonly" SET DEFAULT true`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ALTER COLUMN "readonly" SET DEFAULT false`); - } - -} diff --git a/server/src/migrations/1712905775156-AddAlbumPermission.ts b/server/src/migrations/1713298646379-AddAlbumUserReadonly.ts similarity index 63% rename from server/src/migrations/1712905775156-AddAlbumPermission.ts rename to server/src/migrations/1713298646379-AddAlbumUserReadonly.ts index 0e3012a5fe..bf42aa3048 100644 --- a/server/src/migrations/1712905775156-AddAlbumPermission.ts +++ b/server/src/migrations/1713298646379-AddAlbumUserReadonly.ts @@ -1,10 +1,11 @@ import { MigrationInterface, QueryRunner } from "typeorm"; -export class AddAlbumPermission1712905775156 implements MigrationInterface { - name = 'AddAlbumPermission1712905775156' +export class AddAlbumUserReadonly1713298646379 implements MigrationInterface { + name = 'AddAlbumUserReadonly1713298646379' public async up(queryRunner: QueryRunner): Promise { await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD "readonly" boolean NOT NULL DEFAULT false`); + await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ALTER COLUMN "readonly" SET DEFAULT true`); } public async down(queryRunner: QueryRunner): Promise { diff --git a/server/src/repositories/access.repository.ts b/server/src/repositories/access.repository.ts index 84cf7083c3..e3669ee3e0 100644 --- a/server/src/repositories/access.repository.ts +++ b/server/src/repositories/access.repository.ts @@ -132,7 +132,7 @@ class AlbumAccess implements IAlbumAccess { where: { id: In([...albumIds]), sharedUsers: { - users: Equal(userId), + user: Equal(userId), // If write is needed we check for it, otherwise both are accepted readonly: readWrite === 'write' ? false : undefined, }, diff --git a/server/src/repositories/album-user.repository.ts b/server/src/repositories/album-user.repository.ts index 054f95cf46..90398b4ce0 100644 --- a/server/src/repositories/album-user.repository.ts +++ b/server/src/repositories/album-user.repository.ts @@ -3,7 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm'; import { AlbumUserEntity } from 'src/entities/album-user.entity'; import { AlbumPermissionId, IAlbumUserRepository } from 'src/interfaces/album-user.interface'; import { Instrumentation } from 'src/utils/instrumentation'; -import { Equal, Repository } from 'typeorm'; +import { Repository } from 'typeorm'; @Instrumentation() @Injectable() @@ -11,21 +11,19 @@ export class AlbumUserRepository implements IAlbumUserRepository { constructor(@InjectRepository(AlbumUserEntity) private repository: Repository) {} async create(dto: Partial): Promise { - const { users, albums } = await this.repository.save(dto); - return this.repository.findOneOrFail({ where: { users, albums }, relations: { users: true } }); + const { user, album } = await this.repository.save(dto); + return this.repository.findOneOrFail({ where: { user, album }, relations: { user: true } }); } async update({ userId, albumId }: AlbumPermissionId, dto: Partial): Promise { - // @ts-expect-error I'm pretty sure I messed something up with the entity because - // if I follow what typescript says I get postgres errors - await this.repository.update({ users: userId, albums: albumId }, dto); + await this.repository.update({ user: { id: userId }, album: { id: albumId } }, dto); return this.repository.findOneOrFail({ - where: { users: Equal(userId), albums: Equal(albumId) }, - relations: { users: true }, + where: { user: { id: userId }, album: { id: albumId } }, + relations: { user: true }, }); } async delete({ userId, albumId }: AlbumPermissionId): Promise { - await this.repository.delete({ users: { id: userId }, albums: { id: albumId } }); + await this.repository.delete({ user: { id: userId }, album: { id: albumId } }); } } diff --git a/server/src/repositories/album.repository.ts b/server/src/repositories/album.repository.ts index eebe4ea65d..d6552383aa 100644 --- a/server/src/repositories/album.repository.ts +++ b/server/src/repositories/album.repository.ts @@ -23,7 +23,7 @@ export class AlbumRepository implements IAlbumRepository { getById(id: string, options: AlbumInfoOptions): Promise { const relations: FindOptionsRelations = { owner: true, - sharedUsers: { users: true }, + sharedUsers: { user: true }, assets: false, sharedLinks: true, }; @@ -52,7 +52,7 @@ export class AlbumRepository implements IAlbumRepository { }, relations: { owner: true, - sharedUsers: { users: true }, + sharedUsers: { user: true }, }, }); } @@ -62,9 +62,9 @@ export class AlbumRepository implements IAlbumRepository { return this.repository.find({ where: [ { ownerId, assets: { id: assetId } }, - { sharedUsers: { users: Equal(ownerId) }, assets: { id: assetId } }, + { sharedUsers: { user: Equal(ownerId) }, assets: { id: assetId } }, ], - relations: { owner: true, sharedUsers: { users: true } }, + relations: { owner: true, sharedUsers: { user: true } }, order: { createdAt: 'DESC' }, }); } @@ -129,7 +129,7 @@ export class AlbumRepository implements IAlbumRepository { @GenerateSql({ params: [DummyValue.UUID] }) getOwned(ownerId: string): Promise { return this.repository.find({ - relations: { sharedUsers: { users: true }, sharedLinks: true, owner: true }, + relations: { sharedUsers: { user: 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 { return this.repository.find({ - relations: { sharedUsers: { users: true }, sharedLinks: true, owner: true }, + relations: { sharedUsers: { user: true }, sharedLinks: true, owner: true }, where: [ - { sharedUsers: { users: Equal(ownerId) } }, + { sharedUsers: { user: Equal(ownerId) } }, { sharedLinks: { userId: ownerId } }, - { ownerId, sharedUsers: { users: Not(IsNull()) } }, + { ownerId, sharedUsers: { user: Not(IsNull()) } }, ], order: { createdAt: 'DESC' }, }); @@ -158,7 +158,7 @@ export class AlbumRepository implements IAlbumRepository { getNotShared(ownerId: string): Promise { return this.repository.find({ relations: { sharedUsers: true, sharedLinks: true, owner: true }, - where: { ownerId, sharedUsers: { users: IsNull() }, sharedLinks: { id: IsNull() } }, + where: { ownerId, sharedUsers: { user: IsNull() }, sharedLinks: { id: IsNull() } }, order: { createdAt: 'DESC' }, }); } @@ -282,7 +282,7 @@ export class AlbumRepository implements IAlbumRepository { where: { id }, relations: { owner: true, - sharedUsers: { users: true }, + sharedUsers: { user: true }, sharedLinks: true, assets: true, }, diff --git a/server/src/services/album.service.ts b/server/src/services/album.service.ts index c45abd8dc5..932f7fc110 100644 --- a/server/src/services/album.service.ts +++ b/server/src/services/album.service.ts @@ -128,7 +128,7 @@ export class AlbumService { ownerId: auth.user.id, albumName: dto.albumName, description: dto.description, - sharedUsers: dto.sharedWithUserIds?.map((userId) => ({ users: { id: userId } }) as AlbumUserEntity) ?? [], + sharedUsers: dto.sharedWithUserIds?.map((userId) => ({ user: { id: userId } }) as AlbumUserEntity) ?? [], assets, albumThumbnailAssetId: assets[0]?.id || null, }); @@ -221,7 +221,7 @@ export class AlbumService { throw new BadRequestException('Cannot be shared with owner'); } - const exists = album.sharedUsers.find(({ users: { id } }) => id === userId); + const exists = album.sharedUsers.find(({ user: { id } }) => id === userId); if (exists) { throw new BadRequestException('User already added'); } @@ -232,7 +232,7 @@ export class AlbumService { } album.sharedUsers.push( - await this.albumPermissionRepository.create({ users: { id: userId }, albums: { id } } as AlbumUserEntity), + await this.albumPermissionRepository.create({ user: { id: userId }, album: { id } } as AlbumUserEntity), ); } @@ -250,7 +250,7 @@ export class AlbumService { throw new BadRequestException('Cannot remove album owner'); } - const exists = album.sharedUsers.find(({ users: { id } }) => id === userId); + const exists = album.sharedUsers.find(({ user: { id } }) => id === userId); if (!exists) { throw new BadRequestException('Album not shared with user'); } @@ -268,7 +268,7 @@ export class AlbumService { const album = await this.findOrFail(id, { withAssets: false }); - const permission = album.sharedUsers.find(({ users: { id } }) => id === userId); + const permission = album.sharedUsers.find(({ user: { id } }) => id === userId); if (!permission) { throw new BadRequestException('Album not shared with user'); }