mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
infra(server): fix Album TypeORM relations and change ids to uuids (#1582)
* infra: make api-key primary key column a UUID * infra: move ManyToMany relations in album entity, make ownerId ManyToOne --------- Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
parent
917f1dea9f
commit
000d0a08f4
24 changed files with 368 additions and 461 deletions
|
|
@ -21,11 +21,9 @@ export class AlbumResponseDto {
|
|||
export function mapAlbum(entity: AlbumEntity): AlbumResponseDto {
|
||||
const sharedUsers: UserResponseDto[] = [];
|
||||
|
||||
entity.sharedUsers?.forEach((userAlbum) => {
|
||||
if (userAlbum.userInfo) {
|
||||
const user = mapUser(userAlbum.userInfo);
|
||||
sharedUsers.push(user);
|
||||
}
|
||||
entity.sharedUsers?.forEach((user) => {
|
||||
const userDto = mapUser(user);
|
||||
sharedUsers.push(userDto);
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
@ -38,7 +36,7 @@ export function mapAlbum(entity: AlbumEntity): AlbumResponseDto {
|
|||
owner: mapUser(entity.owner),
|
||||
sharedUsers,
|
||||
shared: sharedUsers.length > 0 || entity.sharedLinks?.length > 0,
|
||||
assets: entity.assets?.map((assetAlbum) => mapAsset(assetAlbum.assetInfo)) || [],
|
||||
assets: entity.assets?.map((asset) => mapAsset(asset)) || [],
|
||||
assetCount: entity.assets?.length || 0,
|
||||
};
|
||||
}
|
||||
|
|
@ -46,11 +44,9 @@ export function mapAlbum(entity: AlbumEntity): AlbumResponseDto {
|
|||
export function mapAlbumExcludeAssetInfo(entity: AlbumEntity): AlbumResponseDto {
|
||||
const sharedUsers: UserResponseDto[] = [];
|
||||
|
||||
entity.sharedUsers?.forEach((userAlbum) => {
|
||||
if (userAlbum.userInfo) {
|
||||
const user = mapUser(userAlbum.userInfo);
|
||||
sharedUsers.push(user);
|
||||
}
|
||||
entity.sharedUsers?.forEach((user) => {
|
||||
const userDto = mapUser(user);
|
||||
sharedUsers.push(userDto);
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ export const IKeyRepository = 'IKeyRepository';
|
|||
|
||||
export interface IKeyRepository {
|
||||
create(dto: Partial<APIKeyEntity>): Promise<APIKeyEntity>;
|
||||
update(userId: string, id: number, dto: Partial<APIKeyEntity>): Promise<APIKeyEntity>;
|
||||
delete(userId: string, id: number): Promise<void>;
|
||||
update(userId: string, id: string, dto: Partial<APIKeyEntity>): Promise<APIKeyEntity>;
|
||||
delete(userId: string, id: string): Promise<void>;
|
||||
/**
|
||||
* Includes the hashed `key` for verification
|
||||
* @param id
|
||||
*/
|
||||
getKey(hashedToken: string): Promise<APIKeyEntity | null>;
|
||||
getById(userId: string, id: number): Promise<APIKeyEntity | null>;
|
||||
getById(userId: string, id: string): Promise<APIKeyEntity | null>;
|
||||
getByUserId(userId: string): Promise<APIKeyEntity[]>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,17 +47,19 @@ describe(APIKeyService.name, () => {
|
|||
it('should throw an error if the key is not found', async () => {
|
||||
keyMock.getById.mockResolvedValue(null);
|
||||
|
||||
await expect(sut.update(authStub.admin, 1, { name: 'New Name' })).rejects.toBeInstanceOf(BadRequestException);
|
||||
await expect(sut.update(authStub.admin, 'random-guid', { name: 'New Name' })).rejects.toBeInstanceOf(
|
||||
BadRequestException,
|
||||
);
|
||||
|
||||
expect(keyMock.update).not.toHaveBeenCalledWith(1);
|
||||
expect(keyMock.update).not.toHaveBeenCalledWith('random-guid');
|
||||
});
|
||||
|
||||
it('should update a key', async () => {
|
||||
keyMock.getById.mockResolvedValue(keyStub.admin);
|
||||
|
||||
await sut.update(authStub.admin, 1, { name: 'New Name' });
|
||||
await sut.update(authStub.admin, 'random-guid', { name: 'New Name' });
|
||||
|
||||
expect(keyMock.update).toHaveBeenCalledWith(authStub.admin.id, 1, { name: 'New Name' });
|
||||
expect(keyMock.update).toHaveBeenCalledWith(authStub.admin.id, 'random-guid', { name: 'New Name' });
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -65,17 +67,17 @@ describe(APIKeyService.name, () => {
|
|||
it('should throw an error if the key is not found', async () => {
|
||||
keyMock.getById.mockResolvedValue(null);
|
||||
|
||||
await expect(sut.delete(authStub.admin, 1)).rejects.toBeInstanceOf(BadRequestException);
|
||||
await expect(sut.delete(authStub.admin, 'random-guid')).rejects.toBeInstanceOf(BadRequestException);
|
||||
|
||||
expect(keyMock.delete).not.toHaveBeenCalledWith(1);
|
||||
expect(keyMock.delete).not.toHaveBeenCalledWith('random-guid');
|
||||
});
|
||||
|
||||
it('should delete a key', async () => {
|
||||
keyMock.getById.mockResolvedValue(keyStub.admin);
|
||||
|
||||
await sut.delete(authStub.admin, 1);
|
||||
await sut.delete(authStub.admin, 'random-guid');
|
||||
|
||||
expect(keyMock.delete).toHaveBeenCalledWith(authStub.admin.id, 1);
|
||||
expect(keyMock.delete).toHaveBeenCalledWith(authStub.admin.id, 'random-guid');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -83,17 +85,17 @@ describe(APIKeyService.name, () => {
|
|||
it('should throw an error if the key is not found', async () => {
|
||||
keyMock.getById.mockResolvedValue(null);
|
||||
|
||||
await expect(sut.getById(authStub.admin, 1)).rejects.toBeInstanceOf(BadRequestException);
|
||||
await expect(sut.getById(authStub.admin, 'random-guid')).rejects.toBeInstanceOf(BadRequestException);
|
||||
|
||||
expect(keyMock.getById).toHaveBeenCalledWith(authStub.admin.id, 1);
|
||||
expect(keyMock.getById).toHaveBeenCalledWith(authStub.admin.id, 'random-guid');
|
||||
});
|
||||
|
||||
it('should get a key by id', async () => {
|
||||
keyMock.getById.mockResolvedValue(keyStub.admin);
|
||||
|
||||
await sut.getById(authStub.admin, 1);
|
||||
await sut.getById(authStub.admin, 'random-guid');
|
||||
|
||||
expect(keyMock.getById).toHaveBeenCalledWith(authStub.admin.id, 1);
|
||||
expect(keyMock.getById).toHaveBeenCalledWith(authStub.admin.id, 'random-guid');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export class APIKeyService {
|
|||
return { secret, apiKey: mapKey(entity) };
|
||||
}
|
||||
|
||||
async update(authUser: AuthUserDto, id: number, dto: APIKeyCreateDto): Promise<APIKeyResponseDto> {
|
||||
async update(authUser: AuthUserDto, id: string, dto: APIKeyCreateDto): Promise<APIKeyResponseDto> {
|
||||
const exists = await this.repository.getById(authUser.id, id);
|
||||
if (!exists) {
|
||||
throw new BadRequestException('API Key not found');
|
||||
|
|
@ -35,7 +35,7 @@ export class APIKeyService {
|
|||
});
|
||||
}
|
||||
|
||||
async delete(authUser: AuthUserDto, id: number): Promise<void> {
|
||||
async delete(authUser: AuthUserDto, id: string): Promise<void> {
|
||||
const exists = await this.repository.getById(authUser.id, id);
|
||||
if (!exists) {
|
||||
throw new BadRequestException('API Key not found');
|
||||
|
|
@ -44,7 +44,7 @@ export class APIKeyService {
|
|||
await this.repository.delete(authUser.id, id);
|
||||
}
|
||||
|
||||
async getById(authUser: AuthUserDto, id: number): Promise<APIKeyResponseDto> {
|
||||
async getById(authUser: AuthUserDto, id: string): Promise<APIKeyResponseDto> {
|
||||
const key = await this.repository.getById(authUser.id, id);
|
||||
if (!key) {
|
||||
throw new BadRequestException('API Key not found');
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import { APIKeyEntity } from '@app/infra/db/entities';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class APIKeyResponseDto {
|
||||
@ApiProperty({ type: 'integer' })
|
||||
id!: number;
|
||||
id!: string;
|
||||
name!: string;
|
||||
createdAt!: string;
|
||||
updatedAt!: string;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export class SharedLinkResponseDto {
|
|||
|
||||
export function mapSharedLink(sharedLink: SharedLinkEntity): SharedLinkResponseDto {
|
||||
const linkAssets = sharedLink.assets || [];
|
||||
const albumAssets = (sharedLink?.album?.assets || []).map((albumAsset) => albumAsset.assetInfo);
|
||||
const albumAssets = (sharedLink?.album?.assets || []).map((asset) => asset);
|
||||
|
||||
const assets = _.uniqBy([...linkAssets, ...albumAssets], (asset) => asset.id);
|
||||
|
||||
|
|
@ -45,7 +45,7 @@ export function mapSharedLink(sharedLink: SharedLinkEntity): SharedLinkResponseD
|
|||
|
||||
export function mapSharedLinkWithNoExif(sharedLink: SharedLinkEntity): SharedLinkResponseDto {
|
||||
const linkAssets = sharedLink.assets || [];
|
||||
const albumAssets = (sharedLink?.album?.assets || []).map((albumAsset) => albumAsset.assetInfo);
|
||||
const albumAssets = (sharedLink?.album?.assets || []).map((asset) => asset);
|
||||
|
||||
const assets = _.uniqBy([...linkAssets, ...albumAssets], (asset) => asset.id);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import {
|
||||
APIKeyEntity,
|
||||
AssetEntity,
|
||||
AssetType,
|
||||
SharedLinkEntity,
|
||||
SharedLinkType,
|
||||
|
|
@ -90,6 +91,30 @@ export const userEntityStub = {
|
|||
}),
|
||||
};
|
||||
|
||||
export const assetEntityStub = {
|
||||
image: Object.freeze<AssetEntity>({
|
||||
id: 'asset-id',
|
||||
deviceAssetId: 'device-asset-id',
|
||||
modifiedAt: today.toISOString(),
|
||||
createdAt: today.toISOString(),
|
||||
userId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: '/original/path',
|
||||
resizePath: null,
|
||||
type: AssetType.IMAGE,
|
||||
webpPath: null,
|
||||
encodedVideoPath: null,
|
||||
updatedAt: today.toISOString(),
|
||||
mimeType: null,
|
||||
isFavorite: true,
|
||||
duration: null,
|
||||
isVisible: true,
|
||||
livePhotoVideoId: null,
|
||||
tags: [],
|
||||
sharedLinks: [],
|
||||
}),
|
||||
};
|
||||
|
||||
const assetInfo: ExifResponseDto = {
|
||||
id: 1,
|
||||
make: 'camera-make',
|
||||
|
|
@ -165,7 +190,7 @@ export const userTokenEntityStub = {
|
|||
|
||||
export const keyStub = {
|
||||
admin: Object.freeze({
|
||||
id: 1,
|
||||
id: 'my-random-guid',
|
||||
name: 'My Key',
|
||||
key: 'my-api-key (hashed)',
|
||||
userId: authStub.admin.id,
|
||||
|
|
@ -348,66 +373,60 @@ export const sharedLinkStub = {
|
|||
sharedLinks: [],
|
||||
assets: [
|
||||
{
|
||||
id: 'album-asset-123',
|
||||
albumId: 'album-123',
|
||||
assetId: 'asset-123',
|
||||
albumInfo: {} as any,
|
||||
assetInfo: {
|
||||
id: 'id_1',
|
||||
userId: 'user_id_1',
|
||||
deviceAssetId: 'device_asset_id_1',
|
||||
deviceId: 'device_id_1',
|
||||
type: AssetType.VIDEO,
|
||||
originalPath: 'fake_path/jpeg',
|
||||
resizePath: '',
|
||||
createdAt: today.toISOString(),
|
||||
modifiedAt: today.toISOString(),
|
||||
updatedAt: today.toISOString(),
|
||||
isFavorite: false,
|
||||
mimeType: 'image/jpeg',
|
||||
smartInfo: {
|
||||
id: 'should-be-a-number',
|
||||
assetId: 'id_1',
|
||||
tags: [],
|
||||
objects: ['a', 'b', 'c'],
|
||||
asset: null as any,
|
||||
},
|
||||
webpPath: '',
|
||||
encodedVideoPath: '',
|
||||
duration: null,
|
||||
isVisible: true,
|
||||
livePhotoVideoId: null,
|
||||
exifInfo: {
|
||||
livePhotoCID: null,
|
||||
id: 1,
|
||||
assetId: 'id_1',
|
||||
description: 'description',
|
||||
exifImageWidth: 500,
|
||||
exifImageHeight: 500,
|
||||
fileSizeInByte: 100,
|
||||
orientation: 'orientation',
|
||||
dateTimeOriginal: today,
|
||||
modifyDate: today,
|
||||
latitude: 100,
|
||||
longitude: 100,
|
||||
city: 'city',
|
||||
state: 'state',
|
||||
country: 'country',
|
||||
make: 'camera-make',
|
||||
model: 'camera-model',
|
||||
imageName: 'fancy-image',
|
||||
lensModel: 'fancy',
|
||||
fNumber: 100,
|
||||
focalLength: 100,
|
||||
iso: 100,
|
||||
exposureTime: '1/16',
|
||||
fps: 100,
|
||||
asset: null as any,
|
||||
exifTextSearchableColumn: '',
|
||||
},
|
||||
id: 'id_1',
|
||||
userId: 'user_id_1',
|
||||
deviceAssetId: 'device_asset_id_1',
|
||||
deviceId: 'device_id_1',
|
||||
type: AssetType.VIDEO,
|
||||
originalPath: 'fake_path/jpeg',
|
||||
resizePath: '',
|
||||
createdAt: today.toISOString(),
|
||||
modifiedAt: today.toISOString(),
|
||||
updatedAt: today.toISOString(),
|
||||
isFavorite: false,
|
||||
mimeType: 'image/jpeg',
|
||||
smartInfo: {
|
||||
id: 'should-be-a-number',
|
||||
assetId: 'id_1',
|
||||
tags: [],
|
||||
sharedLinks: [],
|
||||
objects: ['a', 'b', 'c'],
|
||||
asset: null as any,
|
||||
},
|
||||
webpPath: '',
|
||||
encodedVideoPath: '',
|
||||
duration: null,
|
||||
isVisible: true,
|
||||
livePhotoVideoId: null,
|
||||
exifInfo: {
|
||||
livePhotoCID: null,
|
||||
id: 1,
|
||||
assetId: 'id_1',
|
||||
description: 'description',
|
||||
exifImageWidth: 500,
|
||||
exifImageHeight: 500,
|
||||
fileSizeInByte: 100,
|
||||
orientation: 'orientation',
|
||||
dateTimeOriginal: today,
|
||||
modifyDate: today,
|
||||
latitude: 100,
|
||||
longitude: 100,
|
||||
city: 'city',
|
||||
state: 'state',
|
||||
country: 'country',
|
||||
make: 'camera-make',
|
||||
model: 'camera-model',
|
||||
imageName: 'fancy-image',
|
||||
lensModel: 'fancy',
|
||||
fNumber: 100,
|
||||
focalLength: 100,
|
||||
iso: 100,
|
||||
exposureTime: '1/16',
|
||||
fps: 100,
|
||||
asset: null as any,
|
||||
exifTextSearchableColumn: '',
|
||||
},
|
||||
tags: [],
|
||||
sharedLinks: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,14 +2,15 @@ import {
|
|||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinTable,
|
||||
ManyToMany,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
import { AssetAlbumEntity } from './asset-album.entity';
|
||||
import { SharedLinkEntity } from './shared-link.entity';
|
||||
import { UserAlbumEntity } from './user-album.entity';
|
||||
import { AssetEntity } from './asset.entity';
|
||||
import { UserEntity } from './user.entity';
|
||||
|
||||
@Entity('albums')
|
||||
|
|
@ -17,12 +18,12 @@ export class AlbumEntity {
|
|||
@PrimaryGeneratedColumn('uuid')
|
||||
id!: string;
|
||||
|
||||
@ManyToOne(() => UserEntity, { eager: true, onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false })
|
||||
owner!: UserEntity;
|
||||
|
||||
@Column()
|
||||
ownerId!: string;
|
||||
|
||||
@ManyToOne(() => UserEntity, { eager: true })
|
||||
owner!: UserEntity;
|
||||
|
||||
@Column({ default: 'Untitled Album' })
|
||||
albumName!: string;
|
||||
|
||||
|
|
@ -35,11 +36,13 @@ export class AlbumEntity {
|
|||
@Column({ comment: 'Asset ID to be used as thumbnail', type: 'varchar', nullable: true })
|
||||
albumThumbnailAssetId!: string | null;
|
||||
|
||||
@OneToMany(() => UserAlbumEntity, (userAlbums) => userAlbums.albumInfo)
|
||||
sharedUsers?: UserAlbumEntity[];
|
||||
@ManyToMany(() => UserEntity, { eager: true })
|
||||
@JoinTable()
|
||||
sharedUsers!: UserEntity[];
|
||||
|
||||
@OneToMany(() => AssetAlbumEntity, (assetAlbumEntity) => assetAlbumEntity.albumInfo)
|
||||
assets?: AssetAlbumEntity[];
|
||||
@ManyToMany(() => AssetEntity, { eager: true })
|
||||
@JoinTable()
|
||||
assets!: AssetEntity[];
|
||||
|
||||
@OneToMany(() => SharedLinkEntity, (link) => link.album)
|
||||
sharedLinks!: SharedLinkEntity[];
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import { UserEntity } from './user.entity';
|
|||
|
||||
@Entity('api_keys')
|
||||
export class APIKeyEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id!: number;
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id!: string;
|
||||
|
||||
@Column()
|
||||
name!: string;
|
||||
|
|
@ -12,12 +12,12 @@ export class APIKeyEntity {
|
|||
@Column({ select: false })
|
||||
key?: string;
|
||||
|
||||
@Column()
|
||||
userId!: string;
|
||||
|
||||
@ManyToOne(() => UserEntity)
|
||||
user?: UserEntity;
|
||||
|
||||
@Column()
|
||||
userId!: string;
|
||||
|
||||
@CreateDateColumn({ type: 'timestamptz' })
|
||||
createdAt!: string;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
import { Column, Entity, JoinColumn, ManyToOne, OneToOne, PrimaryGeneratedColumn, Unique } from 'typeorm';
|
||||
import { AlbumEntity } from './album.entity';
|
||||
import { AssetEntity } from './asset.entity';
|
||||
|
||||
@Entity('asset_album')
|
||||
@Unique('UQ_unique_asset_in_album', ['albumId', 'assetId'])
|
||||
export class AssetAlbumEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id!: string;
|
||||
|
||||
@Column()
|
||||
albumId!: string;
|
||||
|
||||
@Column()
|
||||
@OneToOne(() => AssetEntity, (entity) => entity.id)
|
||||
assetId!: string;
|
||||
|
||||
@ManyToOne(() => AlbumEntity, (album) => album.assets, {
|
||||
onDelete: 'CASCADE',
|
||||
nullable: true,
|
||||
})
|
||||
@JoinColumn({ name: 'albumId' })
|
||||
albumInfo!: AlbumEntity;
|
||||
|
||||
@ManyToOne(() => AssetEntity, {
|
||||
onDelete: 'CASCADE',
|
||||
nullable: true,
|
||||
})
|
||||
@JoinColumn({ name: 'assetId' })
|
||||
assetInfo!: AssetEntity;
|
||||
}
|
||||
|
|
@ -1,13 +1,11 @@
|
|||
export * from './album.entity';
|
||||
export * from './api-key.entity';
|
||||
export * from './asset-album.entity';
|
||||
export * from './asset.entity';
|
||||
export * from './device-info.entity';
|
||||
export * from './exif.entity';
|
||||
export * from './smart-info.entity';
|
||||
export * from './system-config.entity';
|
||||
export * from './tag.entity';
|
||||
export * from './user-album.entity';
|
||||
export * from './user.entity';
|
||||
export * from './user-token.entity';
|
||||
export * from './shared-link.entity';
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
import { Column, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn, Unique } from 'typeorm';
|
||||
import { UserEntity } from './user.entity';
|
||||
import { AlbumEntity } from './album.entity';
|
||||
|
||||
@Entity('user_shared_album')
|
||||
@Unique('PK_unique_user_in_album', ['albumId', 'sharedUserId'])
|
||||
export class UserAlbumEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id!: string;
|
||||
|
||||
@Column()
|
||||
albumId!: string;
|
||||
|
||||
@Column()
|
||||
sharedUserId!: string;
|
||||
|
||||
@ManyToOne(() => AlbumEntity, (album) => album.sharedUsers, {
|
||||
onDelete: 'CASCADE',
|
||||
nullable: true,
|
||||
})
|
||||
@JoinColumn({ name: 'albumId' })
|
||||
albumInfo!: AlbumEntity;
|
||||
|
||||
@ManyToOne(() => UserEntity)
|
||||
@JoinColumn({ name: 'sharedUserId' })
|
||||
userInfo!: UserEntity;
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class APIKeyUUIDPrimaryKey1675808874445 implements MigrationInterface {
|
||||
name = 'APIKeyUUIDPrimaryKey1675808874445'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad"`);
|
||||
await queryRunner.query(`ALTER TABLE "api_keys" DROP COLUMN "id"`);
|
||||
await queryRunner.query(`ALTER TABLE "api_keys" ADD "id" uuid NOT NULL DEFAULT uuid_generate_v4()`);
|
||||
await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad" PRIMARY KEY ("id")`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad"`);
|
||||
await queryRunner.query(`ALTER TABLE "api_keys" DROP COLUMN "id"`);
|
||||
await queryRunner.query(`ALTER TABLE "api_keys" ADD "id" SERIAL NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad" PRIMARY KEY ("id")`);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class FixAlbumEntityTypeORM1675812532822 implements MigrationInterface {
|
||||
name = 'FixAlbumEntityTypeORM1675812532822'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
|
||||
await queryRunner.query(`ALTER TABLE "asset_album" RENAME TO "albums_assets_assets"`);
|
||||
await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP CONSTRAINT "FK_7ae4e03729895bf87e056d7b598"`);
|
||||
await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP CONSTRAINT "FK_256a30a03a4a0aff0394051397d"`);
|
||||
await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP CONSTRAINT "UQ_unique_asset_in_album"`);
|
||||
await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP CONSTRAINT "PK_a34e076afbc601d81938e2c2277"`);
|
||||
await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP COLUMN "id"`);
|
||||
await queryRunner.query(`ALTER TABLE "albums_assets_assets" RENAME COLUMN "albumId" TO "albumsId"`);
|
||||
await queryRunner.query(`ALTER TABLE "albums_assets_assets" RENAME COLUMN "assetId" TO "assetsId"`);
|
||||
await queryRunner.query(`ALTER TABLE "albums_assets_assets" ADD CONSTRAINT "PK_c67bc36fa845fb7b18e0e398180" PRIMARY KEY ("albumsId", "assetsId")`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_e590fa396c6898fcd4a50e4092" ON "albums_assets_assets" ("albumsId") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_4bd1303d199f4e72ccdf998c62" ON "albums_assets_assets" ("assetsId") `);
|
||||
await queryRunner.query(`ALTER TABLE "albums_assets_assets" ADD CONSTRAINT "FK_e590fa396c6898fcd4a50e40927" FOREIGN KEY ("albumsId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE CASCADE`);
|
||||
await queryRunner.query(`ALTER TABLE "albums_assets_assets" ADD CONSTRAINT "FK_4bd1303d199f4e72ccdf998c621" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`);
|
||||
|
||||
await queryRunner.query(`ALTER TABLE "user_shared_album" RENAME TO "albums_shared_users_users"`);
|
||||
await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP CONSTRAINT "FK_543c31211653e63e080ba882eb5"`);
|
||||
await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP CONSTRAINT "FK_7b3bf0f5f8da59af30519c25f18"`);
|
||||
await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP CONSTRAINT "PK_unique_user_in_album"`);
|
||||
await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP CONSTRAINT "PK_b6562316a98845a7b3e9a25cdd0"`);
|
||||
await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP COLUMN "id"`);
|
||||
await queryRunner.query(`ALTER TABLE "albums_shared_users_users" RENAME COLUMN "albumId" TO "albumsId"`);
|
||||
await queryRunner.query(`ALTER TABLE "albums_shared_users_users" RENAME COLUMN "sharedUserId" TO "usersId"`);
|
||||
await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD CONSTRAINT "PK_7df55657e0b2e8b626330a0ebc8" PRIMARY KEY ("albumsId", "usersId")`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_427c350ad49bd3935a50baab73" ON "albums_shared_users_users" ("albumsId") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_f48513bf9bccefd6ff3ad30bd0" ON "albums_shared_users_users" ("usersId") `);
|
||||
await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD CONSTRAINT "FK_427c350ad49bd3935a50baab737" FOREIGN KEY ("albumsId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE CASCADE`);
|
||||
await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD CONSTRAINT "FK_f48513bf9bccefd6ff3ad30bd06" FOREIGN KEY ("usersId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`);
|
||||
|
||||
await queryRunner.query(`ALTER TABLE "albums" DROP CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4"`)
|
||||
await queryRunner.query(`ALTER TABLE "albums" ADD CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "albums_assets_assets" RENAME TO "asset_album"`);
|
||||
await queryRunner.query(`ALTER TABLE "albums_shared_users_users" RENAME TO "user_shared_album"`);
|
||||
await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_e590fa396c6898fcd4a50e40927"`);
|
||||
await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_4bd1303d199f4e72ccdf998c621"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "FK_427c350ad49bd3935a50baab737"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "FK_f48513bf9bccefd6ff3ad30bd06"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_427c350ad49bd3935a50baab73"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_f48513bf9bccefd6ff3ad30bd0"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_e590fa396c6898fcd4a50e4092"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_4bd1303d199f4e72ccdf998c62"`);
|
||||
|
||||
await queryRunner.query(`ALTER TABLE "albums" DROP CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "albums" ADD CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
|
||||
);
|
||||
|
||||
await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "PK_7df55657e0b2e8b626330a0ebc8"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "PK_323f8dcbe85373722886940f143" PRIMARY KEY ("albumsId")`);
|
||||
await queryRunner.query(`ALTER TABLE "user_shared_album" DROP COLUMN "usersId"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "PK_323f8dcbe85373722886940f143"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_shared_album" DROP COLUMN "albumsId"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_shared_album" ADD "sharedUserId" uuid NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE "user_shared_album" ADD "albumId" uuid NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE "user_shared_album" ADD "id" SERIAL NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "PK_b6562316a98845a7b3e9a25cdd0" PRIMARY KEY ("id")`);
|
||||
await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "PK_unique_user_in_album" UNIQUE ("albumId", "sharedUserId")`);
|
||||
await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "FK_7b3bf0f5f8da59af30519c25f18" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "FK_543c31211653e63e080ba882eb5" FOREIGN KEY ("sharedUserId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
||||
|
||||
await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "PK_c67bc36fa845fb7b18e0e398180"`);
|
||||
await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "PK_b4f2e5b96efc25cbccd80a04f7a" PRIMARY KEY ("albumsId")`);
|
||||
await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "PK_b4f2e5b96efc25cbccd80a04f7a"`);
|
||||
await queryRunner.query(`ALTER TABLE "asset_album" RENAME COLUMN "albumsId" TO "albumId"`);
|
||||
await queryRunner.query(`ALTER TABLE "asset_album" RENAME COLUMN "assetsId" TO "assetId"`);
|
||||
await queryRunner.query(`ALTER TABLE "asset_album" ADD "id" SERIAL NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "PK_a34e076afbc601d81938e2c2277" PRIMARY KEY ("id")`);
|
||||
await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "UQ_unique_asset_in_album" UNIQUE ("albumId", "assetId")`);
|
||||
await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "FK_256a30a03a4a0aff0394051397d" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "FK_7ae4e03729895bf87e056d7b598" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -12,12 +12,12 @@ export class APIKeyRepository implements IKeyRepository {
|
|||
return this.repository.save(dto);
|
||||
}
|
||||
|
||||
async update(userId: string, id: number, dto: Partial<APIKeyEntity>): Promise<APIKeyEntity> {
|
||||
async update(userId: string, id: string, dto: Partial<APIKeyEntity>): Promise<APIKeyEntity> {
|
||||
await this.repository.update({ userId, id }, dto);
|
||||
return this.repository.findOneOrFail({ where: { id: dto.id } });
|
||||
}
|
||||
|
||||
async delete(userId: string, id: number): Promise<void> {
|
||||
async delete(userId: string, id: string): Promise<void> {
|
||||
await this.repository.delete({ userId, id });
|
||||
}
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ export class APIKeyRepository implements IKeyRepository {
|
|||
});
|
||||
}
|
||||
|
||||
getById(userId: string, id: number): Promise<APIKeyEntity | null> {
|
||||
getById(userId: string, id: string): Promise<APIKeyEntity | null> {
|
||||
return this.repository.findOne({ where: { userId, id } });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,9 +24,7 @@ export class SharedLinkRepository implements ISharedLinkRepository {
|
|||
},
|
||||
album: {
|
||||
assets: {
|
||||
assetInfo: {
|
||||
exifInfo: true,
|
||||
},
|
||||
exifInfo: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -37,9 +35,7 @@ export class SharedLinkRepository implements ISharedLinkRepository {
|
|||
},
|
||||
album: {
|
||||
assets: {
|
||||
assetInfo: {
|
||||
createdAt: 'ASC',
|
||||
},
|
||||
createdAt: 'ASC',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -69,9 +65,7 @@ export class SharedLinkRepository implements ISharedLinkRepository {
|
|||
relations: {
|
||||
assets: true,
|
||||
album: {
|
||||
assets: {
|
||||
assetInfo: true,
|
||||
},
|
||||
assets: true,
|
||||
},
|
||||
user: true,
|
||||
},
|
||||
|
|
@ -109,7 +103,7 @@ export class SharedLinkRepository implements ISharedLinkRepository {
|
|||
id,
|
||||
album: {
|
||||
assets: {
|
||||
assetId,
|
||||
id: assetId,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue