Skip to content

Commit 7f73f2e

Browse files
committed
Merge branch 'feat/nullable-dates' of https://github.com/immich-app/immich into feat/inline-offline-check
2 parents df56b41 + 921ef80 commit 7f73f2e

File tree

13 files changed

+108
-74
lines changed

13 files changed

+108
-74
lines changed
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
blank_issues_enabled: false
21
blank_pull_request_template_enabled: false

.github/PULL_REQUEST_TEMPLATE/pull_request_template.md

Lines changed: 0 additions & 22 deletions
This file was deleted.

.github/pull_request_template.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
## Description
2+
3+
<!--- Describe your changes in detail -->
4+
<!--- Why is this change required? What problem does it solve? -->
5+
<!--- If it fixes an open issue, please link to the issue here. -->
6+
7+
Fixes # (issue)
8+
9+
## How Has This Been Tested?
10+
11+
<!-- Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration -->
12+
13+
- [ ] Test A
14+
- [ ] Test B
15+
16+
<details><summary><h2>Screenshots (if appropriate)</h2></summary>
17+
18+
<!-- Images go below this line. -->
19+
20+
</details>
21+
22+
<!-- API endpoint changes (if relevant)
23+
## API Changes
24+
The `/api/something` endpoint is now `/api/something-else`
25+
-->
26+
27+
## Checklist:
28+
29+
- [ ] I have performed a self-review of my own code
30+
- [ ] I have made corresponding changes to the documentation if applicable
31+
- [ ] I have no unrelated changes in the PR.
32+
- [ ] I have confirmed that any new dependencies are strictly necessary.
33+
- [ ] I have written tests for new code (if applicable)
34+
- [ ] I have followed naming conventions/patterns in the surrounding code
35+
- [ ] All code in `src/services` uses repositories implementations for database calls, filesystem operations, etc.
36+
- [ ] All code in `src/repositories/` is pretty basic/simple and does not have any immich specific logic (that belongs in `src/services`)

docs/docs/install/unraid.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ alt="Select Plugins > Compose.Manager > Add New Stack > Label it Immich"
7272
</ul>
7373
</details>
7474

