fix db schema annotations
This commit is contained in:
@@ -125,9 +125,9 @@ export const mapAlbum = (entity: AlbumEntity, withAssets: boolean, auth?: AuthDt
|
|||||||
|
|
||||||
if (entity.sharedUsers) {
|
if (entity.sharedUsers) {
|
||||||
for (const permission of entity.sharedUsers) {
|
for (const permission of entity.sharedUsers) {
|
||||||
sharedUsers.push(mapUser(permission.users));
|
sharedUsers.push(mapUser(permission.user));
|
||||||
sharedUsersV2.push({
|
sharedUsersV2.push({
|
||||||
user: mapUser(permission.users),
|
user: mapUser(permission.user),
|
||||||
readonly: permission.readonly,
|
readonly: permission.readonly,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,22 @@
|
|||||||
import { AlbumEntity } from 'src/entities/album.entity';
|
import { AlbumEntity } from 'src/entities/album.entity';
|
||||||
import { UserEntity } from 'src/entities/user.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')
|
@Entity('albums_shared_users_users')
|
||||||
// Indices for JoinTable
|
// Pre-existing indices from original album <--> user ManyToMany mapping
|
||||||
@Index('IDX_427c350ad49bd3935a50baab73', ['albums'])
|
@Index('IDX_427c350ad49bd3935a50baab73', ['album'])
|
||||||
@Index('IDX_f48513bf9bccefd6ff3ad30bd0', ['users'])
|
@Index('IDX_f48513bf9bccefd6ff3ad30bd0', ['user'])
|
||||||
export class AlbumUserEntity {
|
export class AlbumUserEntity {
|
||||||
@PrimaryColumn({ type: 'uuid', name: 'albumsId' })
|
@PrimaryColumn({ type: 'uuid', name: 'albumsId' })
|
||||||
|
@JoinColumn({ name: 'albumsId' })
|
||||||
@ManyToOne(() => AlbumEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false })
|
@ManyToOne(() => AlbumEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false })
|
||||||
albums!: AlbumEntity;
|
album!: AlbumEntity;
|
||||||
|
|
||||||
@PrimaryColumn({ type: 'uuid', name: 'usersId' })
|
@PrimaryColumn({ type: 'uuid', name: 'usersId' })
|
||||||
|
@JoinColumn({ name: 'usersId' })
|
||||||
@ManyToOne(() => UserEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false })
|
@ManyToOne(() => UserEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false })
|
||||||
users!: UserEntity;
|
user!: UserEntity;
|
||||||
|
|
||||||
@Column({ default: true })
|
@Column({ default: false })
|
||||||
readonly!: boolean;
|
readonly!: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export class AlbumEntity {
|
|||||||
@Column({ comment: 'Asset ID to be used as thumbnail', nullable: true })
|
@Column({ comment: 'Asset ID to be used as thumbnail', nullable: true })
|
||||||
albumThumbnailAssetId!: string | null;
|
albumThumbnailAssetId!: string | null;
|
||||||
|
|
||||||
@OneToMany(() => AlbumUserEntity, (permission) => permission.albums)
|
@OneToMany(() => AlbumUserEntity, (permission) => permission.album)
|
||||||
sharedUsers!: AlbumUserEntity[];
|
sharedUsers!: AlbumUserEntity[];
|
||||||
|
|
||||||
@ManyToMany(() => AssetEntity, (asset) => asset.albums)
|
@ManyToMany(() => AssetEntity, (asset) => asset.albums)
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
||||||
|
|
||||||
export class SetReadonlyDefault1712905931092 implements MigrationInterface {
|
|
||||||
name = 'SetReadonlyDefault1712905931092'
|
|
||||||
|
|
||||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
||||||
await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ALTER COLUMN "readonly" SET DEFAULT true`);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
||||||
await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ALTER COLUMN "readonly" SET DEFAULT false`);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
+3
-2
@@ -1,10 +1,11 @@
|
|||||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
export class AddAlbumPermission1712905775156 implements MigrationInterface {
|
export class AddAlbumUserReadonly1713298646379 implements MigrationInterface {
|
||||||
name = 'AddAlbumPermission1712905775156'
|
name = 'AddAlbumUserReadonly1713298646379'
|
||||||
|
|
||||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
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" 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<void> {
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
@@ -132,7 +132,7 @@ class AlbumAccess implements IAlbumAccess {
|
|||||||
where: {
|
where: {
|
||||||
id: In([...albumIds]),
|
id: In([...albumIds]),
|
||||||
sharedUsers: {
|
sharedUsers: {
|
||||||
users: Equal(userId),
|
user: Equal(userId),
|
||||||
// If write is needed we check for it, otherwise both are accepted
|
// If write is needed we check for it, otherwise both are accepted
|
||||||
readonly: readWrite === 'write' ? false : undefined,
|
readonly: readWrite === 'write' ? false : undefined,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm';
|
|||||||
import { AlbumUserEntity } from 'src/entities/album-user.entity';
|
import { AlbumUserEntity } from 'src/entities/album-user.entity';
|
||||||
import { AlbumPermissionId, IAlbumUserRepository } from 'src/interfaces/album-user.interface';
|
import { AlbumPermissionId, IAlbumUserRepository } from 'src/interfaces/album-user.interface';
|
||||||
import { Instrumentation } from 'src/utils/instrumentation';
|
import { Instrumentation } from 'src/utils/instrumentation';
|
||||||
import { Equal, Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
@Instrumentation()
|
@Instrumentation()
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -11,21 +11,19 @@ export class AlbumUserRepository implements IAlbumUserRepository {
|
|||||||
constructor(@InjectRepository(AlbumUserEntity) private repository: Repository<AlbumUserEntity>) {}
|
constructor(@InjectRepository(AlbumUserEntity) private repository: Repository<AlbumUserEntity>) {}
|
||||||
|
|
||||||
async create(dto: Partial<AlbumUserEntity>): Promise<AlbumUserEntity> {
|
async create(dto: Partial<AlbumUserEntity>): Promise<AlbumUserEntity> {
|
||||||
const { users, albums } = await this.repository.save(dto);
|
const { user, album } = await this.repository.save(dto);
|
||||||
return this.repository.findOneOrFail({ where: { users, albums }, relations: { users: true } });
|
return this.repository.findOneOrFail({ where: { user, album }, relations: { user: true } });
|
||||||
}
|
}
|
||||||
|
|
||||||
async update({ userId, albumId }: AlbumPermissionId, dto: Partial<AlbumUserEntity>): Promise<AlbumUserEntity> {
|
async update({ userId, albumId }: AlbumPermissionId, dto: Partial<AlbumUserEntity>): Promise<AlbumUserEntity> {
|
||||||
// @ts-expect-error I'm pretty sure I messed something up with the entity because
|
await this.repository.update({ user: { id: userId }, album: { id: albumId } }, dto);
|
||||||
// if I follow what typescript says I get postgres errors
|
|
||||||
await this.repository.update({ users: userId, albums: albumId }, dto);
|
|
||||||
return this.repository.findOneOrFail({
|
return this.repository.findOneOrFail({
|
||||||
where: { users: Equal(userId), albums: Equal(albumId) },
|
where: { user: { id: userId }, album: { id: albumId } },
|
||||||
relations: { users: true },
|
relations: { user: true },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete({ userId, albumId }: AlbumPermissionId): Promise<void> {
|
async delete({ userId, albumId }: AlbumPermissionId): Promise<void> {
|
||||||
await this.repository.delete({ users: { id: userId }, albums: { id: albumId } });
|
await this.repository.delete({ user: { id: userId }, album: { id: albumId } });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export class AlbumRepository implements IAlbumRepository {
|
|||||||
getById(id: string, options: AlbumInfoOptions): Promise<AlbumEntity | null> {
|
getById(id: string, options: AlbumInfoOptions): Promise<AlbumEntity | null> {
|
||||||
const relations: FindOptionsRelations<AlbumEntity> = {
|
const relations: FindOptionsRelations<AlbumEntity> = {
|
||||||
owner: true,
|
owner: true,
|
||||||
sharedUsers: { users: true },
|
sharedUsers: { user: true },
|
||||||
assets: false,
|
assets: false,
|
||||||
sharedLinks: true,
|
sharedLinks: true,
|
||||||
};
|
};
|
||||||
@@ -52,7 +52,7 @@ export class AlbumRepository implements IAlbumRepository {
|
|||||||
},
|
},
|
||||||
relations: {
|
relations: {
|
||||||
owner: true,
|
owner: true,
|
||||||
sharedUsers: { users: true },
|
sharedUsers: { user: true },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -62,9 +62,9 @@ export class AlbumRepository implements IAlbumRepository {
|
|||||||
return this.repository.find({
|
return this.repository.find({
|
||||||
where: [
|
where: [
|
||||||
{ ownerId, assets: { id: assetId } },
|
{ 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' },
|
order: { createdAt: 'DESC' },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -129,7 +129,7 @@ export class AlbumRepository implements IAlbumRepository {
|
|||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
getOwned(ownerId: string): Promise<AlbumEntity[]> {
|
getOwned(ownerId: string): Promise<AlbumEntity[]> {
|
||||||
return this.repository.find({
|
return this.repository.find({
|
||||||
relations: { sharedUsers: { users: true }, sharedLinks: true, owner: true },
|
relations: { sharedUsers: { user: true }, sharedLinks: true, owner: true },
|
||||||
where: { ownerId },
|
where: { ownerId },
|
||||||
order: { createdAt: 'DESC' },
|
order: { createdAt: 'DESC' },
|
||||||
});
|
});
|
||||||
@@ -141,11 +141,11 @@ export class AlbumRepository implements IAlbumRepository {
|
|||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
getShared(ownerId: string): Promise<AlbumEntity[]> {
|
getShared(ownerId: string): Promise<AlbumEntity[]> {
|
||||||
return this.repository.find({
|
return this.repository.find({
|
||||||
relations: { sharedUsers: { users: true }, sharedLinks: true, owner: true },
|
relations: { sharedUsers: { user: true }, sharedLinks: true, owner: true },
|
||||||
where: [
|
where: [
|
||||||
{ sharedUsers: { users: Equal(ownerId) } },
|
{ sharedUsers: { user: Equal(ownerId) } },
|
||||||
{ sharedLinks: { userId: ownerId } },
|
{ sharedLinks: { userId: ownerId } },
|
||||||
{ ownerId, sharedUsers: { users: Not(IsNull()) } },
|
{ ownerId, sharedUsers: { user: Not(IsNull()) } },
|
||||||
],
|
],
|
||||||
order: { createdAt: 'DESC' },
|
order: { createdAt: 'DESC' },
|
||||||
});
|
});
|
||||||
@@ -158,7 +158,7 @@ export class AlbumRepository implements IAlbumRepository {
|
|||||||
getNotShared(ownerId: string): Promise<AlbumEntity[]> {
|
getNotShared(ownerId: string): Promise<AlbumEntity[]> {
|
||||||
return this.repository.find({
|
return this.repository.find({
|
||||||
relations: { sharedUsers: true, sharedLinks: true, owner: true },
|
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' },
|
order: { createdAt: 'DESC' },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -282,7 +282,7 @@ export class AlbumRepository implements IAlbumRepository {
|
|||||||
where: { id },
|
where: { id },
|
||||||
relations: {
|
relations: {
|
||||||
owner: true,
|
owner: true,
|
||||||
sharedUsers: { users: true },
|
sharedUsers: { user: true },
|
||||||
sharedLinks: true,
|
sharedLinks: true,
|
||||||
assets: true,
|
assets: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ export class AlbumService {
|
|||||||
ownerId: auth.user.id,
|
ownerId: auth.user.id,
|
||||||
albumName: dto.albumName,
|
albumName: dto.albumName,
|
||||||
description: dto.description,
|
description: dto.description,
|
||||||
sharedUsers: dto.sharedWithUserIds?.map((userId) => ({ users: { id: userId } }) as AlbumUserEntity) ?? [],
|
sharedUsers: dto.sharedWithUserIds?.map((userId) => ({ user: { id: userId } }) as AlbumUserEntity) ?? [],
|
||||||
assets,
|
assets,
|
||||||
albumThumbnailAssetId: assets[0]?.id || null,
|
albumThumbnailAssetId: assets[0]?.id || null,
|
||||||
});
|
});
|
||||||
@@ -221,7 +221,7 @@ export class AlbumService {
|
|||||||
throw new BadRequestException('Cannot be shared with owner');
|
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) {
|
if (exists) {
|
||||||
throw new BadRequestException('User already added');
|
throw new BadRequestException('User already added');
|
||||||
}
|
}
|
||||||
@@ -232,7 +232,7 @@ export class AlbumService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
album.sharedUsers.push(
|
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');
|
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) {
|
if (!exists) {
|
||||||
throw new BadRequestException('Album not shared with user');
|
throw new BadRequestException('Album not shared with user');
|
||||||
}
|
}
|
||||||
@@ -268,7 +268,7 @@ export class AlbumService {
|
|||||||
|
|
||||||
const album = await this.findOrFail(id, { withAssets: false });
|
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) {
|
if (!permission) {
|
||||||
throw new BadRequestException('Album not shared with user');
|
throw new BadRequestException('Album not shared with user');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user