From 789e3e392414f236c20739de78f20ca2cccf1dba Mon Sep 17 00:00:00 2001 From: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> Date: Tue, 30 May 2023 15:15:56 +0200 Subject: [PATCH] refactor(server): use date type for entities (#2602) --- mobile/lib/shared/models/album.dart | 4 +- mobile/lib/shared/models/user.dart | 4 +- mobile/lib/shared/services/sync.service.dart | 4 +- mobile/openapi/doc/APIKeyResponseDto.md | Bin 503 -> 537 bytes mobile/openapi/doc/AdminSignupResponseDto.md | Bin 541 -> 558 bytes mobile/openapi/doc/AlbumResponseDto.md | Bin 885 -> 919 bytes .../openapi/doc/CreateAssetsShareLinkDto.md | Bin 670 -> 687 bytes mobile/openapi/doc/EditSharedLinkDto.md | Bin 604 -> 621 bytes mobile/openapi/doc/SharedLinkResponseDto.md | Bin 900 -> 934 bytes mobile/openapi/doc/UserResponseDto.md | Bin 817 -> 829 bytes .../lib/model/admin_signup_response_dto.dart | Bin 4343 -> 4364 bytes .../openapi/lib/model/album_response_dto.dart | Bin 6076 -> 6118 bytes .../lib/model/api_key_response_dto.dart | Bin 3983 -> 4025 bytes .../model/create_assets_share_link_dto.dart | Bin 7149 -> 7171 bytes .../lib/model/edit_shared_link_dto.dart | Bin 6309 -> 6331 bytes .../lib/model/shared_link_response_dto.dart | Bin 7053 -> 7096 bytes .../openapi/lib/model/user_response_dto.dart | Bin 7238 -> 6520 bytes .../test/admin_signup_response_dto_test.dart | Bin 980 -> 982 bytes .../openapi/test/album_response_dto_test.dart | Bin 1683 -> 1687 bytes .../test/api_key_response_dto_test.dart | Bin 862 -> 866 bytes .../create_assets_share_link_dto_test.dart | Bin 1151 -> 1153 bytes .../test/edit_shared_link_dto_test.dart | Bin 997 -> 999 bytes .../test/shared_link_response_dto_test.dart | Bin 1734 -> 1738 bytes .../openapi/test/user_response_dto_test.dart | Bin 1712 -> 1716 bytes .../src/api-v1/album/album-repository.ts | 8 +- .../src/api-v1/album/album.service.spec.ts | 19 ++-- .../album/dto/create-album-shared-link.dto.ts | 10 ++- .../asset/dto/create-asset-shared-link.dto.ts | 8 +- .../immich/src/api-v1/tag/tag.service.spec.ts | 6 +- server/immich-openapi-specs.json | 28 ++++-- .../domain/src/album/album.service.spec.ts | 5 +- .../album/response-dto/album-response.dto.ts | 4 +- .../src/api-key/api-key-response.dto.ts | 4 +- .../libs/domain/src/auth/auth.service.spec.ts | 4 +- .../response-dto/admin-signup-response.dto.ts | 2 +- .../src/partner/partner.service.spec.ts | 12 +-- .../src/share/dto/create-shared-link.dto.ts | 2 +- .../src/share/dto/edit-shared-link.dto.ts | 2 +- .../response-dto/shared-link-response.dto.ts | 4 +- server/libs/domain/src/share/share.core.ts | 2 +- .../user/response-dto/user-response.dto.ts | 8 +- .../libs/domain/src/user/user.service.spec.ts | 27 +++--- server/libs/domain/test/fixtures.ts | 83 +++++++++--------- .../libs/infra/src/entities/album.entity.ts | 4 +- .../libs/infra/src/entities/api-key.entity.ts | 4 +- .../infra/src/entities/shared-link.entity.ts | 4 +- server/libs/infra/src/entities/user.entity.ts | 10 +-- .../1685370430343-UserDatesTimestamptz.ts | 16 ++++ web/src/api/open-api/api.ts | 16 ++-- 49 files changed, 172 insertions(+), 132 deletions(-) create mode 100644 server/libs/infra/src/migrations/1685370430343-UserDatesTimestamptz.ts diff --git a/mobile/lib/shared/models/album.dart b/mobile/lib/shared/models/album.dart index 3223a465b9..a3cffa1698 100644 --- a/mobile/lib/shared/models/album.dart +++ b/mobile/lib/shared/models/album.dart @@ -128,8 +128,8 @@ class Album { final Album a = Album( remoteId: dto.id, name: dto.albumName, - createdAt: DateTime.parse(dto.createdAt), - modifiedAt: DateTime.parse(dto.updatedAt), + createdAt: dto.createdAt, + modifiedAt: dto.updatedAt, shared: dto.shared, ); a.owner.value = await db.users.getById(dto.ownerId); diff --git a/mobile/lib/shared/models/user.dart b/mobile/lib/shared/models/user.dart index d6e3d487cd..267503c304 100644 --- a/mobile/lib/shared/models/user.dart +++ b/mobile/lib/shared/models/user.dart @@ -22,9 +22,7 @@ class User { User.fromDto(UserResponseDto dto) : id = dto.id, - updatedAt = dto.updatedAt != null - ? DateTime.parse(dto.updatedAt!).toUtc() - : DateTime.now().toUtc(), + updatedAt = dto.updatedAt, email = dto.email, firstName = dto.firstName, lastName = dto.lastName, diff --git a/mobile/lib/shared/services/sync.service.dart b/mobile/lib/shared/services/sync.service.dart index fae95a84fc..f5471d1546 100644 --- a/mobile/lib/shared/services/sync.service.dart +++ b/mobile/lib/shared/services/sync.service.dart @@ -279,7 +279,7 @@ class SyncService { album.name = dto.albumName; album.shared = dto.shared; - album.modifiedAt = DateTime.parse(dto.updatedAt); + album.modifiedAt = dto.updatedAt; if (album.thumbnail.value?.remoteId != dto.albumThumbnailAssetId) { album.thumbnail.value = await _db.assets .where() @@ -713,5 +713,5 @@ bool _hasAlbumResponseDtoChanged(AlbumResponseDto dto, Album a) { dto.albumThumbnailAssetId != a.thumbnail.value?.remoteId || dto.shared != a.shared || dto.sharedUsers.length != a.sharedUsers.length || - !DateTime.parse(dto.updatedAt).isAtSameMomentAs(a.modifiedAt); + !dto.updatedAt.isAtSameMomentAs(a.modifiedAt); } diff --git a/mobile/openapi/doc/APIKeyResponseDto.md b/mobile/openapi/doc/APIKeyResponseDto.md index 81367fb184f833842afb86665a6fba810b48a82f..7e36819b9b5cfe135f037561f811d34eb873e8e1 100644 GIT binary patch delta 89 zcmey)Jd<%zW-rDNMgTWB4W$48 delta 16 XcmZ3_I*)aO4&&r^j53=e8N(O>F*^k} diff --git a/mobile/openapi/doc/EditSharedLinkDto.md b/mobile/openapi/doc/EditSharedLinkDto.md index a875bdbe7c8dacfdabe6e4fe1f5892327ef3e009..8097bb1183c33af72d99877e33bd2c539a8aee70 100644 GIT binary patch delta 44 tcmcb^@|I5)ffRXss&g8 diff --git a/mobile/openapi/doc/SharedLinkResponseDto.md b/mobile/openapi/doc/SharedLinkResponseDto.md index b8de598bee85cd607e88dc75fca6d594bc6166e8..8ffe0787a8d920c4c9c6e13b549cf022de0bd274 100644 GIT binary patch delta 90 zcmZo+U&g*+1*31YmX=FmNoq)DZmO16tOks&mz$!gP@@1uTv}SG6$P0^sl|>ZT3SE> MJUTXCX57dK03ntgM*si- delta 56 zcmZ3+-on0N1*5i>R&YsCW?s6MmO_mJ5OHZ~rB)PV7Nr(DmVm_&3O0Xb+{g$3@m>+} diff --git a/mobile/openapi/doc/UserResponseDto.md b/mobile/openapi/doc/UserResponseDto.md index 658f5b8d1f3bad08584352fcc2deb75fcc4939a6..c551dd5c77c5ae2dfdecf678ad89cf6cc78e7100 100644 GIT binary patch delta 39 vcmdnUwwG-~8RO*XjQNwx82KiPFey%EW-?;u($Xp|NJ*T`#iTtshv_H)@+%96 delta 84 zcmdnXwvlZ^86#(MQEFmIYKmjYL1u_QGlGdFc}9#c89lwL`GXi2h$CXn_l&bKf#Fbpm!%FIjG(A@l# kwSq+srdlJbI6qHEp-4TsC>3Zxierhoj)MB;CazL80AmCl`Tzg` delta 37 tcmeBC`mVSkgNZG;q$o2leRBa*IrC;lwhETX`#EJMcX4rTp21bZ1_1w<45|PC diff --git a/mobile/openapi/lib/model/album_response_dto.dart b/mobile/openapi/lib/model/album_response_dto.dart index af1ed3f073411330dc7cf1818bcd255ea0bd5a7e..4661d5f281b01ff579c9b1744bf2874a80279426 100644 GIT binary patch delta 172 zcmdm^|4e^_AhWznVo7R9W^SrNa#1Rfnc`St&Bdjl02AJY9o=Ksz9yv>*jyua!b> UVgX#~Mt)&tpwi8U`G2wl0EbyM_W%F@ delta 104 zcmaE+zej(AAhUFENl|8AxTvtibrjSm%W_I% SDk&{Ufj9}IWOFR15gP#8tU2WX delta 80 zcmdlf-!H#Gh)FuQq$o2lT_L$BHL)Z$#j(Vii%UTPBDmR*=^)c&9#-wmfvoqLCx7LT X;e;!j?8hk$5}91Z$+fwI(~u1SJr){) diff --git a/mobile/openapi/lib/model/create_assets_share_link_dto.dart b/mobile/openapi/lib/model/create_assets_share_link_dto.dart index 04d9d2f6ac3a979f3c19e642db07e1a95a4b139b..c8391c1db67ccf43fad5b63c6a9fbf50b499d5cd 100644 GIT binary patch delta 94 zcmaEB-fXd9Av1?dVo7R9W^U@{<;*5r(u#T|`JpAr8k#`bvpCYHbY1hWGG5Az@O delta 44 zcmZp+cx%34A@k%sPL9p1nT@z6&*YciEY4TWHn~?+hBLLIAhRg7*s)~u29aQP0B4~O Ab^rhX diff --git a/mobile/openapi/lib/model/edit_shared_link_dto.dart b/mobile/openapi/lib/model/edit_shared_link_dto.dart index f6f64d76faa0668b0575f5459801a2bf21ba73a6..e6a38994fb7706d05282b59890268fbe905e3814 100644 GIT binary patch delta 114 zcmZ2#xZ7|;4zsXJVo7R9W^SszLTW`pW>IRfW69Fwu_QGlGdEQsxhNILOmQr+=HgONfC+D2#$wMQrB{+4T9T}x38X!X z^DWE_41-IGGV{_kG$%jh@s(D@BY&P(ezF)Z-{yGU%dFurQ#G=R^Ye5Riqzqjs_Q7I mt7|Ii0Ii0A)QW=4qSRu?5-Ww=!~zVBaA}an&C`Xyu>$}hUPJ)^ delta 90 zcmdmC-fO<0n?*Xfq$o2lT_L$BHL)Z$#j(Vii%UTPBDi@Ki#^9=W?tXPjy&?4yLcb6 gPHq&E;e;!l+$kD=rr$Sg`Nb}ZR^NcbB&0E$^3rT_o{ diff --git a/mobile/openapi/lib/model/user_response_dto.dart b/mobile/openapi/lib/model/user_response_dto.dart index 3a637a7e3008bba68764c6c908b9128fc9f3fc1c..f6af4a2519268543f77dc7f43835c4809e1dfbc8 100644 GIT binary patch delta 277 zcmX?R@xy3?IMZYkrpU?D8TlrQGs$wMq~@fSq^3BQOrFRnGI<%(^vOZYE+Q_8C8;5q zxv2`tMX5k0P}-Vn@&^`a4w%H|d^QWl$!FO6CogAL-5kx~&Nz85XX4~VoYIr!xNIkv zaBZ9Hz?~!l*KVr4t z`pNvF(!6ku>N*PQlmCeXPnHxF6eul7fjA5#s;;THxk5C9ZSqHHXKf_)Tna#-4)>zE N4nzR1ce0L*6aZ`VRvpC@N!n#Udt*5I_h%CdYzqB{DZQIby4to+=gy1WU9XK#b z5QHkgP>&$;R4h4oY=|&G^bN_s?Z;BGfy5^43*SLg2q6m45w38U7Rm=#NSujtg;6RE zhALx@z<{I6;<3~)WHLtBiX-74Wh(0#Bz0$fVWHIqPo3Zp!*)hFLLbAljhTL=;?Q_- zl#xD;BMmN-5(dn90$QcUQ*8!w&!9&EZkxMl|Ik-{Bshk^IG^(V;=nlnM~hhnbKk%w zv^2j|c^d2k;%&CRx=p cYStd6KzUJdvz46IycN1vuhP#eeRAuyKMxU3ssI20 diff --git a/mobile/openapi/test/admin_signup_response_dto_test.dart b/mobile/openapi/test/admin_signup_response_dto_test.dart index d71763a3277a93f7afe58f92357e73725b76abc8..62eb488fe1028581ae71fd98a8c663df10509183 100644 GIT binary patch delta 20 bcmcb@evN%Y7Bh!SVo7R9W^U@_0_GF|Pw)p- delta 29 lcmcb{euaHQ7V~6#CU(x`qSVBa)D*{($?q8zC(mWJ1OS~@3ReIC diff --git a/mobile/openapi/test/album_response_dto_test.dart b/mobile/openapi/test/album_response_dto_test.dart index e911f76df45334d0a28ba469dce0c3073c8650a4..bc8c153958d34db5b97e26b14d54b448929ddb90 100644 GIT binary patch delta 42 scmbQtJDqn!EHjTwVo7R9W^SrNa#8B!bIj_K`kcq=3u_QGlGdFc}ITHsH096?W00000 delta 19 acmZqV{LitWkcln0q$o2leRCxf2NM8B;Rb2| diff --git a/mobile/openapi/test/edit_shared_link_dto_test.dart b/mobile/openapi/test/edit_shared_link_dto_test.dart index 4e80594887f657557180db1fa66c7485307c6a5e..1308ee71d20530609bc1a3c6133c5c317c6ccac0 100644 GIT binary patch delta 21 ccmaFL{+xY-6BCC^Vo7R9W^U?c52m?{09Vcje*gdg delta 19 acmaFP{*--#6BAo-Nl|8A`erYtxr_ixU_PU>t?iih|6d)MCey$$?CYn-f^lm;l4R5qbas delta 24 dcmX@bdyIF(3FgVU%*GPxr#ND!zHmKH6$}Pb@EHr;{ZO554HdR delta 40 ucmdnOyMcGZNoMBcqSVPBS=A@Iv6@Z(z{I}!By%w1 { album.sharedUsers.push(...addUsersDto.sharedUserIds.map((id) => ({ id } as UserEntity))); - album.updatedAt = new Date().toISOString(); + album.updatedAt = new Date(); await this.albumRepository.save(album); @@ -71,7 +71,7 @@ export class AlbumRepository implements IAlbumRepository { async removeUser(album: AlbumEntity, userId: string): Promise { album.sharedUsers = album.sharedUsers.filter((user) => user.id !== userId); - album.updatedAt = new Date().toISOString(); + album.updatedAt = new Date(); await this.albumRepository.save(album); } @@ -84,7 +84,7 @@ export class AlbumRepository implements IAlbumRepository { const numRemovedAssets = assetCount - album.assets.length; if (numRemovedAssets > 0) { - album.updatedAt = new Date().toISOString(); + album.updatedAt = new Date(); } await this.albumRepository.save(album, {}); @@ -111,7 +111,7 @@ export class AlbumRepository implements IAlbumRepository { const successfullyAdded = addAssetsDto.assetIds.length - alreadyExisting.length; if (successfullyAdded > 0) { - album.updatedAt = new Date().toISOString(); + album.updatedAt = new Date(); } await this.albumRepository.save(album); diff --git a/server/apps/immich/src/api-v1/album/album.service.spec.ts b/server/apps/immich/src/api-v1/album/album.service.spec.ts index e1b1def7f9..1b24ee8d58 100644 --- a/server/apps/immich/src/api-v1/album/album.service.spec.ts +++ b/server/apps/immich/src/api-v1/album/album.service.spec.ts @@ -32,8 +32,9 @@ describe('Album service', () => { ...authUser, firstName: 'auth', lastName: 'user', - createdAt: 'date', - updatedAt: 'date', + createdAt: new Date('2022-06-19T23:41:36.910Z'), + deletedAt: null, + updatedAt: new Date('2022-06-19T23:41:36.910Z'), profileImagePath: '', shouldChangePassword: false, oauthId: '', @@ -52,8 +53,8 @@ describe('Album service', () => { albumEntity.owner = albumOwner; albumEntity.id = albumId; albumEntity.albumName = 'name'; - albumEntity.createdAt = 'date'; - albumEntity.updatedAt = 'date'; + albumEntity.createdAt = new Date('2022-06-19T23:41:36.910Z'); + albumEntity.updatedAt = new Date('2022-06-19T23:41:36.910Z'); albumEntity.sharedUsers = []; albumEntity.assets = []; albumEntity.albumThumbnailAssetId = null; @@ -67,7 +68,7 @@ describe('Album service', () => { albumEntity.owner = albumOwner; albumEntity.id = albumId; albumEntity.albumName = 'name'; - albumEntity.createdAt = 'date'; + albumEntity.createdAt = new Date('2022-06-19T23:41:36.910Z'); albumEntity.assets = []; albumEntity.albumThumbnailAssetId = null; albumEntity.sharedUsers = [ @@ -86,7 +87,7 @@ describe('Album service', () => { albumEntity.owner = albumOwner; albumEntity.id = albumId; albumEntity.albumName = 'name'; - albumEntity.createdAt = 'date'; + albumEntity.createdAt = new Date('2022-06-19T23:41:36.910Z'); albumEntity.assets = []; albumEntity.albumThumbnailAssetId = null; albumEntity.sharedUsers = [ @@ -109,7 +110,7 @@ describe('Album service', () => { albumEntity.ownerId = '5555'; albumEntity.id = albumId; albumEntity.albumName = 'name'; - albumEntity.createdAt = 'date'; + albumEntity.createdAt = new Date('2022-06-19T23:41:36.910Z'); albumEntity.sharedUsers = []; albumEntity.assets = []; albumEntity.albumThumbnailAssetId = null; @@ -158,8 +159,8 @@ describe('Album service', () => { owner: mapUser(albumOwner), id: albumId, albumName: 'name', - createdAt: 'date', - updatedAt: 'date', + createdAt: new Date('2022-06-19T23:41:36.910Z'), + updatedAt: new Date('2022-06-19T23:41:36.910Z'), sharedUsers: [], assets: [], albumThumbnailAssetId: null, diff --git a/server/apps/immich/src/api-v1/album/dto/create-album-shared-link.dto.ts b/server/apps/immich/src/api-v1/album/dto/create-album-shared-link.dto.ts index a35e527489..bda8439814 100644 --- a/server/apps/immich/src/api-v1/album/dto/create-album-shared-link.dto.ts +++ b/server/apps/immich/src/api-v1/album/dto/create-album-shared-link.dto.ts @@ -1,15 +1,17 @@ import { ApiProperty } from '@nestjs/swagger'; import { ValidateUUID } from 'apps/immich/src/decorators/validate-uuid.decorator'; -import { IsBoolean, IsISO8601, IsOptional, IsString } from 'class-validator'; +import { Type } from 'class-transformer'; +import { IsBoolean, IsDate, IsOptional, IsString } from 'class-validator'; export class CreateAlbumShareLinkDto { @ValidateUUID() albumId!: string; - @IsISO8601() @IsOptional() - @ApiProperty({ format: 'date-time' }) - expiresAt?: string; + @IsDate() + @Type(() => Date) + @ApiProperty() + expiresAt?: Date; @IsBoolean() @IsOptional() diff --git a/server/apps/immich/src/api-v1/asset/dto/create-asset-shared-link.dto.ts b/server/apps/immich/src/api-v1/asset/dto/create-asset-shared-link.dto.ts index 3e6cb0960d..258e7009a9 100644 --- a/server/apps/immich/src/api-v1/asset/dto/create-asset-shared-link.dto.ts +++ b/server/apps/immich/src/api-v1/asset/dto/create-asset-shared-link.dto.ts @@ -1,5 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsArray, IsBoolean, IsNotEmpty, IsOptional, IsString } from 'class-validator'; +import { Type } from 'class-transformer'; +import { IsArray, IsBoolean, IsDate, IsNotEmpty, IsOptional, IsString } from 'class-validator'; export class CreateAssetsShareLinkDto { @IsArray() @@ -17,9 +18,10 @@ export class CreateAssetsShareLinkDto { }) assetIds!: string[]; - @IsString() + @IsDate() + @Type(() => Date) @IsOptional() - expiresAt?: string; + expiresAt?: Date; @IsBoolean() @IsOptional() diff --git a/server/apps/immich/src/api-v1/tag/tag.service.spec.ts b/server/apps/immich/src/api-v1/tag/tag.service.spec.ts index 69c92f82cd..4609bb781e 100644 --- a/server/apps/immich/src/api-v1/tag/tag.service.spec.ts +++ b/server/apps/immich/src/api-v1/tag/tag.service.spec.ts @@ -21,9 +21,9 @@ describe('TagService', () => { email: 'testuser@email.com', profileImagePath: '', shouldChangePassword: true, - createdAt: '2022-12-02T19:29:23.603Z', - deletedAt: undefined, - updatedAt: '2022-12-02T19:29:23.603Z', + createdAt: new Date('2022-12-02T19:29:23.603Z'), + deletedAt: null, + updatedAt: new Date('2022-12-02T19:29:23.603Z'), tags: [], assets: [], oauthId: 'oauth-id-1', diff --git a/server/immich-openapi-specs.json b/server/immich-openapi-specs.json index 2fbf1a22a4..aa674ea604 100644 --- a/server/immich-openapi-specs.json +++ b/server/immich-openapi-specs.json @@ -4196,9 +4196,6 @@ "type": "string", "nullable": true }, - "createdAt": { - "type": "string" - }, "profileImagePath": { "type": "string" }, @@ -4208,11 +4205,17 @@ "isAdmin": { "type": "boolean" }, - "deletedAt": { + "createdAt": { "format": "date-time", "type": "string" }, + "deletedAt": { + "format": "date-time", + "type": "string", + "nullable": true + }, "updatedAt": { + "format": "date-time", "type": "string" }, "oauthId": { @@ -4225,10 +4228,12 @@ "firstName", "lastName", "storageLabel", - "createdAt", "profileImagePath", "shouldChangePassword", "isAdmin", + "createdAt", + "deletedAt", + "updatedAt", "oauthId" ] }, @@ -4536,9 +4541,11 @@ "type": "string" }, "createdAt": { + "format": "date-time", "type": "string" }, "updatedAt": { + "format": "date-time", "type": "string" }, "albumThumbnailAssetId": { @@ -4633,9 +4640,11 @@ "type": "string" }, "createdAt": { + "format": "date-time", "type": "string" }, "updatedAt": { + "format": "date-time", "type": "string" } }, @@ -4800,6 +4809,7 @@ "type": "string" }, "createdAt": { + "format": "date-time", "type": "string" } }, @@ -5396,9 +5406,11 @@ "type": "string" }, "createdAt": { + "format": "date-time", "type": "string" }, "expiresAt": { + "format": "date-time", "type": "string", "nullable": true }, @@ -5441,6 +5453,7 @@ "type": "string" }, "expiresAt": { + "format": "date-time", "type": "string", "nullable": true }, @@ -6255,6 +6268,7 @@ } }, "expiresAt": { + "format": "date-time", "type": "string" }, "allowUpload": { @@ -6393,8 +6407,8 @@ "format": "uuid" }, "expiresAt": { - "type": "string", - "format": "date-time" + "format": "date-time", + "type": "string" }, "allowUpload": { "type": "boolean" diff --git a/server/libs/domain/src/album/album.service.spec.ts b/server/libs/domain/src/album/album.service.spec.ts index 81dcd9926c..005b8e0b10 100644 --- a/server/libs/domain/src/album/album.service.spec.ts +++ b/server/libs/domain/src/album/album.service.spec.ts @@ -128,7 +128,6 @@ describe(AlbumService.name, () => { createdAt: expect.anything(), id: 'album-1', owner: { - createdAt: '2021-01-01', email: 'admin@test.com', firstName: 'admin_first_name', id: 'admin_id', @@ -138,7 +137,9 @@ describe(AlbumService.name, () => { profileImagePath: '', shouldChangePassword: false, storageLabel: 'admin', - updatedAt: '2021-01-01', + createdAt: new Date('2021-01-01'), + deletedAt: null, + updatedAt: new Date('2021-01-01'), }, ownerId: 'admin_id', shared: false, diff --git a/server/libs/domain/src/album/response-dto/album-response.dto.ts b/server/libs/domain/src/album/response-dto/album-response.dto.ts index 9f911e0208..629bc5e070 100644 --- a/server/libs/domain/src/album/response-dto/album-response.dto.ts +++ b/server/libs/domain/src/album/response-dto/album-response.dto.ts @@ -7,8 +7,8 @@ export class AlbumResponseDto { id!: string; ownerId!: string; albumName!: string; - createdAt!: string; - updatedAt!: string; + createdAt!: Date; + updatedAt!: Date; albumThumbnailAssetId!: string | null; shared!: boolean; sharedUsers!: UserResponseDto[]; diff --git a/server/libs/domain/src/api-key/api-key-response.dto.ts b/server/libs/domain/src/api-key/api-key-response.dto.ts index f7b5b5e9f9..541984179c 100644 --- a/server/libs/domain/src/api-key/api-key-response.dto.ts +++ b/server/libs/domain/src/api-key/api-key-response.dto.ts @@ -8,8 +8,8 @@ export class APIKeyCreateResponseDto { export class APIKeyResponseDto { id!: string; name!: string; - createdAt!: string; - updatedAt!: string; + createdAt!: Date; + updatedAt!: Date; } export function mapKey(entity: APIKeyEntity): APIKeyResponseDto { diff --git a/server/libs/domain/src/auth/auth.service.spec.ts b/server/libs/domain/src/auth/auth.service.spec.ts index e0a02c814c..3a3d73415d 100644 --- a/server/libs/domain/src/auth/auth.service.spec.ts +++ b/server/libs/domain/src/auth/auth.service.spec.ts @@ -231,10 +231,10 @@ describe('AuthService', () => { it('should sign up the admin', async () => { userMock.getAdmin.mockResolvedValue(null); - userMock.create.mockResolvedValue({ ...dto, id: 'admin', createdAt: 'today' } as UserEntity); + userMock.create.mockResolvedValue({ ...dto, id: 'admin', createdAt: new Date('2021-01-01') } as UserEntity); await expect(sut.adminSignUp(dto)).resolves.toEqual({ id: 'admin', - createdAt: 'today', + createdAt: new Date('2021-01-01'), email: 'test@immich.com', firstName: 'immich', lastName: 'admin', diff --git a/server/libs/domain/src/auth/response-dto/admin-signup-response.dto.ts b/server/libs/domain/src/auth/response-dto/admin-signup-response.dto.ts index 436405c903..5c2e4413ce 100644 --- a/server/libs/domain/src/auth/response-dto/admin-signup-response.dto.ts +++ b/server/libs/domain/src/auth/response-dto/admin-signup-response.dto.ts @@ -5,7 +5,7 @@ export class AdminSignupResponseDto { email!: string; firstName!: string; lastName!: string; - createdAt!: string; + createdAt!: Date; } export function mapAdminSignupResponse(entity: UserEntity): AdminSignupResponseDto { diff --git a/server/libs/domain/src/partner/partner.service.spec.ts b/server/libs/domain/src/partner/partner.service.spec.ts index b2f55ee3ab..2598567cc6 100644 --- a/server/libs/domain/src/partner/partner.service.spec.ts +++ b/server/libs/domain/src/partner/partner.service.spec.ts @@ -6,8 +6,6 @@ import { PartnerService } from './partner.service'; const responseDto = { admin: { - createdAt: '2021-01-01', - deletedAt: undefined, email: 'admin@test.com', firstName: 'admin_first_name', id: 'admin_id', @@ -16,12 +14,12 @@ const responseDto = { oauthId: '', profileImagePath: '', shouldChangePassword: false, - updatedAt: '2021-01-01', storageLabel: 'admin', + createdAt: new Date('2021-01-01'), + deletedAt: null, + updatedAt: new Date('2021-01-01'), }, user1: { - createdAt: '2021-01-01', - deletedAt: undefined, email: 'immich@test.com', firstName: 'immich_first_name', id: 'user-id', @@ -30,8 +28,10 @@ const responseDto = { oauthId: '', profileImagePath: '', shouldChangePassword: false, - updatedAt: '2021-01-01', storageLabel: null, + createdAt: new Date('2021-01-01'), + deletedAt: null, + updatedAt: new Date('2021-01-01'), }, }; diff --git a/server/libs/domain/src/share/dto/create-shared-link.dto.ts b/server/libs/domain/src/share/dto/create-shared-link.dto.ts index 7f5af8b08e..db82021c6a 100644 --- a/server/libs/domain/src/share/dto/create-shared-link.dto.ts +++ b/server/libs/domain/src/share/dto/create-shared-link.dto.ts @@ -2,7 +2,7 @@ import { AlbumEntity, AssetEntity, SharedLinkType } from '@app/infra/entities'; export class CreateSharedLinkDto { description?: string; - expiresAt?: string; + expiresAt?: Date; type!: SharedLinkType; assets!: AssetEntity[]; album?: AlbumEntity; diff --git a/server/libs/domain/src/share/dto/edit-shared-link.dto.ts b/server/libs/domain/src/share/dto/edit-shared-link.dto.ts index 92ea2619ad..f35f5ed9ac 100644 --- a/server/libs/domain/src/share/dto/edit-shared-link.dto.ts +++ b/server/libs/domain/src/share/dto/edit-shared-link.dto.ts @@ -5,7 +5,7 @@ export class EditSharedLinkDto { description?: string; @IsOptional() - expiresAt?: string | null; + expiresAt?: Date | null; @IsOptional() allowUpload?: boolean; diff --git a/server/libs/domain/src/share/response-dto/shared-link-response.dto.ts b/server/libs/domain/src/share/response-dto/shared-link-response.dto.ts index 2618a264c7..bb76567896 100644 --- a/server/libs/domain/src/share/response-dto/shared-link-response.dto.ts +++ b/server/libs/domain/src/share/response-dto/shared-link-response.dto.ts @@ -12,8 +12,8 @@ export class SharedLinkResponseDto { @ApiProperty({ enumName: 'SharedLinkType', enum: SharedLinkType }) type!: SharedLinkType; - createdAt!: string; - expiresAt!: string | null; + createdAt!: Date; + expiresAt!: Date | null; assets!: AssetResponseDto[]; album?: AlbumResponseDto; allowUpload!: boolean; diff --git a/server/libs/domain/src/share/share.core.ts b/server/libs/domain/src/share/share.core.ts index 4229999a4e..706650a7b7 100644 --- a/server/libs/domain/src/share/share.core.ts +++ b/server/libs/domain/src/share/share.core.ts @@ -23,7 +23,7 @@ export class ShareCore { key: Buffer.from(this.cryptoRepository.randomBytes(50)), description: dto.description, userId, - createdAt: new Date().toISOString(), + createdAt: new Date(), expiresAt: dto.expiresAt ?? null, type: dto.type, assets: dto.assets, diff --git a/server/libs/domain/src/user/response-dto/user-response.dto.ts b/server/libs/domain/src/user/response-dto/user-response.dto.ts index 53da95fd52..6ad8e848c4 100644 --- a/server/libs/domain/src/user/response-dto/user-response.dto.ts +++ b/server/libs/domain/src/user/response-dto/user-response.dto.ts @@ -6,12 +6,12 @@ export class UserResponseDto { firstName!: string; lastName!: string; storageLabel!: string | null; - createdAt!: string; profileImagePath!: string; shouldChangePassword!: boolean; isAdmin!: boolean; - deletedAt?: Date; - updatedAt?: string; + createdAt!: Date; + deletedAt!: Date | null; + updatedAt!: Date; oauthId!: string; } @@ -22,10 +22,10 @@ export function mapUser(entity: UserEntity): UserResponseDto { firstName: entity.firstName, lastName: entity.lastName, storageLabel: entity.storageLabel, - createdAt: entity.createdAt, profileImagePath: entity.profileImagePath, shouldChangePassword: entity.shouldChangePassword, isAdmin: entity.isAdmin, + createdAt: entity.createdAt, deletedAt: entity.deletedAt, updatedAt: entity.updatedAt, oauthId: entity.oauthId, diff --git a/server/libs/domain/src/user/user.service.spec.ts b/server/libs/domain/src/user/user.service.spec.ts index 895d86bfca..1fbe5cccbb 100644 --- a/server/libs/domain/src/user/user.service.spec.ts +++ b/server/libs/domain/src/user/user.service.spec.ts @@ -47,8 +47,9 @@ const adminUser: UserEntity = Object.freeze({ oauthId: '', shouldChangePassword: false, profileImagePath: '', - createdAt: '2021-01-01', - updatedAt: '2021-01-01', + createdAt: new Date('2021-01-01'), + deletedAt: null, + updatedAt: new Date('2021-01-01'), tags: [], assets: [], storageLabel: 'admin', @@ -64,8 +65,9 @@ const immichUser: UserEntity = Object.freeze({ oauthId: '', shouldChangePassword: false, profileImagePath: '', - createdAt: '2021-01-01', - updatedAt: '2021-01-01', + createdAt: new Date('2021-01-01'), + deletedAt: null, + updatedAt: new Date('2021-01-01'), tags: [], assets: [], storageLabel: null, @@ -81,8 +83,9 @@ const updatedImmichUser: UserEntity = Object.freeze({ oauthId: '', shouldChangePassword: true, profileImagePath: '', - createdAt: '2021-01-01', - updatedAt: '2021-01-01', + createdAt: new Date('2021-01-01'), + deletedAt: null, + updatedAt: new Date('2021-01-01'), tags: [], assets: [], storageLabel: null, @@ -91,15 +94,15 @@ const updatedImmichUser: UserEntity = Object.freeze({ const adminUserResponse = Object.freeze({ id: adminUserAuth.id, email: 'admin@test.com', - deletedAt: undefined, firstName: 'admin_first_name', lastName: 'admin_last_name', isAdmin: true, oauthId: '', shouldChangePassword: false, profileImagePath: '', - createdAt: '2021-01-01', - updatedAt: '2021-01-01', + createdAt: new Date('2021-01-01'), + deletedAt: null, + updatedAt: new Date('2021-01-01'), storageLabel: 'admin', }); @@ -140,15 +143,15 @@ describe(UserService.name, () => { { id: adminUserAuth.id, email: 'admin@test.com', - deletedAt: undefined, firstName: 'admin_first_name', lastName: 'admin_last_name', isAdmin: true, oauthId: '', shouldChangePassword: false, profileImagePath: '', - createdAt: '2021-01-01', - updatedAt: '2021-01-01', + createdAt: new Date('2021-01-01'), + deletedAt: null, + updatedAt: new Date('2021-01-01'), storageLabel: 'admin', }, ]); diff --git a/server/libs/domain/test/fixtures.ts b/server/libs/domain/test/fixtures.ts index da420346fe..3d00e7f249 100644 --- a/server/libs/domain/test/fixtures.ts +++ b/server/libs/domain/test/fixtures.ts @@ -85,8 +85,9 @@ export const userEntityStub = { oauthId: '', shouldChangePassword: false, profileImagePath: '', - createdAt: '2021-01-01', - updatedAt: '2021-01-01', + createdAt: new Date('2021-01-01'), + deletedAt: null, + updatedAt: new Date('2021-01-01'), tags: [], assets: [], }), @@ -99,8 +100,9 @@ export const userEntityStub = { oauthId: '', shouldChangePassword: false, profileImagePath: '', - createdAt: '2021-01-01', - updatedAt: '2021-01-01', + createdAt: new Date('2021-01-01'), + deletedAt: null, + updatedAt: new Date('2021-01-01'), tags: [], assets: [], }), @@ -113,8 +115,9 @@ export const userEntityStub = { oauthId: '', shouldChangePassword: false, profileImagePath: '', - createdAt: '2021-01-01', - updatedAt: '2021-01-01', + createdAt: new Date('2021-01-01'), + deletedAt: null, + updatedAt: new Date('2021-01-01'), tags: [], assets: [], }), @@ -317,8 +320,8 @@ export const albumStub = { assets: [], albumThumbnailAsset: null, albumThumbnailAssetId: null, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), + createdAt: new Date(), + updatedAt: new Date(), sharedLinks: [], sharedUsers: [], }), @@ -330,8 +333,8 @@ export const albumStub = { assets: [], albumThumbnailAsset: null, albumThumbnailAssetId: null, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), + createdAt: new Date(), + updatedAt: new Date(), sharedLinks: [], sharedUsers: [userEntityStub.user1], }), @@ -343,8 +346,8 @@ export const albumStub = { assets: [], albumThumbnailAsset: null, albumThumbnailAssetId: null, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), + createdAt: new Date(), + updatedAt: new Date(), sharedLinks: [], sharedUsers: [userEntityStub.admin], }), @@ -356,8 +359,8 @@ export const albumStub = { assets: [assetEntityStub.image], albumThumbnailAsset: null, albumThumbnailAssetId: null, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), + createdAt: new Date(), + updatedAt: new Date(), sharedLinks: [], sharedUsers: [], }), @@ -369,8 +372,8 @@ export const albumStub = { assets: [], albumThumbnailAsset: assetEntityStub.image, albumThumbnailAssetId: assetEntityStub.image.id, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), + createdAt: new Date(), + updatedAt: new Date(), sharedLinks: [], sharedUsers: [], }), @@ -382,8 +385,8 @@ export const albumStub = { assets: [], albumThumbnailAsset: null, albumThumbnailAssetId: null, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), + createdAt: new Date(), + updatedAt: new Date(), sharedLinks: [], sharedUsers: [], }), @@ -395,8 +398,8 @@ export const albumStub = { assets: [assetEntityStub.image], albumThumbnailAsset: assetEntityStub.livePhotoMotionAsset, albumThumbnailAssetId: assetEntityStub.livePhotoMotionAsset.id, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), + createdAt: new Date(), + updatedAt: new Date(), sharedLinks: [], sharedUsers: [], }), @@ -408,8 +411,8 @@ export const albumStub = { assets: [assetEntityStub.image], albumThumbnailAsset: assetEntityStub.image, albumThumbnailAssetId: assetEntityStub.image.id, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), + createdAt: new Date(), + updatedAt: new Date(), sharedLinks: [], sharedUsers: [], }), @@ -468,8 +471,8 @@ const assetResponse: AssetResponseDto = { const albumResponse: AlbumResponseDto = { albumName: 'Test Album', albumThumbnailAssetId: null, - createdAt: today.toISOString(), - updatedAt: today.toISOString(), + createdAt: today, + updatedAt: today, id: 'album-123', ownerId: 'admin_id', owner: mapUser(userEntityStub.admin), @@ -645,8 +648,8 @@ export const sharedLinkStub = { user: userEntityStub.admin, key: Buffer.from('secret-key', 'utf8'), type: SharedLinkType.ALBUM, - createdAt: today.toISOString(), - expiresAt: tomorrow.toISOString(), + createdAt: today, + expiresAt: tomorrow, allowUpload: true, allowDownload: true, showExif: true, @@ -659,8 +662,8 @@ export const sharedLinkStub = { user: userEntityStub.admin, key: Buffer.from('secret-key', 'utf8'), type: SharedLinkType.ALBUM, - createdAt: today.toISOString(), - expiresAt: yesterday.toISOString(), + createdAt: today, + expiresAt: yesterday, allowUpload: true, allowDownload: true, showExif: true, @@ -672,8 +675,8 @@ export const sharedLinkStub = { user: userEntityStub.admin, key: Buffer.from('secret-key', 'utf8'), type: SharedLinkType.ALBUM, - createdAt: today.toISOString(), - expiresAt: tomorrow.toISOString(), + createdAt: today, + expiresAt: tomorrow, allowUpload: false, allowDownload: false, showExif: true, @@ -683,8 +686,8 @@ export const sharedLinkStub = { ownerId: authStub.admin.id, owner: userEntityStub.admin, albumName: 'Test Album', - createdAt: today.toISOString(), - updatedAt: today.toISOString(), + createdAt: today, + updatedAt: today, albumThumbnailAsset: null, albumThumbnailAssetId: null, sharedUsers: [], @@ -763,9 +766,9 @@ export const sharedLinkResponseStub = { allowDownload: true, allowUpload: true, assets: [], - createdAt: today.toISOString(), + createdAt: today, description: undefined, - expiresAt: tomorrow.toISOString(), + expiresAt: tomorrow, id: '123', key: '7365637265742d6b6579', showExif: true, @@ -777,9 +780,9 @@ export const sharedLinkResponseStub = { allowDownload: true, allowUpload: true, assets: [], - createdAt: today.toISOString(), + createdAt: today, description: undefined, - expiresAt: yesterday.toISOString(), + expiresAt: yesterday, id: '123', key: '7365637265742d6b6579', showExif: true, @@ -791,8 +794,8 @@ export const sharedLinkResponseStub = { userId: 'admin_id', key: '7365637265742d6b6579', type: SharedLinkType.ALBUM, - createdAt: today.toISOString(), - expiresAt: tomorrow.toISOString(), + createdAt: today, + expiresAt: tomorrow, description: undefined, allowUpload: false, allowDownload: false, @@ -805,8 +808,8 @@ export const sharedLinkResponseStub = { userId: 'admin_id', key: '7365637265742d6b6579', type: SharedLinkType.ALBUM, - createdAt: today.toISOString(), - expiresAt: tomorrow.toISOString(), + createdAt: today, + expiresAt: tomorrow, description: undefined, allowUpload: false, allowDownload: false, diff --git a/server/libs/infra/src/entities/album.entity.ts b/server/libs/infra/src/entities/album.entity.ts index 6dcebf8a17..6dd4b92740 100644 --- a/server/libs/infra/src/entities/album.entity.ts +++ b/server/libs/infra/src/entities/album.entity.ts @@ -28,10 +28,10 @@ export class AlbumEntity { albumName!: string; @CreateDateColumn({ type: 'timestamptz' }) - createdAt!: string; + createdAt!: Date; @UpdateDateColumn({ type: 'timestamptz' }) - updatedAt!: string; + updatedAt!: Date; @ManyToOne(() => AssetEntity, { nullable: true, onDelete: 'SET NULL', onUpdate: 'CASCADE' }) albumThumbnailAsset!: AssetEntity | null; diff --git a/server/libs/infra/src/entities/api-key.entity.ts b/server/libs/infra/src/entities/api-key.entity.ts index 54108832d8..0c8252fe43 100644 --- a/server/libs/infra/src/entities/api-key.entity.ts +++ b/server/libs/infra/src/entities/api-key.entity.ts @@ -19,8 +19,8 @@ export class APIKeyEntity { userId!: string; @CreateDateColumn({ type: 'timestamptz' }) - createdAt!: string; + createdAt!: Date; @UpdateDateColumn({ type: 'timestamptz' }) - updatedAt!: string; + updatedAt!: Date; } diff --git a/server/libs/infra/src/entities/shared-link.entity.ts b/server/libs/infra/src/entities/shared-link.entity.ts index 231b3a9660..7bca8dddf4 100644 --- a/server/libs/infra/src/entities/shared-link.entity.ts +++ b/server/libs/infra/src/entities/shared-link.entity.ts @@ -35,10 +35,10 @@ export class SharedLinkEntity { type!: SharedLinkType; @CreateDateColumn({ type: 'timestamptz' }) - createdAt!: string; + createdAt!: Date; @Column({ type: 'timestamptz', nullable: true }) - expiresAt!: string | null; + expiresAt!: Date | null; @Column({ type: 'boolean', default: false }) allowUpload!: boolean; diff --git a/server/libs/infra/src/entities/user.entity.ts b/server/libs/infra/src/entities/user.entity.ts index 06c6981263..f175a603c3 100644 --- a/server/libs/infra/src/entities/user.entity.ts +++ b/server/libs/infra/src/entities/user.entity.ts @@ -42,14 +42,14 @@ export class UserEntity { @Column({ default: true }) shouldChangePassword!: boolean; - @CreateDateColumn() - createdAt!: string; + @CreateDateColumn({ type: 'timestamptz' }) + createdAt!: Date; - @DeleteDateColumn() - deletedAt?: Date; + @DeleteDateColumn({ type: 'timestamptz' }) + deletedAt!: Date | null; @UpdateDateColumn({ type: 'timestamptz' }) - updatedAt!: string; + updatedAt!: Date; @OneToMany(() => TagEntity, (tag) => tag.user) tags!: TagEntity[]; diff --git a/server/libs/infra/src/migrations/1685370430343-UserDatesTimestamptz.ts b/server/libs/infra/src/migrations/1685370430343-UserDatesTimestamptz.ts new file mode 100644 index 0000000000..0a70e012d8 --- /dev/null +++ b/server/libs/infra/src/migrations/1685370430343-UserDatesTimestamptz.ts @@ -0,0 +1,16 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class UserDatesTimestamptz1685370430343 implements MigrationInterface { + name = 'UserDatesTimestamptz1685370430343' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE`); + await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "deletedAt" TYPE TIMESTAMP WITH TIME ZONE`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "deletedAt" TYPE TIMESTAMP`); + await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "createdAt" TYPE TIMESTAMP`); + } + +} diff --git a/web/src/api/open-api/api.ts b/web/src/api/open-api/api.ts index 269fd55c10..cc4a6355e9 100644 --- a/web/src/api/open-api/api.ts +++ b/web/src/api/open-api/api.ts @@ -2712,12 +2712,6 @@ export interface UserResponseDto { * @memberof UserResponseDto */ 'storageLabel': string | null; - /** - * - * @type {string} - * @memberof UserResponseDto - */ - 'createdAt': string; /** * * @type {string} @@ -2741,13 +2735,19 @@ export interface UserResponseDto { * @type {string} * @memberof UserResponseDto */ - 'deletedAt'?: string; + 'createdAt': string; /** * * @type {string} * @memberof UserResponseDto */ - 'updatedAt'?: string; + 'deletedAt': string | null; + /** + * + * @type {string} + * @memberof UserResponseDto + */ + 'updatedAt': string; /** * * @type {string}