75-
5. Click "**Save Changes**", you will be promoted to edit stack UI labels, just leave this blank and click "**Ok**"
75+
5. Click "**Save Changes**", you will be prompted to edit stack UI labels, just leave this blank and click "**Ok**"
7676
6. Select the cog ⚙️ next to Immich, click "**Edit Stack**", then click "**Env File**"
7777
7. Paste the entire contents of the [Immich example.env](https://github.com/immich-app/immich/releases/latest/download/example.env) file into the Unraid editor, then **before saving** edit the following:
7878

server/src/dtos/asset-response.dto.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -113,22 +113,12 @@ const hexOrBufferToBase64 = (encoded: string | Buffer) => {
113113
export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): AssetResponseDto {
114114
const { stripMetadata = false, withStack = false } = options;
115115

116-
if (entity.localDateTime === null) {
117-
throw new Error(`Asset ${entity.id} has no localDateTime`);
118-
}
119-
if (entity.fileCreatedAt === null) {
120-
throw new Error(`Asset ${entity.id} has no fileCreatedAt`);
121-
}
122-
if (entity.fileModifiedAt === null) {
123-
throw new Error(`Asset ${entity.id} has no fileModifiedAt`);
124-
}
125-
126116
if (stripMetadata) {
127117
const sanitizedAssetResponse: SanitizedAssetResponseDto = {
128118
id: entity.id,
129119
type: entity.type,
130120
originalMimeType: mimeTypes.lookup(entity.originalFileName),
131-
thumbhash: entity.thumbhash?.toString('base64') ?? null,
121+
thumbhash: entity.thumbhash ? hexOrBufferToBase64(entity.thumbhash) : null,
132122
localDateTime: entity.localDateTime,
133123
duration: entity.duration ?? '0:00:00.00000',
134124
livePhotoVideoId: entity.livePhotoVideoId,

server/src/entities/asset.entity.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,13 @@ export class AssetEntity {
101101

102102
@Index('idx_asset_file_created_at')
103103
@Column({ type: 'timestamptz', nullable: true, default: null })
104-
fileCreatedAt!: Date | null;
104+
fileCreatedAt!: Date;
105105

106106
@Column({ type: 'timestamptz', nullable: true, default: null })
107-
localDateTime!: Date | null;
107+
localDateTime!: Date;
108108

109109
@Column({ type: 'timestamptz', nullable: true, default: null })
110-
fileModifiedAt!: Date | null;
110+
fileModifiedAt!: Date;
111111

112112
@Column({ type: 'boolean', default: false })
113113
isFavorite!: boolean;
@@ -180,6 +180,12 @@ export class AssetEntity {
180180
duplicateId!: string | null;
181181
}
182182

183+
export type AssetEntityPlaceholder = AssetEntity & {
184+
fileCreatedAt: Date | null;
185+
fileModifiedAt: Date | null;
186+
localDateTime: Date | null;
187+
};
188+
183189
export function withExif<O>(qb: SelectQueryBuilder<DB, 'assets', O>) {
184190
return qb.leftJoin('exif', 'assets.id', 'exif.assetId').select((eb) => eb.fn.toJson(eb.table('exif')).as('exifInfo'));
185191
}

server/src/queries/asset.repository.sql

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ with
4949
and "assets"."deletedAt" is null
5050
and "assets"."fileCreatedAt" is not null
5151
and "assets"."fileModifiedAt" is not null
52+
and "assets"."localDateTime" is not null
5253
order by
5354
(assets."localDateTime" at time zone 'UTC')::date desc
5455
limit
@@ -461,8 +462,6 @@ from
461462
where
462463
"assets"."ownerId" = any ($1::uuid[])
463464
and "isVisible" = $2
464-
and "assets"."fileCreatedAt" is not null
465-
and "assets"."fileModifiedAt" is not null
466465
and "updatedAt" > $3
467466
limit
468467
$4

server/src/queries/map.repository.sql

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
-- NOTE: This file is auto generated by ./sql-generator
2+
3+
-- MapRepository.getMapMarkers
4+
select
5+
"id",
6+
"exif"."latitude" as "lat",
7+
"exif"."longitude" as "lon",
8+
"exif"."city",
9+
"exif"."state",
10+
"exif"."country"
11+
from
12+
"assets"
13+
inner join "exif" on "assets"."id" = "exif"."assetId"
14+
and "exif"."latitude" is not null
15+
and "exif"."longitude" is not null
16+
left join "albums_assets_assets" on "assets"."id" = "albums_assets_assets"."assetsId"
17+
where
18+
"isVisible" = $1
19+
and "deletedAt" is null
20+
and "ownerId" in ($2)
21+
order by
22+
"fileCreatedAt" desc

server/src/repositories/asset.repository.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { Injectable } from '@nestjs/common';
2-
import { Insertable, Kysely, UpdateResult, Updateable, sql } from 'kysely';
2+
import { Insertable, Kysely, NotNull, UpdateResult, Updateable, sql } from 'kysely';
33
import { isEmpty, isUndefined, omitBy } from 'lodash';
44
import { InjectKysely } from 'nestjs-kysely';
55
import { ASSET_FILE_CONFLICT_KEYS, EXIF_CONFLICT_KEYS, JOB_STATUS_CONFLICT_KEYS } from 'src/constants';
66
import { AssetFiles, AssetJobStatus, Assets, DB, Exif } from 'src/db';
77
import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators';
88
import {
99
AssetEntity,
10+
AssetEntityPlaceholder,
1011
hasPeople,
1112
searchAssetBuilder,
1213
truncatedDate,
@@ -80,8 +81,12 @@ export class AssetRepository implements IAssetRepository {
8081
.execute();
8182
}
8283

83-
create(asset: Insertable<Assets>): Promise<AssetEntity> {
84-
return this.db.insertInto('assets').values(asset).returningAll().executeTakeFirst() as any as Promise<AssetEntity>;
84+
create(asset: Insertable<Assets>): Promise<AssetEntityPlaceholder> {
85+
return this.db
86+
.insertInto('assets')
87+
.values(asset)
88+
.returningAll()
89+
.executeTakeFirst() as any as Promise<AssetEntityPlaceholder>;
8590
}
8691

8792
createAll(assets: Insertable<Assets>[]): Promise<AssetEntity[]> {
@@ -128,13 +133,17 @@ export class AssetRepository implements IAssetRepository {
128133
.where('assets.deletedAt', 'is', null)
129134
.where('assets.fileCreatedAt', 'is not', null)
130135
.where('assets.fileModifiedAt', 'is not', null)
136+
.where('assets.localDateTime', 'is not', null)
131137
.orderBy(sql`(assets."localDateTime" at time zone 'UTC')::date`, 'desc')
132138
.limit(20)
133139
.as('a'),
134140
(join) => join.onTrue(),
135141
)
136142
.innerJoin('exif', 'a.id', 'exif.assetId')
137143
.selectAll('a')
144+
.$narrowType<{ fileCreatedAt: NotNull }>()
145+
.$narrowType<{ fileModifiedAt: NotNull }>()
146+
.$narrowType<{ localDateTime: NotNull }>()
138147
.select((eb) => eb.fn.toJson(eb.table('exif')).as('exifInfo')),
139148
)
140149
.selectFrom('res')
@@ -857,8 +866,6 @@ export class AssetRepository implements IAssetRepository {
857866
.select((eb) => eb.fn.toJson(eb.table('stacked_assets')).as('stack'))
858867
.where('assets.ownerId', '=', anyUuid(options.userIds))
859868
.where('isVisible', '=', true)
860-
.where('assets.fileCreatedAt', 'is not', null)
861-
.where('assets.fileModifiedAt', 'is not', null)
862869
.where('updatedAt', '>', options.updatedAfter)
863870
.limit(options.limit)
864871
.execute() as any as Promise<AssetEntity[]>;

server/src/repositories/config.repository.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ const getEnv = (): EnvData => {
227227
}
228228

229229
const driverOptions = {
230+
...parsedOptions,
230231
onnotice: (notice: Notice) => {
231232
if (notice['severity'] !== 'NOTICE') {
232233
console.warn('Postgres notice:', notice);
@@ -247,7 +248,9 @@ const getEnv = (): EnvData => {
247248
serialize: (value: number) => value.toString(),
248249
},
249250
},
250-
...parsedOptions,
251+
connection: {
252+
TimeZone: 'UTC',
253+
},
251254
};
252255

253256
return {

0 commit comments

Comments
 (0)