refactor: database types (#17468)

This commit is contained in:
Jason Rasmussen 2025-04-08 12:40:03 -04:00 committed by GitHub
parent ac65d46ec6
commit 4794eeca88
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 44 additions and 39 deletions

View File

@ -1,5 +1,6 @@
import { UserMetadataEntity } from 'src/entities/user-metadata.entity'; import { UserMetadataEntity } from 'src/entities/user-metadata.entity';
import { AssetStatus, AssetType, Permission, UserStatus } from 'src/enum'; import { AssetStatus, AssetType, MemoryType, Permission, UserStatus } from 'src/enum';
import { OnThisDayData } from 'src/types';
export type AuthUser = { export type AuthUser = {
id: string; id: string;
@ -38,6 +39,31 @@ export type ApiKey = {
permissions: Permission[]; permissions: Permission[];
}; };
export type Tag = {
id: string;
value: string;
createdAt: Date;
updatedAt: Date;
color: string | null;
parentId: string | null;
};
export type Memory = {
id: string;
createdAt: Date;
updatedAt: Date;
deletedAt: Date | null;
memoryAt: Date;
seenAt: Date | null;
showAt: Date | null;
hideAt: Date | null;
type: MemoryType;
data: OnThisDayData;
ownerId: string;
isSaved: boolean;
assets: Asset[];
};
export type User = { export type User = {
id: string; id: string;
name: string; name: string;

View File

@ -1,11 +1,11 @@
import { ApiProperty } from '@nestjs/swagger'; import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer'; import { Type } from 'class-transformer';
import { IsEnum, IsInt, IsObject, IsPositive, ValidateNested } from 'class-validator'; import { IsEnum, IsInt, IsObject, IsPositive, ValidateNested } from 'class-validator';
import { Memory } from 'src/database';
import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto';
import { AuthDto } from 'src/dtos/auth.dto'; import { AuthDto } from 'src/dtos/auth.dto';
import { AssetEntity } from 'src/entities/asset.entity'; import { AssetEntity } from 'src/entities/asset.entity';
import { MemoryType } from 'src/enum'; import { MemoryType } from 'src/enum';
import { MemoryItem } from 'src/types';
import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation'; import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation';
class MemoryBaseDto { class MemoryBaseDto {
@ -89,7 +89,7 @@ export class MemoryResponseDto {
assets!: AssetResponseDto[]; assets!: AssetResponseDto[];
} }
export const mapMemory = (entity: MemoryItem, auth: AuthDto): MemoryResponseDto => { export const mapMemory = (entity: Memory, auth: AuthDto): MemoryResponseDto => {
return { return {
id: entity.id, id: entity.id,
createdAt: entity.createdAt, createdAt: entity.createdAt,

View File

@ -1,6 +1,6 @@
import { ApiProperty } from '@nestjs/swagger'; import { ApiProperty } from '@nestjs/swagger';
import { IsHexColor, IsNotEmpty, IsString } from 'class-validator'; import { IsHexColor, IsNotEmpty, IsString } from 'class-validator';
import { TagItem } from 'src/types'; import { Tag } from 'src/database';
import { Optional, ValidateHexColor, ValidateUUID } from 'src/validation'; import { Optional, ValidateHexColor, ValidateUUID } from 'src/validation';
export class TagCreateDto { export class TagCreateDto {
@ -51,7 +51,7 @@ export class TagResponseDto {
color?: string; color?: string;
} }
export function mapTag(entity: TagItem): TagResponseDto { export function mapTag(entity: Tag): TagResponseDto {
return { return {
id: entity.id, id: entity.id,
parentId: entity.parentId ?? undefined, parentId: entity.parentId ?? undefined,

View File

@ -1,5 +1,6 @@
import { DeduplicateJoinsPlugin, ExpressionBuilder, Kysely, SelectQueryBuilder, sql } from 'kysely'; import { DeduplicateJoinsPlugin, ExpressionBuilder, Kysely, SelectQueryBuilder, sql } from 'kysely';
import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres'; import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres';
import { Tag } from 'src/database';
import { DB } from 'src/db'; import { DB } from 'src/db';
import { AlbumEntity } from 'src/entities/album.entity'; import { AlbumEntity } from 'src/entities/album.entity';
import { AssetFaceEntity } from 'src/entities/asset-face.entity'; import { AssetFaceEntity } from 'src/entities/asset-face.entity';
@ -12,7 +13,6 @@ import { UserEntity } from 'src/entities/user.entity';
import { AssetFileType, AssetStatus, AssetType } from 'src/enum'; import { AssetFileType, AssetStatus, AssetType } from 'src/enum';
import { TimeBucketSize } from 'src/repositories/asset.repository'; import { TimeBucketSize } from 'src/repositories/asset.repository';
import { AssetSearchBuilderOptions } from 'src/repositories/search.repository'; import { AssetSearchBuilderOptions } from 'src/repositories/search.repository';
import { TagItem } from 'src/types';
import { anyUuid, asUuid } from 'src/utils/database'; import { anyUuid, asUuid } from 'src/utils/database';
export const ASSET_CHECKSUM_CONSTRAINT = 'UQ_assets_owner_checksum'; export const ASSET_CHECKSUM_CONSTRAINT = 'UQ_assets_owner_checksum';
@ -49,7 +49,7 @@ export class AssetEntity {
originalFileName!: string; originalFileName!: string;
sidecarPath!: string | null; sidecarPath!: string | null;
exifInfo?: ExifEntity; exifInfo?: ExifEntity;
tags?: TagItem[]; tags?: Tag[];
sharedLinks!: SharedLinkEntity[]; sharedLinks!: SharedLinkEntity[];
albums?: AlbumEntity[]; albums?: AlbumEntity[];
faces!: AssetFaceEntity[]; faces!: AssetFaceEntity[];

View File

@ -1,9 +1,9 @@
import { BadRequestException, Injectable } from '@nestjs/common'; import { BadRequestException, Injectable } from '@nestjs/common';
import { ApiKey } from 'src/database';
import { APIKeyCreateDto, APIKeyCreateResponseDto, APIKeyResponseDto, APIKeyUpdateDto } from 'src/dtos/api-key.dto'; import { APIKeyCreateDto, APIKeyCreateResponseDto, APIKeyResponseDto, APIKeyUpdateDto } from 'src/dtos/api-key.dto';
import { AuthDto } from 'src/dtos/auth.dto'; import { AuthDto } from 'src/dtos/auth.dto';
import { Permission } from 'src/enum'; import { Permission } from 'src/enum';
import { BaseService } from 'src/services/base.service'; import { BaseService } from 'src/services/base.service';
import { ApiKeyItem } from 'src/types';
import { isGranted } from 'src/utils/access'; import { isGranted } from 'src/utils/access';
@Injectable() @Injectable()
@ -58,7 +58,7 @@ export class ApiKeyService extends BaseService {
return keys.map((key) => this.map(key)); return keys.map((key) => this.map(key));
} }
private map(entity: ApiKeyItem): APIKeyResponseDto { private map(entity: ApiKey): APIKeyResponseDto {
return { return {
id: entity.id, id: entity.id,
name: entity.name, name: entity.name,

View File

@ -14,8 +14,6 @@ import {
VideoCodec, VideoCodec,
} from 'src/enum'; } from 'src/enum';
import { ActivityRepository } from 'src/repositories/activity.repository'; import { ActivityRepository } from 'src/repositories/activity.repository';
import { ApiKeyRepository } from 'src/repositories/api-key.repository';
import { MemoryRepository } from 'src/repositories/memory.repository';
import { SearchRepository } from 'src/repositories/search.repository'; import { SearchRepository } from 'src/repositories/search.repository';
import { SessionRepository } from 'src/repositories/session.repository'; import { SessionRepository } from 'src/repositories/session.repository';
@ -24,8 +22,6 @@ export type DeepPartial<T> = T extends object ? { [K in keyof T]?: DeepPartial<T
export type RepositoryInterface<T extends object> = Pick<T, keyof T>; export type RepositoryInterface<T extends object> = Pick<T, keyof T>;
type IActivityRepository = RepositoryInterface<ActivityRepository>; type IActivityRepository = RepositoryInterface<ActivityRepository>;
type IApiKeyRepository = RepositoryInterface<ApiKeyRepository>;
type IMemoryRepository = RepositoryInterface<MemoryRepository>;
type ISearchRepository = RepositoryInterface<SearchRepository>; type ISearchRepository = RepositoryInterface<SearchRepository>;
type ISessionRepository = RepositoryInterface<SessionRepository>; type ISessionRepository = RepositoryInterface<SessionRepository>;
@ -35,26 +31,8 @@ export type ActivityItem =
export type SearchPlacesItem = Awaited<ReturnType<ISearchRepository['searchPlaces']>>[0]; export type SearchPlacesItem = Awaited<ReturnType<ISearchRepository['searchPlaces']>>[0];
export type ApiKeyItem =
| Awaited<ReturnType<IApiKeyRepository['create']>>
| NonNullable<Awaited<ReturnType<IApiKeyRepository['getById']>>>
| Awaited<ReturnType<IApiKeyRepository['getByUserId']>>[0];
export type MemoryItem =
| Awaited<ReturnType<IMemoryRepository['create']>>
| Awaited<ReturnType<IMemoryRepository['search']>>[0];
export type SessionItem = Awaited<ReturnType<ISessionRepository['getByUserId']>>[0]; export type SessionItem = Awaited<ReturnType<ISessionRepository['getByUserId']>>[0];
export type TagItem = {
id: string;
value: string;
createdAt: Date;
updatedAt: Date;
color: string | null;
parentId: string | null;
};
export interface CropOptions { export interface CropOptions {
top: number; top: number;
left: number; left: number;

View File

@ -1,15 +1,15 @@
import { Tag } from 'src/database';
import { TagRepository } from 'src/repositories/tag.repository'; import { TagRepository } from 'src/repositories/tag.repository';
import { TagItem } from 'src/types';
type UpsertRequest = { userId: string; tags: string[] }; type UpsertRequest = { userId: string; tags: string[] };
export const upsertTags = async (repository: TagRepository, { userId, tags }: UpsertRequest) => { export const upsertTags = async (repository: TagRepository, { userId, tags }: UpsertRequest) => {
tags = [...new Set(tags)]; tags = [...new Set(tags)];
const results: TagItem[] = []; const results: Tag[] = [];
for (const tag of tags) { for (const tag of tags) {
const parts = tag.split('/').filter(Boolean); const parts = tag.split('/').filter(Boolean);
let parent: TagItem | undefined; let parent: Tag | undefined;
for (const part of parts) { for (const part of parts) {
const value = parent ? `${parent.value}/${part}` : part; const value = parent ? `${parent.value}/${part}` : part;

View File

@ -1,7 +1,7 @@
import { Tag } from 'src/database';
import { TagResponseDto } from 'src/dtos/tag.dto'; import { TagResponseDto } from 'src/dtos/tag.dto';
import { TagItem } from 'src/types';
const parent = Object.freeze<TagItem>({ const parent = Object.freeze<Tag>({
id: 'tag-parent', id: 'tag-parent',
createdAt: new Date('2021-01-01T00:00:00Z'), createdAt: new Date('2021-01-01T00:00:00Z'),
updatedAt: new Date('2021-01-01T00:00:00Z'), updatedAt: new Date('2021-01-01T00:00:00Z'),
@ -10,7 +10,7 @@ const parent = Object.freeze<TagItem>({
parentId: null, parentId: null,
}); });
const child = Object.freeze<TagItem>({ const child = Object.freeze<Tag>({
id: 'tag-child', id: 'tag-child',
createdAt: new Date('2021-01-01T00:00:00Z'), createdAt: new Date('2021-01-01T00:00:00Z'),
updatedAt: new Date('2021-01-01T00:00:00Z'), updatedAt: new Date('2021-01-01T00:00:00Z'),

View File

@ -5,6 +5,7 @@ import {
AuthApiKey, AuthApiKey,
AuthUser, AuthUser,
Library, Library,
Memory,
Partner, Partner,
SidecarWriteAsset, SidecarWriteAsset,
User, User,
@ -12,7 +13,7 @@ import {
} from 'src/database'; } from 'src/database';
import { AuthDto } from 'src/dtos/auth.dto'; import { AuthDto } from 'src/dtos/auth.dto';
import { AssetStatus, AssetType, MemoryType, Permission, UserStatus } from 'src/enum'; import { AssetStatus, AssetType, MemoryType, Permission, UserStatus } from 'src/enum';
import { ActivityItem, MemoryItem, OnThisDayData } from 'src/types'; import { ActivityItem, OnThisDayData } from 'src/types';
export const newUuid = () => randomUUID() as string; export const newUuid = () => randomUUID() as string;
export const newUuids = () => export const newUuids = () =>
@ -196,7 +197,7 @@ const libraryFactory = (library: Partial<Library> = {}) => ({
...library, ...library,
}); });
const memoryFactory = (memory: Partial<MemoryItem> = {}) => ({ const memoryFactory = (memory: Partial<Memory> = {}) => ({
id: newUuid(), id: newUuid(),
createdAt: newDate(), createdAt: newDate(),
updatedAt: newDate(), updatedAt: newDate(),