diff --git a/server/src/database.ts b/server/src/database.ts index 100ba451e7..baa1e98aac 100644 --- a/server/src/database.ts +++ b/server/src/database.ts @@ -230,8 +230,8 @@ export type Session = { createdAt: Date; updatedAt: Date; expiresAt: Date | null; - deviceOS: string; - deviceType: string; + deviceOS: string | null; + deviceType: string | null; appVersion: string | null; pinExpiresAt: Date | null; isPendingSyncReset: boolean; @@ -251,7 +251,7 @@ export type Person = { color: string | null; faceAssetId: string | null; isHidden: boolean; - thumbnailPath: string; + thumbnailPath: string | null; }; export type AssetFace = { diff --git a/server/src/dtos/person.dto.ts b/server/src/dtos/person.dto.ts index fa56ded176..b1408198bb 100644 --- a/server/src/dtos/person.dto.ts +++ b/server/src/dtos/person.dto.ts @@ -64,7 +64,7 @@ export const PersonResponseSchema = z name: z.string().describe('Person name'), // TODO: use `isoDateToDate` when using `ZodSerializerDto` on the controllers. birthDate: z.string().meta({ format: 'date' }).describe('Person date of birth').nullable(), - thumbnailPath: z.string().describe('Thumbnail path'), + thumbnailPath: z.string().nullable().describe('Thumbnail path'), isHidden: z.boolean().describe('Is hidden'), // TODO: use `isoDatetimeToDate` when using `ZodSerializerDto` on the controllers. updatedAt: z diff --git a/server/src/dtos/session.dto.ts b/server/src/dtos/session.dto.ts index c81dac6d79..f49ac87bf1 100644 --- a/server/src/dtos/session.dto.ts +++ b/server/src/dtos/session.dto.ts @@ -5,8 +5,8 @@ import z from 'zod'; const SessionCreateSchema = z .object({ duration: z.int().min(1).optional().describe('Session duration in seconds'), - deviceType: z.string().optional().describe('Device type'), - deviceOS: z.string().optional().describe('Device OS'), + deviceType: z.string().nullish().transform((val) => (val === '' ? null : val)).describe('Device type'), + deviceOS: z.string().nullish().transform((val) => (val === '' ? null : val)).describe('Device OS'), }) .meta({ id: 'SessionCreateDto' }); @@ -23,8 +23,8 @@ const SessionResponseSchema = z updatedAt: z.string().describe('Last update date'), expiresAt: z.string().optional().describe('Expiration date'), current: z.boolean().describe('Is current session'), - deviceType: z.string().describe('Device type'), - deviceOS: z.string().describe('Device OS'), + deviceType: z.string().nullable().describe('Device type'), + deviceOS: z.string().nullable().describe('Device OS'), appVersion: z.string().nullable().describe('App version'), isPendingSyncReset: z.boolean().describe('Is pending sync reset'), }) diff --git a/server/src/schema/migrations/1784471379379-ConvertSessionDeviceTypeAndOSEmptyStringToNull.ts b/server/src/schema/migrations/1784471379379-ConvertSessionDeviceTypeAndOSEmptyStringToNull.ts new file mode 100644 index 0000000000..2bfbfd08e0 --- /dev/null +++ b/server/src/schema/migrations/1784471379379-ConvertSessionDeviceTypeAndOSEmptyStringToNull.ts @@ -0,0 +1,19 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`ALTER TABLE "session" ALTER COLUMN "deviceType" DROP NOT NULL;`.execute(db); + await sql`ALTER TABLE "session" ALTER COLUMN "deviceType" SET DEFAULT NULL;`.execute(db); + await sql`UPDATE "session" SET "deviceType" = NULL WHERE "deviceType" = '';`.execute(db); + await sql`ALTER TABLE "session" ALTER COLUMN "deviceOS" DROP NOT NULL;`.execute(db); + await sql`ALTER TABLE "session" ALTER COLUMN "deviceOS" SET DEFAULT NULL;`.execute(db); + await sql`UPDATE "session" SET "deviceOS" = NULL WHERE "deviceOS" = '';`.execute(db); +} + +export async function down(): Promise { + await sql`UPDATE "session" SET "deviceOS" = '' WHERE "deviceOS" IS NULL;`.execute(db); + await sql`ALTER TABLE "session" ALTER COLUMN "deviceOS" SET DEFAULT '';`.execute(db); + await sql`ALTER TABLE "session" ALTER COLUMN "deviceOS" SET NOT NULL;`.execute(db); + await sql`UPDATE "session" SET "deviceType" = '' WHERE "deviceType" IS NULL;`.execute(db); + await sql`ALTER TABLE "session" ALTER COLUMN "deviceType" SET DEFAULT '';`.execute(db); + await sql`ALTER TABLE "session" ALTER COLUMN "deviceType" SET NOT NULL;`.execute(db); +} diff --git a/server/src/schema/migrations/1784554467506-ConvertPersonThumbnailPathEmptyStringToNull.ts b/server/src/schema/migrations/1784554467506-ConvertPersonThumbnailPathEmptyStringToNull.ts new file mode 100644 index 0000000000..8abeda8cb9 --- /dev/null +++ b/server/src/schema/migrations/1784554467506-ConvertPersonThumbnailPathEmptyStringToNull.ts @@ -0,0 +1,13 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`ALTER TABLE "person" ALTER COLUMN "thumbnailPath" DROP NOT NULL;`.execute(db); + await sql`ALTER TABLE "person" ALTER COLUMN "thumbnailPath" SET DEFAULT NULL;`.execute(db); + await sql`UPDATE "person" SET "thumbnailPath" = NULL WHERE "thumbnailPath" = '';`.execute(db); +} + +export async function down(): Promise { + await sql`UPDATE "person" SET "thumbnailPath" = '' WHERE "thumbnailPath" IS NULL;`.execute(db); + await sql`ALTER TABLE "person" ALTER COLUMN "thumbnailPath" SET DEFAULT '';`.execute(db); + await sql`ALTER TABLE "person" ALTER COLUMN "thumbnailPath" SET NOT NULL;`.execute(db); +} diff --git a/server/src/schema/tables/person.table.ts b/server/src/schema/tables/person.table.ts index 35447acfd0..4600702ed0 100644 --- a/server/src/schema/tables/person.table.ts +++ b/server/src/schema/tables/person.table.ts @@ -46,8 +46,8 @@ export class PersonTable { @Column({ default: '' }) name!: Generated; - @Column({ default: '' }) - thumbnailPath!: Generated; + @Column({ nullable: true, default: null }) + thumbnailPath!: string | null; @Column({ type: 'boolean', default: false }) isHidden!: Generated; diff --git a/server/src/schema/tables/session.table.ts b/server/src/schema/tables/session.table.ts index 68381b7f8e..f270a1ea0c 100644 --- a/server/src/schema/tables/session.table.ts +++ b/server/src/schema/tables/session.table.ts @@ -35,11 +35,11 @@ export class SessionTable { @ForeignKeyColumn(() => SessionTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE', nullable: true }) parentId!: string | null; - @Column({ default: '' }) - deviceType!: Generated; + @Column({ nullable: true, default: null }) + deviceType!: string | null; - @Column({ default: '' }) - deviceOS!: Generated; + @Column({ nullable: true, default: null }) + deviceOS!: string | null; @Column({ nullable: true }) appVersion!: string | null;