@igstack/app-catalog-backend-core 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/db/client.d.mts +54 -1
- package/dist/db/client.d.mts.map +1 -1
- package/dist/db/client.mjs +88 -7
- package/dist/db/client.mjs.map +1 -1
- package/dist/db/index.d.mts +1 -1
- package/dist/db/syncAppCatalog.mjs +2 -1
- package/dist/db/syncAppCatalog.mjs.map +1 -1
- package/dist/generated/prisma/internal/class.mjs +4 -4
- package/dist/generated/prisma/internal/class.mjs.map +1 -1
- package/dist/generated/prisma/internal/prismaNamespace.d.mts +1 -0
- package/dist/generated/prisma/internal/prismaNamespace.d.mts.map +1 -1
- package/dist/generated/prisma/models/DbResource.d.mts +43 -1
- package/dist/generated/prisma/models/DbResource.d.mts.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/middleware/database.d.mts +1 -0
- package/dist/middleware/database.d.mts.map +1 -1
- package/dist/middleware/database.mjs +10 -13
- package/dist/middleware/database.mjs.map +1 -1
- package/dist/modules/appCatalog/freshness.mjs +5 -3
- package/dist/modules/appCatalog/freshness.mjs.map +1 -1
- package/dist/modules/appCatalog/service.mjs +5 -1
- package/dist/modules/appCatalog/service.mjs.map +1 -1
- package/dist/modules/assets/assetCache.mjs +25 -0
- package/dist/modules/assets/assetCache.mjs.map +1 -0
- package/dist/modules/assets/assetRestController.d.mts.map +1 -1
- package/dist/modules/assets/assetRestController.mjs +17 -6
- package/dist/modules/assets/assetRestController.mjs.map +1 -1
- package/dist/modules/assets/assetUtils.mjs +1 -0
- package/dist/modules/assets/assetUtils.mjs.map +1 -1
- package/dist/modules/assets/screenshotRestController.d.mts.map +1 -1
- package/dist/modules/assets/screenshotRestController.mjs +7 -2
- package/dist/modules/assets/screenshotRestController.mjs.map +1 -1
- package/dist/modules/assets/upsertAsset.mjs +7 -1
- package/dist/modules/assets/upsertAsset.mjs.map +1 -1
- package/dist/modules/icons/iconRestController.d.mts.map +1 -1
- package/dist/modules/icons/iconRestController.mjs +7 -2
- package/dist/modules/icons/iconRestController.mjs.map +1 -1
- package/dist/modules/lighthouseKeeper/tools.d.mts.map +1 -1
- package/dist/modules/lighthouseKeeper/tools.mjs +5 -5
- package/dist/modules/lighthouseKeeper/tools.mjs.map +1 -1
- package/dist/types/common/appCatalogTypes.d.mts +8 -0
- package/dist/types/common/appCatalogTypes.d.mts.map +1 -1
- package/package.json +4 -4
- package/prisma/schema.prisma +5 -2
- package/src/__tests__/assetMime.test.ts +74 -0
- package/src/__tests__/dbSchema.test.ts +129 -0
- package/src/__tests__/dbSchemaAdapter.test.ts +118 -0
- package/src/__tests__/freshness.test.ts +56 -17
- package/src/__tests__/iconCache.test.ts +81 -0
- package/src/db/client.ts +118 -12
- package/src/db/index.ts +10 -1
- package/src/db/syncAppCatalog.ts +3 -0
- package/src/generated/prisma/internal/class.ts +4 -4
- package/src/generated/prisma/internal/prismaNamespace.ts +1 -0
- package/src/generated/prisma/internal/prismaNamespaceBrowser.ts +1 -0
- package/src/generated/prisma/models/DbResource.ts +44 -1
- package/src/index.ts +4 -0
- package/src/middleware/database.ts +20 -19
- package/src/modules/admin/chat/createDatabaseTools.ts +6 -2
- package/src/modules/appCatalog/freshness.ts +29 -11
- package/src/modules/appCatalog/service.ts +9 -4
- package/src/modules/assets/assetCache.ts +30 -0
- package/src/modules/assets/assetRestController.ts +25 -2
- package/src/modules/assets/assetUtils.ts +1 -0
- package/src/modules/assets/screenshotRestController.ts +13 -1
- package/src/modules/assets/upsertAsset.ts +8 -0
- package/src/modules/icons/iconRestController.ts +11 -1
- package/src/modules/lighthouseKeeper/tools.ts +6 -7
- package/src/types/common/appCatalogTypes.ts +8 -0
|
@@ -44,6 +44,7 @@ export type DbResourceMinAggregateOutputType = {
|
|
|
44
44
|
accessComments: string | null
|
|
45
45
|
lastCheckedAt: Date | null
|
|
46
46
|
nextCheckAfter: Date | null
|
|
47
|
+
lastContentChangeAt: Date | null
|
|
47
48
|
createdAt: Date | null
|
|
48
49
|
updatedAt: Date | null
|
|
49
50
|
}
|
|
@@ -66,6 +67,7 @@ export type DbResourceMaxAggregateOutputType = {
|
|
|
66
67
|
accessComments: string | null
|
|
67
68
|
lastCheckedAt: Date | null
|
|
68
69
|
nextCheckAfter: Date | null
|
|
70
|
+
lastContentChangeAt: Date | null
|
|
69
71
|
createdAt: Date | null
|
|
70
72
|
updatedAt: Date | null
|
|
71
73
|
}
|
|
@@ -101,6 +103,7 @@ export type DbResourceCountAggregateOutputType = {
|
|
|
101
103
|
extra:PrismaJson.SubResourceExtra
|
|
102
104
|
lastCheckedAt: number
|
|
103
105
|
nextCheckAfter: number
|
|
106
|
+
lastContentChangeAt: number
|
|
104
107
|
createdAt: number
|
|
105
108
|
updatedAt: number
|
|
106
109
|
_all: number
|
|
@@ -125,6 +128,7 @@ export type DbResourceMinAggregateInputType = {
|
|
|
125
128
|
accessComments?: true
|
|
126
129
|
lastCheckedAt?: true
|
|
127
130
|
nextCheckAfter?: true
|
|
131
|
+
lastContentChangeAt?: true
|
|
128
132
|
createdAt?: true
|
|
129
133
|
updatedAt?: true
|
|
130
134
|
}
|
|
@@ -147,6 +151,7 @@ export type DbResourceMaxAggregateInputType = {
|
|
|
147
151
|
accessComments?: true
|
|
148
152
|
lastCheckedAt?: true
|
|
149
153
|
nextCheckAfter?: true
|
|
154
|
+
lastContentChangeAt?: true
|
|
150
155
|
createdAt?: true
|
|
151
156
|
updatedAt?: true
|
|
152
157
|
}
|
|
@@ -182,6 +187,7 @@ export type DbResourceCountAggregateInputType = {
|
|
|
182
187
|
extra?: true
|
|
183
188
|
lastCheckedAt?: true
|
|
184
189
|
nextCheckAfter?: true
|
|
190
|
+
lastContentChangeAt?: true
|
|
185
191
|
createdAt?: true
|
|
186
192
|
updatedAt?: true
|
|
187
193
|
_all?: true
|
|
@@ -290,6 +296,7 @@ export type DbResourceGroupByOutputType = {
|
|
|
290
296
|
extra:PrismaJson.SubResourceExtra | null
|
|
291
297
|
lastCheckedAt: Date | null
|
|
292
298
|
nextCheckAfter: Date | null
|
|
299
|
+
lastContentChangeAt: Date | null
|
|
293
300
|
createdAt: Date
|
|
294
301
|
updatedAt: Date
|
|
295
302
|
_count: DbResourceCountAggregateOutputType | null
|
|
@@ -346,6 +353,7 @@ export type DbResourceWhereInput = {
|
|
|
346
353
|
extra?: Prisma.JsonNullableFilter<"DbResource">
|
|
347
354
|
lastCheckedAt?: Prisma.DateTimeNullableFilter<"DbResource"> | Date | string | null
|
|
348
355
|
nextCheckAfter?: Prisma.DateTimeNullableFilter<"DbResource"> | Date | string | null
|
|
356
|
+
lastContentChangeAt?: Prisma.DateTimeNullableFilter<"DbResource"> | Date | string | null
|
|
349
357
|
createdAt?: Prisma.DateTimeFilter<"DbResource"> | Date | string
|
|
350
358
|
updatedAt?: Prisma.DateTimeFilter<"DbResource"> | Date | string
|
|
351
359
|
sourceRefs?: Prisma.SourceReferenceListRelationFilter
|
|
@@ -384,6 +392,7 @@ export type DbResourceOrderByWithRelationInput = {
|
|
|
384
392
|
extra?: Prisma.SortOrderInput | Prisma.SortOrder
|
|
385
393
|
lastCheckedAt?: Prisma.SortOrderInput | Prisma.SortOrder
|
|
386
394
|
nextCheckAfter?: Prisma.SortOrderInput | Prisma.SortOrder
|
|
395
|
+
lastContentChangeAt?: Prisma.SortOrderInput | Prisma.SortOrder
|
|
387
396
|
createdAt?: Prisma.SortOrder
|
|
388
397
|
updatedAt?: Prisma.SortOrder
|
|
389
398
|
sourceRefs?: Prisma.SourceReferenceOrderByRelationAggregateInput
|
|
@@ -425,6 +434,7 @@ export type DbResourceWhereUniqueInput = Prisma.AtLeast<{
|
|
|
425
434
|
extra?: Prisma.JsonNullableFilter<"DbResource">
|
|
426
435
|
lastCheckedAt?: Prisma.DateTimeNullableFilter<"DbResource"> | Date | string | null
|
|
427
436
|
nextCheckAfter?: Prisma.DateTimeNullableFilter<"DbResource"> | Date | string | null
|
|
437
|
+
lastContentChangeAt?: Prisma.DateTimeNullableFilter<"DbResource"> | Date | string | null
|
|
428
438
|
createdAt?: Prisma.DateTimeFilter<"DbResource"> | Date | string
|
|
429
439
|
updatedAt?: Prisma.DateTimeFilter<"DbResource"> | Date | string
|
|
430
440
|
sourceRefs?: Prisma.SourceReferenceListRelationFilter
|
|
@@ -463,6 +473,7 @@ export type DbResourceOrderByWithAggregationInput = {
|
|
|
463
473
|
extra?: Prisma.SortOrderInput | Prisma.SortOrder
|
|
464
474
|
lastCheckedAt?: Prisma.SortOrderInput | Prisma.SortOrder
|
|
465
475
|
nextCheckAfter?: Prisma.SortOrderInput | Prisma.SortOrder
|
|
476
|
+
lastContentChangeAt?: Prisma.SortOrderInput | Prisma.SortOrder
|
|
466
477
|
createdAt?: Prisma.SortOrder
|
|
467
478
|
updatedAt?: Prisma.SortOrder
|
|
468
479
|
_count?: Prisma.DbResourceCountOrderByAggregateInput
|
|
@@ -504,6 +515,7 @@ export type DbResourceScalarWhereWithAggregatesInput = {
|
|
|
504
515
|
extra?: Prisma.JsonNullableWithAggregatesFilter<"DbResource">
|
|
505
516
|
lastCheckedAt?: Prisma.DateTimeNullableWithAggregatesFilter<"DbResource"> | Date | string | null
|
|
506
517
|
nextCheckAfter?: Prisma.DateTimeNullableWithAggregatesFilter<"DbResource"> | Date | string | null
|
|
518
|
+
lastContentChangeAt?: Prisma.DateTimeNullableWithAggregatesFilter<"DbResource"> | Date | string | null
|
|
507
519
|
createdAt?: Prisma.DateTimeWithAggregatesFilter<"DbResource"> | Date | string
|
|
508
520
|
updatedAt?: Prisma.DateTimeWithAggregatesFilter<"DbResource"> | Date | string
|
|
509
521
|
}
|
|
@@ -538,6 +550,7 @@ export type DbResourceCreateInput = {
|
|
|
538
550
|
extra?:PrismaJson.SubResourceExtra | Prisma.NullableJsonNullValueInput
|
|
539
551
|
lastCheckedAt?: Date | string | null
|
|
540
552
|
nextCheckAfter?: Date | string | null
|
|
553
|
+
lastContentChangeAt?: Date | string | null
|
|
541
554
|
createdAt?: Date | string
|
|
542
555
|
updatedAt?: Date | string
|
|
543
556
|
sourceRefs?: Prisma.SourceReferenceCreateNestedManyWithoutResourceInput
|
|
@@ -576,6 +589,7 @@ export type DbResourceUncheckedCreateInput = {
|
|
|
576
589
|
extra?:PrismaJson.SubResourceExtra | Prisma.NullableJsonNullValueInput
|
|
577
590
|
lastCheckedAt?: Date | string | null
|
|
578
591
|
nextCheckAfter?: Date | string | null
|
|
592
|
+
lastContentChangeAt?: Date | string | null
|
|
579
593
|
createdAt?: Date | string
|
|
580
594
|
updatedAt?: Date | string
|
|
581
595
|
sourceRefs?: Prisma.SourceReferenceUncheckedCreateNestedManyWithoutResourceInput
|
|
@@ -612,6 +626,7 @@ export type DbResourceUpdateInput = {
|
|
|
612
626
|
extra?:PrismaJson.SubResourceExtra | Prisma.NullableJsonNullValueInput
|
|
613
627
|
lastCheckedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
614
628
|
nextCheckAfter?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
629
|
+
lastContentChangeAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
615
630
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
616
631
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
617
632
|
sourceRefs?: Prisma.SourceReferenceUpdateManyWithoutResourceNestedInput
|
|
@@ -650,6 +665,7 @@ export type DbResourceUncheckedUpdateInput = {
|
|
|
650
665
|
extra?:PrismaJson.SubResourceExtra | Prisma.NullableJsonNullValueInput
|
|
651
666
|
lastCheckedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
652
667
|
nextCheckAfter?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
668
|
+
lastContentChangeAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
653
669
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
654
670
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
655
671
|
sourceRefs?: Prisma.SourceReferenceUncheckedUpdateManyWithoutResourceNestedInput
|
|
@@ -687,6 +703,7 @@ export type DbResourceCreateManyInput = {
|
|
|
687
703
|
extra?:PrismaJson.SubResourceExtra | Prisma.NullableJsonNullValueInput
|
|
688
704
|
lastCheckedAt?: Date | string | null
|
|
689
705
|
nextCheckAfter?: Date | string | null
|
|
706
|
+
lastContentChangeAt?: Date | string | null
|
|
690
707
|
createdAt?: Date | string
|
|
691
708
|
updatedAt?: Date | string
|
|
692
709
|
}
|
|
@@ -721,6 +738,7 @@ export type DbResourceUpdateManyMutationInput = {
|
|
|
721
738
|
extra?:PrismaJson.SubResourceExtra | Prisma.NullableJsonNullValueInput
|
|
722
739
|
lastCheckedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
723
740
|
nextCheckAfter?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
741
|
+
lastContentChangeAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
724
742
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
725
743
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
726
744
|
}
|
|
@@ -756,6 +774,7 @@ export type DbResourceUncheckedUpdateManyInput = {
|
|
|
756
774
|
extra?:PrismaJson.SubResourceExtra | Prisma.NullableJsonNullValueInput
|
|
757
775
|
lastCheckedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
758
776
|
nextCheckAfter?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
777
|
+
lastContentChangeAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
759
778
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
760
779
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
761
780
|
}
|
|
@@ -806,6 +825,7 @@ export type DbResourceCountOrderByAggregateInput = {
|
|
|
806
825
|
extra?: Prisma.SortOrder
|
|
807
826
|
lastCheckedAt?: Prisma.SortOrder
|
|
808
827
|
nextCheckAfter?: Prisma.SortOrder
|
|
828
|
+
lastContentChangeAt?: Prisma.SortOrder
|
|
809
829
|
createdAt?: Prisma.SortOrder
|
|
810
830
|
updatedAt?: Prisma.SortOrder
|
|
811
831
|
}
|
|
@@ -828,6 +848,7 @@ export type DbResourceMaxOrderByAggregateInput = {
|
|
|
828
848
|
accessComments?: Prisma.SortOrder
|
|
829
849
|
lastCheckedAt?: Prisma.SortOrder
|
|
830
850
|
nextCheckAfter?: Prisma.SortOrder
|
|
851
|
+
lastContentChangeAt?: Prisma.SortOrder
|
|
831
852
|
createdAt?: Prisma.SortOrder
|
|
832
853
|
updatedAt?: Prisma.SortOrder
|
|
833
854
|
}
|
|
@@ -850,6 +871,7 @@ export type DbResourceMinOrderByAggregateInput = {
|
|
|
850
871
|
accessComments?: Prisma.SortOrder
|
|
851
872
|
lastCheckedAt?: Prisma.SortOrder
|
|
852
873
|
nextCheckAfter?: Prisma.SortOrder
|
|
874
|
+
lastContentChangeAt?: Prisma.SortOrder
|
|
853
875
|
createdAt?: Prisma.SortOrder
|
|
854
876
|
updatedAt?: Prisma.SortOrder
|
|
855
877
|
}
|
|
@@ -1024,6 +1046,7 @@ export type DbResourceCreateWithoutChildrenInput = {
|
|
|
1024
1046
|
extra?:PrismaJson.SubResourceExtra | Prisma.NullableJsonNullValueInput
|
|
1025
1047
|
lastCheckedAt?: Date | string | null
|
|
1026
1048
|
nextCheckAfter?: Date | string | null
|
|
1049
|
+
lastContentChangeAt?: Date | string | null
|
|
1027
1050
|
createdAt?: Date | string
|
|
1028
1051
|
updatedAt?: Date | string
|
|
1029
1052
|
sourceRefs?: Prisma.SourceReferenceCreateNestedManyWithoutResourceInput
|
|
@@ -1061,6 +1084,7 @@ export type DbResourceUncheckedCreateWithoutChildrenInput = {
|
|
|
1061
1084
|
extra?:PrismaJson.SubResourceExtra | Prisma.NullableJsonNullValueInput
|
|
1062
1085
|
lastCheckedAt?: Date | string | null
|
|
1063
1086
|
nextCheckAfter?: Date | string | null
|
|
1087
|
+
lastContentChangeAt?: Date | string | null
|
|
1064
1088
|
createdAt?: Date | string
|
|
1065
1089
|
updatedAt?: Date | string
|
|
1066
1090
|
sourceRefs?: Prisma.SourceReferenceUncheckedCreateNestedManyWithoutResourceInput
|
|
@@ -1101,6 +1125,7 @@ export type DbResourceCreateWithoutParentInput = {
|
|
|
1101
1125
|
extra?:PrismaJson.SubResourceExtra | Prisma.NullableJsonNullValueInput
|
|
1102
1126
|
lastCheckedAt?: Date | string | null
|
|
1103
1127
|
nextCheckAfter?: Date | string | null
|
|
1128
|
+
lastContentChangeAt?: Date | string | null
|
|
1104
1129
|
createdAt?: Date | string
|
|
1105
1130
|
updatedAt?: Date | string
|
|
1106
1131
|
sourceRefs?: Prisma.SourceReferenceCreateNestedManyWithoutResourceInput
|
|
@@ -1137,6 +1162,7 @@ export type DbResourceUncheckedCreateWithoutParentInput = {
|
|
|
1137
1162
|
extra?:PrismaJson.SubResourceExtra | Prisma.NullableJsonNullValueInput
|
|
1138
1163
|
lastCheckedAt?: Date | string | null
|
|
1139
1164
|
nextCheckAfter?: Date | string | null
|
|
1165
|
+
lastContentChangeAt?: Date | string | null
|
|
1140
1166
|
createdAt?: Date | string
|
|
1141
1167
|
updatedAt?: Date | string
|
|
1142
1168
|
sourceRefs?: Prisma.SourceReferenceUncheckedCreateNestedManyWithoutResourceInput
|
|
@@ -1194,6 +1220,7 @@ export type DbResourceUpdateWithoutChildrenInput = {
|
|
|
1194
1220
|
extra?:PrismaJson.SubResourceExtra | Prisma.NullableJsonNullValueInput
|
|
1195
1221
|
lastCheckedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
1196
1222
|
nextCheckAfter?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
1223
|
+
lastContentChangeAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
1197
1224
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
1198
1225
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
1199
1226
|
sourceRefs?: Prisma.SourceReferenceUpdateManyWithoutResourceNestedInput
|
|
@@ -1231,6 +1258,7 @@ export type DbResourceUncheckedUpdateWithoutChildrenInput = {
|
|
|
1231
1258
|
extra?:PrismaJson.SubResourceExtra | Prisma.NullableJsonNullValueInput
|
|
1232
1259
|
lastCheckedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
1233
1260
|
nextCheckAfter?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
1261
|
+
lastContentChangeAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
1234
1262
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
1235
1263
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
1236
1264
|
sourceRefs?: Prisma.SourceReferenceUncheckedUpdateManyWithoutResourceNestedInput
|
|
@@ -1286,6 +1314,7 @@ export type DbResourceScalarWhereInput = {
|
|
|
1286
1314
|
extra?: Prisma.JsonNullableFilter<"DbResource">
|
|
1287
1315
|
lastCheckedAt?: Prisma.DateTimeNullableFilter<"DbResource"> | Date | string | null
|
|
1288
1316
|
nextCheckAfter?: Prisma.DateTimeNullableFilter<"DbResource"> | Date | string | null
|
|
1317
|
+
lastContentChangeAt?: Prisma.DateTimeNullableFilter<"DbResource"> | Date | string | null
|
|
1289
1318
|
createdAt?: Prisma.DateTimeFilter<"DbResource"> | Date | string
|
|
1290
1319
|
updatedAt?: Prisma.DateTimeFilter<"DbResource"> | Date | string
|
|
1291
1320
|
}
|
|
@@ -1320,6 +1349,7 @@ export type DbResourceCreateWithoutSourceRefsInput = {
|
|
|
1320
1349
|
extra?:PrismaJson.SubResourceExtra | Prisma.NullableJsonNullValueInput
|
|
1321
1350
|
lastCheckedAt?: Date | string | null
|
|
1322
1351
|
nextCheckAfter?: Date | string | null
|
|
1352
|
+
lastContentChangeAt?: Date | string | null
|
|
1323
1353
|
createdAt?: Date | string
|
|
1324
1354
|
updatedAt?: Date | string
|
|
1325
1355
|
parent?: Prisma.DbResourceCreateNestedOneWithoutChildrenInput
|
|
@@ -1357,6 +1387,7 @@ export type DbResourceUncheckedCreateWithoutSourceRefsInput = {
|
|
|
1357
1387
|
extra?:PrismaJson.SubResourceExtra | Prisma.NullableJsonNullValueInput
|
|
1358
1388
|
lastCheckedAt?: Date | string | null
|
|
1359
1389
|
nextCheckAfter?: Date | string | null
|
|
1390
|
+
lastContentChangeAt?: Date | string | null
|
|
1360
1391
|
createdAt?: Date | string
|
|
1361
1392
|
updatedAt?: Date | string
|
|
1362
1393
|
children?: Prisma.DbResourceUncheckedCreateNestedManyWithoutParentInput
|
|
@@ -1408,6 +1439,7 @@ export type DbResourceUpdateWithoutSourceRefsInput = {
|
|
|
1408
1439
|
extra?:PrismaJson.SubResourceExtra | Prisma.NullableJsonNullValueInput
|
|
1409
1440
|
lastCheckedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
1410
1441
|
nextCheckAfter?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
1442
|
+
lastContentChangeAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
1411
1443
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
1412
1444
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
1413
1445
|
parent?: Prisma.DbResourceUpdateOneWithoutChildrenNestedInput
|
|
@@ -1445,6 +1477,7 @@ export type DbResourceUncheckedUpdateWithoutSourceRefsInput = {
|
|
|
1445
1477
|
extra?:PrismaJson.SubResourceExtra | Prisma.NullableJsonNullValueInput
|
|
1446
1478
|
lastCheckedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
1447
1479
|
nextCheckAfter?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
1480
|
+
lastContentChangeAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
1448
1481
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
1449
1482
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
1450
1483
|
children?: Prisma.DbResourceUncheckedUpdateManyWithoutParentNestedInput
|
|
@@ -1480,6 +1513,7 @@ export type DbResourceCreateManyParentInput = {
|
|
|
1480
1513
|
extra?:PrismaJson.SubResourceExtra | Prisma.NullableJsonNullValueInput
|
|
1481
1514
|
lastCheckedAt?: Date | string | null
|
|
1482
1515
|
nextCheckAfter?: Date | string | null
|
|
1516
|
+
lastContentChangeAt?: Date | string | null
|
|
1483
1517
|
createdAt?: Date | string
|
|
1484
1518
|
updatedAt?: Date | string
|
|
1485
1519
|
}
|
|
@@ -1514,6 +1548,7 @@ export type DbResourceUpdateWithoutParentInput = {
|
|
|
1514
1548
|
extra?:PrismaJson.SubResourceExtra | Prisma.NullableJsonNullValueInput
|
|
1515
1549
|
lastCheckedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
1516
1550
|
nextCheckAfter?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
1551
|
+
lastContentChangeAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
1517
1552
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
1518
1553
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
1519
1554
|
sourceRefs?: Prisma.SourceReferenceUpdateManyWithoutResourceNestedInput
|
|
@@ -1550,6 +1585,7 @@ export type DbResourceUncheckedUpdateWithoutParentInput = {
|
|
|
1550
1585
|
extra?:PrismaJson.SubResourceExtra | Prisma.NullableJsonNullValueInput
|
|
1551
1586
|
lastCheckedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
1552
1587
|
nextCheckAfter?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
1588
|
+
lastContentChangeAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
1553
1589
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
1554
1590
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
1555
1591
|
sourceRefs?: Prisma.SourceReferenceUncheckedUpdateManyWithoutResourceNestedInput
|
|
@@ -1586,6 +1622,7 @@ export type DbResourceUncheckedUpdateManyWithoutParentInput = {
|
|
|
1586
1622
|
extra?:PrismaJson.SubResourceExtra | Prisma.NullableJsonNullValueInput
|
|
1587
1623
|
lastCheckedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
1588
1624
|
nextCheckAfter?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
1625
|
+
lastContentChangeAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
|
1589
1626
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
1590
1627
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
1591
1628
|
}
|
|
@@ -1661,6 +1698,7 @@ export type DbResourceSelect<ExtArgs extends runtime.Types.Extensions.InternalAr
|
|
|
1661
1698
|
extra?: boolean
|
|
1662
1699
|
lastCheckedAt?: boolean
|
|
1663
1700
|
nextCheckAfter?: boolean
|
|
1701
|
+
lastContentChangeAt?: boolean
|
|
1664
1702
|
createdAt?: boolean
|
|
1665
1703
|
updatedAt?: boolean
|
|
1666
1704
|
sourceRefs?: boolean | Prisma.DbResource$sourceRefsArgs<ExtArgs>
|
|
@@ -1700,6 +1738,7 @@ export type DbResourceSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Ex
|
|
|
1700
1738
|
extra?: boolean
|
|
1701
1739
|
lastCheckedAt?: boolean
|
|
1702
1740
|
nextCheckAfter?: boolean
|
|
1741
|
+
lastContentChangeAt?: boolean
|
|
1703
1742
|
createdAt?: boolean
|
|
1704
1743
|
updatedAt?: boolean
|
|
1705
1744
|
parent?: boolean | Prisma.DbResource$parentArgs<ExtArgs>
|
|
@@ -1736,6 +1775,7 @@ export type DbResourceSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Ex
|
|
|
1736
1775
|
extra?: boolean
|
|
1737
1776
|
lastCheckedAt?: boolean
|
|
1738
1777
|
nextCheckAfter?: boolean
|
|
1778
|
+
lastContentChangeAt?: boolean
|
|
1739
1779
|
createdAt?: boolean
|
|
1740
1780
|
updatedAt?: boolean
|
|
1741
1781
|
parent?: boolean | Prisma.DbResource$parentArgs<ExtArgs>
|
|
@@ -1772,11 +1812,12 @@ export type DbResourceSelectScalar = {
|
|
|
1772
1812
|
extra?: boolean
|
|
1773
1813
|
lastCheckedAt?: boolean
|
|
1774
1814
|
nextCheckAfter?: boolean
|
|
1815
|
+
lastContentChangeAt?: boolean
|
|
1775
1816
|
createdAt?: boolean
|
|
1776
1817
|
updatedAt?: boolean
|
|
1777
1818
|
}
|
|
1778
1819
|
|
|
1779
|
-
export type DbResourceOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "slug" | "type" | "displayName" | "abbreviation" | "nicknames" | "description" | "access" | "teams" | "accessRequest" | "notes" | "tags" | "appUrl" | "links" | "iconName" | "screenshotIds" | "deprecated" | "aiPrompt" | "urlIssues" | "tiers" | "parentSlug" | "tier" | "familySlug" | "aliases" | "ownerPersonSlug" | "accessMaintainerGroupSlugs" | "accessComments" | "extra" | "lastCheckedAt" | "nextCheckAfter" | "createdAt" | "updatedAt", ExtArgs["result"]["dbResource"]>
|
|
1820
|
+
export type DbResourceOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "slug" | "type" | "displayName" | "abbreviation" | "nicknames" | "description" | "access" | "teams" | "accessRequest" | "notes" | "tags" | "appUrl" | "links" | "iconName" | "screenshotIds" | "deprecated" | "aiPrompt" | "urlIssues" | "tiers" | "parentSlug" | "tier" | "familySlug" | "aliases" | "ownerPersonSlug" | "accessMaintainerGroupSlugs" | "accessComments" | "extra" | "lastCheckedAt" | "nextCheckAfter" | "lastContentChangeAt" | "createdAt" | "updatedAt", ExtArgs["result"]["dbResource"]>
|
|
1780
1821
|
export type DbResourceInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
|
1781
1822
|
sourceRefs?: boolean | Prisma.DbResource$sourceRefsArgs<ExtArgs>
|
|
1782
1823
|
parent?: boolean | Prisma.DbResource$parentArgs<ExtArgs>
|
|
@@ -1846,6 +1887,7 @@ export type $DbResourcePayload<ExtArgs extends runtime.Types.Extensions.Internal
|
|
|
1846
1887
|
extra:PrismaJson.SubResourceExtra | null
|
|
1847
1888
|
lastCheckedAt: Date | null
|
|
1848
1889
|
nextCheckAfter: Date | null
|
|
1890
|
+
lastContentChangeAt: Date | null
|
|
1849
1891
|
createdAt: Date
|
|
1850
1892
|
updatedAt: Date
|
|
1851
1893
|
}, ExtArgs["result"]["dbResource"]>
|
|
@@ -2304,6 +2346,7 @@ export interface DbResourceFieldRefs {
|
|
|
2304
2346
|
readonly extra: Prisma.FieldRef<"DbResource", 'Json'>
|
|
2305
2347
|
readonly lastCheckedAt: Prisma.FieldRef<"DbResource", 'DateTime'>
|
|
2306
2348
|
readonly nextCheckAfter: Prisma.FieldRef<"DbResource", 'DateTime'>
|
|
2349
|
+
readonly lastContentChangeAt: Prisma.FieldRef<"DbResource", 'DateTime'>
|
|
2307
2350
|
readonly createdAt: Prisma.FieldRef<"DbResource", 'DateTime'>
|
|
2308
2351
|
readonly updatedAt: Prisma.FieldRef<"DbResource", 'DateTime'>
|
|
2309
2352
|
}
|
package/src/index.ts
CHANGED
|
@@ -59,14 +59,18 @@ export {
|
|
|
59
59
|
|
|
60
60
|
// Database utilities
|
|
61
61
|
export {
|
|
62
|
+
buildPgSchemaOptions,
|
|
62
63
|
buildPgSslConfig,
|
|
63
64
|
connectDb,
|
|
65
|
+
createCorePrismaClient,
|
|
64
66
|
disconnectDb,
|
|
65
67
|
getDbClient,
|
|
68
|
+
resolveDbSchema,
|
|
66
69
|
setDbClient,
|
|
67
70
|
syncAppCatalog,
|
|
68
71
|
TABLE_SYNC_MAGAZINE,
|
|
69
72
|
tableSyncPrisma,
|
|
73
|
+
verifyDbSchema,
|
|
70
74
|
type MakeTFromPrismaModel,
|
|
71
75
|
type ObjectKeys,
|
|
72
76
|
type ScalarFilter,
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { PrismaClient } from '../generated/prisma/client'
|
|
2
|
-
import
|
|
3
|
-
import pg from 'pg'
|
|
1
|
+
import type { PrismaClient } from '../generated/prisma/client'
|
|
2
|
+
import type pg from 'pg'
|
|
4
3
|
import type { AcDatabaseConfig } from './types'
|
|
5
|
-
import {
|
|
6
|
-
|
|
4
|
+
import {
|
|
5
|
+
createCorePrismaClient,
|
|
6
|
+
setDbClient,
|
|
7
|
+
verifyDbSchema,
|
|
8
|
+
} from '../db/client'
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* Formats a database connection URL from structured config.
|
|
@@ -36,25 +38,19 @@ export class AcDatabaseManager {
|
|
|
36
38
|
*/
|
|
37
39
|
getClient(): PrismaClient {
|
|
38
40
|
if (!this.client) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
this.pool = new pg.Pool({
|
|
46
|
-
connectionString: datasourceUrl,
|
|
47
|
-
...(ssl === undefined ? {} : { ssl }),
|
|
48
|
-
})
|
|
49
|
-
const adapter = new PrismaPg(this.pool)
|
|
50
|
-
|
|
51
|
-
this.client = new PrismaClient({
|
|
52
|
-
adapter,
|
|
41
|
+
// This client - not getDbClient()'s - is the one the app ends up using.
|
|
42
|
+
// The `?schema=` in the url is inert (node-postgres drops unknown URL
|
|
43
|
+
// params), so the schema has to travel via createCorePrismaClient.
|
|
44
|
+
const created = createCorePrismaClient({
|
|
45
|
+
connectionString: formatConnectionUrl(this.config),
|
|
46
|
+
configuredSchema: this.configuredSchema(),
|
|
53
47
|
log:
|
|
54
48
|
process.env.NODE_ENV === 'development'
|
|
55
49
|
? ['warn', 'error']
|
|
56
50
|
: ['warn', 'error'],
|
|
57
51
|
})
|
|
52
|
+
this.pool = created.pool
|
|
53
|
+
this.client = created.client
|
|
58
54
|
|
|
59
55
|
// Bridge with existing backend-core getDbClient() usage
|
|
60
56
|
setDbClient(this.client)
|
|
@@ -65,6 +61,7 @@ export class AcDatabaseManager {
|
|
|
65
61
|
async connect(): Promise<void> {
|
|
66
62
|
const client = this.getClient()
|
|
67
63
|
await client.$connect()
|
|
64
|
+
if (this.pool) await verifyDbSchema(this.pool, this.configuredSchema())
|
|
68
65
|
}
|
|
69
66
|
|
|
70
67
|
async disconnect(): Promise<void> {
|
|
@@ -77,4 +74,8 @@ export class AcDatabaseManager {
|
|
|
77
74
|
this.pool = null
|
|
78
75
|
}
|
|
79
76
|
}
|
|
77
|
+
|
|
78
|
+
private configuredSchema(): string | undefined {
|
|
79
|
+
return 'url' in this.config ? undefined : this.config.schema
|
|
80
|
+
}
|
|
80
81
|
}
|
|
@@ -37,7 +37,11 @@ export function createPrismaDatabaseClient(prisma: {
|
|
|
37
37
|
},
|
|
38
38
|
getTables: async () => {
|
|
39
39
|
const tables = await prisma.$queryRawUnsafe<{ tablename: string }[]>(
|
|
40
|
-
|
|
40
|
+
// current_schema() - not a hardcoded 'public' - because the DML these
|
|
41
|
+
// tools go on to run is unqualified and therefore resolves through
|
|
42
|
+
// search_path. A schema-isolated deployment would otherwise be told
|
|
43
|
+
// about tables it cannot see.
|
|
44
|
+
`SELECT tablename FROM pg_tables WHERE schemaname = current_schema()`,
|
|
41
45
|
)
|
|
42
46
|
return tables.map((t) => t.tablename)
|
|
43
47
|
},
|
|
@@ -51,7 +55,7 @@ export function createPrismaDatabaseClient(prisma: {
|
|
|
51
55
|
>(
|
|
52
56
|
`SELECT column_name, data_type, is_nullable
|
|
53
57
|
FROM information_schema.columns
|
|
54
|
-
WHERE table_name = '${tableName}' AND table_schema =
|
|
58
|
+
WHERE table_name = '${tableName}' AND table_schema = current_schema()`,
|
|
55
59
|
)
|
|
56
60
|
return columns.map((c) => ({
|
|
57
61
|
name: c.column_name,
|
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
* Freshness semantic for a catalog resource.
|
|
3
3
|
*
|
|
4
4
|
* The backend owns this calculation so the frontend stays dumb: it just renders
|
|
5
|
-
* `lastCheckedAt` and, when `isStale` is
|
|
5
|
+
* `lastContentChangeAt` (falling back to `lastCheckedAt`) and, when `isStale` is
|
|
6
|
+
* true, a muted "may be out of date" note.
|
|
6
7
|
*
|
|
7
8
|
* Data source: the autoupdate scan records, per app, when its sources were last
|
|
8
|
-
* verified (`lastCheckedAt`)
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* verified (`lastCheckedAt`), when their content last actually changed
|
|
10
|
+
* (`lastContentChangeAt`), and when the next check is due (`nextCheckAfter`,
|
|
11
|
+
* adaptive backoff). The two "last" dates are deliberately distinct — a source
|
|
12
|
+
* can be re-read many times without changing, so "checked" is not "updated".
|
|
13
|
+
* "Stale" means the entry is more than a grace period past its due date — i.e.
|
|
14
|
+
* the automated freshness loop has fallen behind for this app, so a human reader
|
|
15
|
+
* should treat the info with mild caution.
|
|
12
16
|
*/
|
|
13
17
|
|
|
14
18
|
import type { Freshness } from '../../types/common/appCatalogTypes'
|
|
@@ -18,21 +22,31 @@ export type { Freshness }
|
|
|
18
22
|
/** A check is "stale" once it is this far past its due date (ms). Default: 7 days. */
|
|
19
23
|
export const STALE_GRACE_MS = 7 * 24 * 60 * 60 * 1000
|
|
20
24
|
|
|
25
|
+
/** The stored dates a resource's freshness is computed from (ISO strings). */
|
|
26
|
+
export interface FreshnessInput {
|
|
27
|
+
/** When the sources were last verified — only means the schedule advanced. */
|
|
28
|
+
lastCheckedAt?: string | null
|
|
29
|
+
/** When the next check is due (adaptive backoff). */
|
|
30
|
+
nextCheckAfter?: string | null
|
|
31
|
+
/** When the source content last actually changed. Null on pre-existing rows. */
|
|
32
|
+
lastContentChangeAt?: string | null
|
|
33
|
+
}
|
|
34
|
+
|
|
21
35
|
/**
|
|
22
36
|
* Compute the freshness semantic from the stored dates.
|
|
23
37
|
*
|
|
24
|
-
* @param
|
|
25
|
-
* @param nextCheckAfter ISO string or null — when the next check is due.
|
|
38
|
+
* @param dates the resource's stored freshness timestamps.
|
|
26
39
|
* @param now current time (defaults to Date.now()); injectable for tests.
|
|
27
40
|
*/
|
|
28
41
|
export function computeFreshness(
|
|
29
|
-
|
|
30
|
-
nextCheckAfter: string | null,
|
|
42
|
+
dates: FreshnessInput,
|
|
31
43
|
now: number = Date.now(),
|
|
32
44
|
): Freshness {
|
|
45
|
+
const { lastCheckedAt, nextCheckAfter, lastContentChangeAt } = dates
|
|
46
|
+
|
|
33
47
|
// Never checked → no line is shown at all (frontend hides null lastCheckedAt).
|
|
34
48
|
if (!lastCheckedAt) {
|
|
35
|
-
return { lastCheckedAt: null, isStale: false }
|
|
49
|
+
return { lastCheckedAt: null, lastContentChangeAt: null, isStale: false }
|
|
36
50
|
}
|
|
37
51
|
|
|
38
52
|
let isStale = false
|
|
@@ -43,5 +57,9 @@ export function computeFreshness(
|
|
|
43
57
|
}
|
|
44
58
|
}
|
|
45
59
|
|
|
46
|
-
return {
|
|
60
|
+
return {
|
|
61
|
+
lastCheckedAt,
|
|
62
|
+
lastContentChangeAt: lastContentChangeAt ?? null,
|
|
63
|
+
isStale,
|
|
64
|
+
}
|
|
47
65
|
}
|
|
@@ -158,10 +158,15 @@ function rowToResource(row: ResourceRowWithSourceRefs): Resource {
|
|
|
158
158
|
: undefined
|
|
159
159
|
const tiers =
|
|
160
160
|
row.tiers == null ? undefined : (row.tiers as unknown as Resource['tiers'])
|
|
161
|
-
const freshness = computeFreshness(
|
|
162
|
-
row.lastCheckedAt ? row.lastCheckedAt.toISOString() : null,
|
|
163
|
-
|
|
164
|
-
|
|
161
|
+
const freshness = computeFreshness({
|
|
162
|
+
lastCheckedAt: row.lastCheckedAt ? row.lastCheckedAt.toISOString() : null,
|
|
163
|
+
nextCheckAfter: row.nextCheckAfter
|
|
164
|
+
? row.nextCheckAfter.toISOString()
|
|
165
|
+
: null,
|
|
166
|
+
lastContentChangeAt: row.lastContentChangeAt
|
|
167
|
+
? row.lastContentChangeAt.toISOString()
|
|
168
|
+
: null,
|
|
169
|
+
})
|
|
165
170
|
|
|
166
171
|
return {
|
|
167
172
|
id: row.id,
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Request, Response } from 'express'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Asset URLs are keyed by row id or by icon name, and `upsertAsset` replaces
|
|
5
|
+
* content in place rather than inserting a new row — so the bytes behind a
|
|
6
|
+
* given URL can change. A long `max-age` would then serve the old artwork for
|
|
7
|
+
* the whole window (an icon replacement stayed invisible for a day). Revalidate
|
|
8
|
+
* against the stored checksum instead: unchanged content still costs only a
|
|
9
|
+
* conditional request answered with 304, and a replacement shows up at once.
|
|
10
|
+
*
|
|
11
|
+
* `variant` distinguishes derived renditions of the same row (a resize width,
|
|
12
|
+
* for example) so they do not share one entity tag.
|
|
13
|
+
*/
|
|
14
|
+
export function setRevalidatingCacheHeaders(
|
|
15
|
+
res: Response,
|
|
16
|
+
checksum: string,
|
|
17
|
+
variant?: string | number,
|
|
18
|
+
): string {
|
|
19
|
+
const etag =
|
|
20
|
+
variant === undefined ? `"${checksum}"` : `"${checksum}-${variant}"`
|
|
21
|
+
|
|
22
|
+
res.setHeader('ETag', etag)
|
|
23
|
+
res.setHeader('Cache-Control', 'public, max-age=0, must-revalidate')
|
|
24
|
+
|
|
25
|
+
return etag
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function isNotModified(req: Request, etag: string): boolean {
|
|
29
|
+
return req.headers['if-none-match'] === etag
|
|
30
|
+
}
|
|
@@ -4,6 +4,7 @@ import sharp from 'sharp'
|
|
|
4
4
|
import { getDbClient } from '../../db'
|
|
5
5
|
import { getImageFormat, isRasterImage, resizeImage } from './assetUtils'
|
|
6
6
|
import { upsertAsset } from './upsertAsset'
|
|
7
|
+
import { isNotModified, setRevalidatingCacheHeaders } from './assetCache'
|
|
7
8
|
|
|
8
9
|
// Configure multer for memory storage
|
|
9
10
|
const upload = multer({
|
|
@@ -106,6 +107,7 @@ export function registerAssetRestController(
|
|
|
106
107
|
name: true,
|
|
107
108
|
width: true,
|
|
108
109
|
height: true,
|
|
110
|
+
checksum: true,
|
|
109
111
|
},
|
|
110
112
|
})
|
|
111
113
|
|
|
@@ -129,6 +131,17 @@ export function registerAssetRestController(
|
|
|
129
131
|
Number.isFinite(width) &&
|
|
130
132
|
width > 0
|
|
131
133
|
|
|
134
|
+
// Answered before resizing, so an unchanged asset costs no image work.
|
|
135
|
+
const etag = setRevalidatingCacheHeaders(
|
|
136
|
+
res,
|
|
137
|
+
asset.checksum,
|
|
138
|
+
shouldResize ? `w${width}` : undefined,
|
|
139
|
+
)
|
|
140
|
+
if (isNotModified(req, etag)) {
|
|
141
|
+
res.status(304).end()
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
|
|
132
145
|
if (shouldResize) {
|
|
133
146
|
const fmt = getImageFormat(asset.mimeType) || 'jpeg'
|
|
134
147
|
const buf = await resizeImage(
|
|
@@ -144,7 +157,6 @@ export function registerAssetRestController(
|
|
|
144
157
|
// Set appropriate headers
|
|
145
158
|
res.setHeader('Content-Type', outMime)
|
|
146
159
|
res.setHeader('Content-Disposition', `inline; filename="${asset.name}"`)
|
|
147
|
-
res.setHeader('Cache-Control', 'public, max-age=86400') // Cache for 1 day
|
|
148
160
|
|
|
149
161
|
// Send binary content (resized if requested)
|
|
150
162
|
res.send(outBuffer)
|
|
@@ -204,6 +216,7 @@ export function registerAssetRestController(
|
|
|
204
216
|
name: true,
|
|
205
217
|
width: true,
|
|
206
218
|
height: true,
|
|
219
|
+
checksum: true,
|
|
207
220
|
},
|
|
208
221
|
})
|
|
209
222
|
|
|
@@ -229,6 +242,17 @@ export function registerAssetRestController(
|
|
|
229
242
|
Number.isFinite(width) &&
|
|
230
243
|
width > 0
|
|
231
244
|
|
|
245
|
+
// Answered before resizing, so an unchanged asset costs no image work.
|
|
246
|
+
const etag = setRevalidatingCacheHeaders(
|
|
247
|
+
res,
|
|
248
|
+
asset.checksum,
|
|
249
|
+
shouldResize ? `w${width}` : undefined,
|
|
250
|
+
)
|
|
251
|
+
if (isNotModified(req, etag)) {
|
|
252
|
+
res.status(304).end()
|
|
253
|
+
return
|
|
254
|
+
}
|
|
255
|
+
|
|
232
256
|
if (shouldResize) {
|
|
233
257
|
const fmt = asset.mimeType.includes('png')
|
|
234
258
|
? 'png'
|
|
@@ -258,7 +282,6 @@ export function registerAssetRestController(
|
|
|
258
282
|
// Set appropriate headers
|
|
259
283
|
res.setHeader('Content-Type', outMime)
|
|
260
284
|
res.setHeader('Content-Disposition', `inline; filename="${asset.name}"`)
|
|
261
|
-
res.setHeader('Cache-Control', 'public, max-age=86400') // Cache for 1 day
|
|
262
285
|
|
|
263
286
|
// Send binary content (resized if requested)
|
|
264
287
|
res.send(outBuffer)
|