mirror of
https://github.com/immich-app/immich.git
synced 2026-04-25 02:19:58 -04:00
refactor: use zod codec for response DTO serialization
This commit is contained in:
parent
350056dd1a
commit
fcd23ee043
30
mobile/openapi/lib/model/album_response_dto.dart
generated
30
mobile/openapi/lib/model/album_response_dto.dart
generated
@ -157,10 +157,14 @@ class AlbumResponseDto {
|
||||
json[r'albumUsers'] = this.albumUsers;
|
||||
json[r'assetCount'] = this.assetCount;
|
||||
json[r'contributorCounts'] = this.contributorCounts;
|
||||
json[r'createdAt'] = this.createdAt.toUtc().toIso8601String();
|
||||
json[r'createdAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.createdAt.millisecondsSinceEpoch
|
||||
: this.createdAt.toUtc().toIso8601String();
|
||||
json[r'description'] = this.description;
|
||||
if (this.endDate != null) {
|
||||
json[r'endDate'] = this.endDate!.toUtc().toIso8601String();
|
||||
json[r'endDate'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.endDate!.millisecondsSinceEpoch
|
||||
: this.endDate!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'endDate'] = null;
|
||||
}
|
||||
@ -168,7 +172,9 @@ class AlbumResponseDto {
|
||||
json[r'id'] = this.id;
|
||||
json[r'isActivityEnabled'] = this.isActivityEnabled;
|
||||
if (this.lastModifiedAssetTimestamp != null) {
|
||||
json[r'lastModifiedAssetTimestamp'] = this.lastModifiedAssetTimestamp!.toUtc().toIso8601String();
|
||||
json[r'lastModifiedAssetTimestamp'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.lastModifiedAssetTimestamp!.millisecondsSinceEpoch
|
||||
: this.lastModifiedAssetTimestamp!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'lastModifiedAssetTimestamp'] = null;
|
||||
}
|
||||
@ -179,11 +185,15 @@ class AlbumResponseDto {
|
||||
}
|
||||
json[r'shared'] = this.shared;
|
||||
if (this.startDate != null) {
|
||||
json[r'startDate'] = this.startDate!.toUtc().toIso8601String();
|
||||
json[r'startDate'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.startDate!.millisecondsSinceEpoch
|
||||
: this.startDate!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'startDate'] = null;
|
||||
}
|
||||
json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String();
|
||||
json[r'updatedAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.updatedAt.millisecondsSinceEpoch
|
||||
: this.updatedAt.toUtc().toIso8601String();
|
||||
return json;
|
||||
}
|
||||
|
||||
@ -201,17 +211,17 @@ class AlbumResponseDto {
|
||||
albumUsers: AlbumUserResponseDto.listFromJson(json[r'albumUsers']),
|
||||
assetCount: mapValueOfType<int>(json, r'assetCount')!,
|
||||
contributorCounts: ContributorCountResponseDto.listFromJson(json[r'contributorCounts']),
|
||||
createdAt: mapDateTime(json, r'createdAt', r'')!,
|
||||
createdAt: mapDateTime(json, r'createdAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
|
||||
description: mapValueOfType<String>(json, r'description')!,
|
||||
endDate: mapDateTime(json, r'endDate', r''),
|
||||
endDate: mapDateTime(json, r'endDate', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
hasSharedLink: mapValueOfType<bool>(json, r'hasSharedLink')!,
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
isActivityEnabled: mapValueOfType<bool>(json, r'isActivityEnabled')!,
|
||||
lastModifiedAssetTimestamp: mapDateTime(json, r'lastModifiedAssetTimestamp', r''),
|
||||
lastModifiedAssetTimestamp: mapDateTime(json, r'lastModifiedAssetTimestamp', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
order: AssetOrder.fromJson(json[r'order']),
|
||||
shared: mapValueOfType<bool>(json, r'shared')!,
|
||||
startDate: mapDateTime(json, r'startDate', r''),
|
||||
updatedAt: mapDateTime(json, r'updatedAt', r'')!,
|
||||
startDate: mapDateTime(json, r'startDate', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
updatedAt: mapDateTime(json, r'updatedAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
30
mobile/openapi/lib/model/asset_response_dto.dart
generated
30
mobile/openapi/lib/model/asset_response_dto.dart
generated
@ -246,7 +246,9 @@ class AssetResponseDto {
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'checksum'] = this.checksum;
|
||||
json[r'createdAt'] = this.createdAt.toUtc().toIso8601String();
|
||||
json[r'createdAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.createdAt.millisecondsSinceEpoch
|
||||
: this.createdAt.toUtc().toIso8601String();
|
||||
if (this.duplicateId != null) {
|
||||
json[r'duplicateId'] = this.duplicateId;
|
||||
} else {
|
||||
@ -262,8 +264,12 @@ class AssetResponseDto {
|
||||
} else {
|
||||
// json[r'exifInfo'] = null;
|
||||
}
|
||||
json[r'fileCreatedAt'] = this.fileCreatedAt.toUtc().toIso8601String();
|
||||
json[r'fileModifiedAt'] = this.fileModifiedAt.toUtc().toIso8601String();
|
||||
json[r'fileCreatedAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.fileCreatedAt.millisecondsSinceEpoch
|
||||
: this.fileCreatedAt.toUtc().toIso8601String();
|
||||
json[r'fileModifiedAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.fileModifiedAt.millisecondsSinceEpoch
|
||||
: this.fileModifiedAt.toUtc().toIso8601String();
|
||||
json[r'hasMetadata'] = this.hasMetadata;
|
||||
if (this.height != null) {
|
||||
json[r'height'] = this.height;
|
||||
@ -286,7 +292,9 @@ class AssetResponseDto {
|
||||
} else {
|
||||
// json[r'livePhotoVideoId'] = null;
|
||||
}
|
||||
json[r'localDateTime'] = this.localDateTime.toUtc().toIso8601String();
|
||||
json[r'localDateTime'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.localDateTime.millisecondsSinceEpoch
|
||||
: this.localDateTime.toUtc().toIso8601String();
|
||||
json[r'originalFileName'] = this.originalFileName;
|
||||
if (this.originalMimeType != null) {
|
||||
json[r'originalMimeType'] = this.originalMimeType;
|
||||
@ -319,7 +327,9 @@ class AssetResponseDto {
|
||||
}
|
||||
json[r'type'] = this.type;
|
||||
json[r'unassignedFaces'] = this.unassignedFaces;
|
||||
json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String();
|
||||
json[r'updatedAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.updatedAt.millisecondsSinceEpoch
|
||||
: this.updatedAt.toUtc().toIso8601String();
|
||||
json[r'visibility'] = this.visibility;
|
||||
if (this.width != null) {
|
||||
json[r'width'] = this.width;
|
||||
@ -339,12 +349,12 @@ class AssetResponseDto {
|
||||
|
||||
return AssetResponseDto(
|
||||
checksum: mapValueOfType<String>(json, r'checksum')!,
|
||||
createdAt: mapDateTime(json, r'createdAt', r'')!,
|
||||
createdAt: mapDateTime(json, r'createdAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
|
||||
duplicateId: mapValueOfType<String>(json, r'duplicateId'),
|
||||
duration: mapValueOfType<String>(json, r'duration'),
|
||||
exifInfo: ExifResponseDto.fromJson(json[r'exifInfo']),
|
||||
fileCreatedAt: mapDateTime(json, r'fileCreatedAt', r'')!,
|
||||
fileModifiedAt: mapDateTime(json, r'fileModifiedAt', r'')!,
|
||||
fileCreatedAt: mapDateTime(json, r'fileCreatedAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
|
||||
fileModifiedAt: mapDateTime(json, r'fileModifiedAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
|
||||
hasMetadata: mapValueOfType<bool>(json, r'hasMetadata')!,
|
||||
height: json[r'height'] == null
|
||||
? null
|
||||
@ -357,7 +367,7 @@ class AssetResponseDto {
|
||||
isTrashed: mapValueOfType<bool>(json, r'isTrashed')!,
|
||||
libraryId: mapValueOfType<String>(json, r'libraryId'),
|
||||
livePhotoVideoId: mapValueOfType<String>(json, r'livePhotoVideoId'),
|
||||
localDateTime: mapDateTime(json, r'localDateTime', r'')!,
|
||||
localDateTime: mapDateTime(json, r'localDateTime', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
|
||||
originalFileName: mapValueOfType<String>(json, r'originalFileName')!,
|
||||
originalMimeType: mapValueOfType<String>(json, r'originalMimeType'),
|
||||
originalPath: mapValueOfType<String>(json, r'originalPath')!,
|
||||
@ -370,7 +380,7 @@ class AssetResponseDto {
|
||||
thumbhash: mapValueOfType<String>(json, r'thumbhash'),
|
||||
type: AssetTypeEnum.fromJson(json[r'type'])!,
|
||||
unassignedFaces: AssetFaceWithoutPersonResponseDto.listFromJson(json[r'unassignedFaces']),
|
||||
updatedAt: mapDateTime(json, r'updatedAt', r'')!,
|
||||
updatedAt: mapDateTime(json, r'updatedAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
|
||||
visibility: AssetVisibility.fromJson(json[r'visibility'])!,
|
||||
width: json[r'width'] == null
|
||||
? null
|
||||
|
||||
12
mobile/openapi/lib/model/exif_response_dto.dart
generated
12
mobile/openapi/lib/model/exif_response_dto.dart
generated
@ -177,7 +177,9 @@ class ExifResponseDto {
|
||||
// json[r'country'] = null;
|
||||
}
|
||||
if (this.dateTimeOriginal != null) {
|
||||
json[r'dateTimeOriginal'] = this.dateTimeOriginal!.toUtc().toIso8601String();
|
||||
json[r'dateTimeOriginal'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.dateTimeOriginal!.millisecondsSinceEpoch
|
||||
: this.dateTimeOriginal!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'dateTimeOriginal'] = null;
|
||||
}
|
||||
@ -247,7 +249,9 @@ class ExifResponseDto {
|
||||
// json[r'model'] = null;
|
||||
}
|
||||
if (this.modifyDate != null) {
|
||||
json[r'modifyDate'] = this.modifyDate!.toUtc().toIso8601String();
|
||||
json[r'modifyDate'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.modifyDate!.millisecondsSinceEpoch
|
||||
: this.modifyDate!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'modifyDate'] = null;
|
||||
}
|
||||
@ -290,7 +294,7 @@ class ExifResponseDto {
|
||||
return ExifResponseDto(
|
||||
city: mapValueOfType<String>(json, r'city'),
|
||||
country: mapValueOfType<String>(json, r'country'),
|
||||
dateTimeOriginal: mapDateTime(json, r'dateTimeOriginal', r''),
|
||||
dateTimeOriginal: mapDateTime(json, r'dateTimeOriginal', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
description: mapValueOfType<String>(json, r'description'),
|
||||
exifImageHeight: json[r'exifImageHeight'] == null
|
||||
? null
|
||||
@ -318,7 +322,7 @@ class ExifResponseDto {
|
||||
: num.parse('${json[r'longitude']}'),
|
||||
make: mapValueOfType<String>(json, r'make'),
|
||||
model: mapValueOfType<String>(json, r'model'),
|
||||
modifyDate: mapDateTime(json, r'modifyDate', r''),
|
||||
modifyDate: mapDateTime(json, r'modifyDate', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
orientation: mapValueOfType<String>(json, r'orientation'),
|
||||
projectionType: mapValueOfType<String>(json, r'projectionType'),
|
||||
rating: json[r'rating'] == null
|
||||
|
||||
@ -83,7 +83,9 @@ class PartnerResponseDto {
|
||||
// json[r'inTimeline'] = null;
|
||||
}
|
||||
json[r'name'] = this.name;
|
||||
json[r'profileChangedAt'] = this.profileChangedAt.toUtc().toIso8601String();
|
||||
json[r'profileChangedAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.profileChangedAt.millisecondsSinceEpoch
|
||||
: this.profileChangedAt.toUtc().toIso8601String();
|
||||
json[r'profileImagePath'] = this.profileImagePath;
|
||||
return json;
|
||||
}
|
||||
@ -102,7 +104,7 @@ class PartnerResponseDto {
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
inTimeline: mapValueOfType<bool>(json, r'inTimeline'),
|
||||
name: mapValueOfType<String>(json, r'name')!,
|
||||
profileChangedAt: mapDateTime(json, r'profileChangedAt', r'')!,
|
||||
profileChangedAt: mapDateTime(json, r'profileChangedAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
|
||||
profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
|
||||
);
|
||||
}
|
||||
|
||||
12
mobile/openapi/lib/model/person_response_dto.dart
generated
12
mobile/openapi/lib/model/person_response_dto.dart
generated
@ -94,7 +94,9 @@ class PersonResponseDto {
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.birthDate != null) {
|
||||
json[r'birthDate'] = _dateFormatter.format(this.birthDate!.toUtc());
|
||||
json[r'birthDate'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$/')
|
||||
? this.birthDate!.millisecondsSinceEpoch
|
||||
: _dateFormatter.format(this.birthDate!.toUtc());
|
||||
} else {
|
||||
// json[r'birthDate'] = null;
|
||||
}
|
||||
@ -113,7 +115,9 @@ class PersonResponseDto {
|
||||
json[r'name'] = this.name;
|
||||
json[r'thumbnailPath'] = this.thumbnailPath;
|
||||
if (this.updatedAt != null) {
|
||||
json[r'updatedAt'] = this.updatedAt!.toUtc().toIso8601String();
|
||||
json[r'updatedAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.updatedAt!.millisecondsSinceEpoch
|
||||
: this.updatedAt!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'updatedAt'] = null;
|
||||
}
|
||||
@ -129,14 +133,14 @@ class PersonResponseDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return PersonResponseDto(
|
||||
birthDate: mapDateTime(json, r'birthDate', r''),
|
||||
birthDate: mapDateTime(json, r'birthDate', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$/'),
|
||||
color: mapValueOfType<String>(json, r'color'),
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
isFavorite: mapValueOfType<bool>(json, r'isFavorite'),
|
||||
isHidden: mapValueOfType<bool>(json, r'isHidden')!,
|
||||
name: mapValueOfType<String>(json, r'name')!,
|
||||
thumbnailPath: mapValueOfType<String>(json, r'thumbnailPath')!,
|
||||
updatedAt: mapDateTime(json, r'updatedAt', r''),
|
||||
updatedAt: mapDateTime(json, r'updatedAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
@ -99,7 +99,9 @@ class PersonWithFacesResponseDto {
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.birthDate != null) {
|
||||
json[r'birthDate'] = _dateFormatter.format(this.birthDate!.toUtc());
|
||||
json[r'birthDate'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$/')
|
||||
? this.birthDate!.millisecondsSinceEpoch
|
||||
: _dateFormatter.format(this.birthDate!.toUtc());
|
||||
} else {
|
||||
// json[r'birthDate'] = null;
|
||||
}
|
||||
@ -119,7 +121,9 @@ class PersonWithFacesResponseDto {
|
||||
json[r'name'] = this.name;
|
||||
json[r'thumbnailPath'] = this.thumbnailPath;
|
||||
if (this.updatedAt != null) {
|
||||
json[r'updatedAt'] = this.updatedAt!.toUtc().toIso8601String();
|
||||
json[r'updatedAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.updatedAt!.millisecondsSinceEpoch
|
||||
: this.updatedAt!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'updatedAt'] = null;
|
||||
}
|
||||
@ -135,7 +139,7 @@ class PersonWithFacesResponseDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return PersonWithFacesResponseDto(
|
||||
birthDate: mapDateTime(json, r'birthDate', r''),
|
||||
birthDate: mapDateTime(json, r'birthDate', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$/'),
|
||||
color: mapValueOfType<String>(json, r'color'),
|
||||
faces: AssetFaceWithoutPersonResponseDto.listFromJson(json[r'faces']),
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
@ -143,7 +147,7 @@ class PersonWithFacesResponseDto {
|
||||
isHidden: mapValueOfType<bool>(json, r'isHidden')!,
|
||||
name: mapValueOfType<String>(json, r'name')!,
|
||||
thumbnailPath: mapValueOfType<String>(json, r'thumbnailPath')!,
|
||||
updatedAt: mapDateTime(json, r'updatedAt', r''),
|
||||
updatedAt: mapDateTime(json, r'updatedAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
12
mobile/openapi/lib/model/tag_response_dto.dart
generated
12
mobile/openapi/lib/model/tag_response_dto.dart
generated
@ -86,7 +86,9 @@ class TagResponseDto {
|
||||
} else {
|
||||
// json[r'color'] = null;
|
||||
}
|
||||
json[r'createdAt'] = this.createdAt.toUtc().toIso8601String();
|
||||
json[r'createdAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.createdAt.millisecondsSinceEpoch
|
||||
: this.createdAt.toUtc().toIso8601String();
|
||||
json[r'id'] = this.id;
|
||||
json[r'name'] = this.name;
|
||||
if (this.parentId != null) {
|
||||
@ -94,7 +96,9 @@ class TagResponseDto {
|
||||
} else {
|
||||
// json[r'parentId'] = null;
|
||||
}
|
||||
json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String();
|
||||
json[r'updatedAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.updatedAt.millisecondsSinceEpoch
|
||||
: this.updatedAt.toUtc().toIso8601String();
|
||||
json[r'value'] = this.value;
|
||||
return json;
|
||||
}
|
||||
@ -109,11 +113,11 @@ class TagResponseDto {
|
||||
|
||||
return TagResponseDto(
|
||||
color: mapValueOfType<String>(json, r'color'),
|
||||
createdAt: mapDateTime(json, r'createdAt', r'')!,
|
||||
createdAt: mapDateTime(json, r'createdAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
name: mapValueOfType<String>(json, r'name')!,
|
||||
parentId: mapValueOfType<String>(json, r'parentId'),
|
||||
updatedAt: mapDateTime(json, r'updatedAt', r'')!,
|
||||
updatedAt: mapDateTime(json, r'updatedAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
|
||||
value: mapValueOfType<String>(json, r'value')!,
|
||||
);
|
||||
}
|
||||
|
||||
@ -153,7 +153,9 @@ class UserAdminResponseDto {
|
||||
}
|
||||
json[r'name'] = this.name;
|
||||
json[r'oauthId'] = this.oauthId;
|
||||
json[r'profileChangedAt'] = this.profileChangedAt.toUtc().toIso8601String();
|
||||
json[r'profileChangedAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.profileChangedAt.millisecondsSinceEpoch
|
||||
: this.profileChangedAt.toUtc().toIso8601String();
|
||||
json[r'profileImagePath'] = this.profileImagePath;
|
||||
if (this.quotaSizeInBytes != null) {
|
||||
json[r'quotaSizeInBytes'] = this.quotaSizeInBytes;
|
||||
@ -196,7 +198,7 @@ class UserAdminResponseDto {
|
||||
license: UserLicense.fromJson(json[r'license']),
|
||||
name: mapValueOfType<String>(json, r'name')!,
|
||||
oauthId: mapValueOfType<String>(json, r'oauthId')!,
|
||||
profileChangedAt: mapDateTime(json, r'profileChangedAt', r'')!,
|
||||
profileChangedAt: mapDateTime(json, r'profileChangedAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
|
||||
profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
|
||||
quotaSizeInBytes: mapValueOfType<int>(json, r'quotaSizeInBytes'),
|
||||
quotaUsageInBytes: mapValueOfType<int>(json, r'quotaUsageInBytes'),
|
||||
|
||||
6
mobile/openapi/lib/model/user_response_dto.dart
generated
6
mobile/openapi/lib/model/user_response_dto.dart
generated
@ -66,7 +66,9 @@ class UserResponseDto {
|
||||
json[r'email'] = this.email;
|
||||
json[r'id'] = this.id;
|
||||
json[r'name'] = this.name;
|
||||
json[r'profileChangedAt'] = this.profileChangedAt.toUtc().toIso8601String();
|
||||
json[r'profileChangedAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.profileChangedAt.millisecondsSinceEpoch
|
||||
: this.profileChangedAt.toUtc().toIso8601String();
|
||||
json[r'profileImagePath'] = this.profileImagePath;
|
||||
return json;
|
||||
}
|
||||
@ -84,7 +86,7 @@ class UserResponseDto {
|
||||
email: mapValueOfType<String>(json, r'email')!,
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
name: mapValueOfType<String>(json, r'name')!,
|
||||
profileChangedAt: mapDateTime(json, r'profileChangedAt', r'')!,
|
||||
profileChangedAt: mapDateTime(json, r'profileChangedAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
|
||||
profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
|
||||
);
|
||||
}
|
||||
|
||||
@ -15300,7 +15300,9 @@
|
||||
},
|
||||
"createdAt": {
|
||||
"description": "Creation date",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
@ -15309,7 +15311,9 @@
|
||||
},
|
||||
"endDate": {
|
||||
"description": "End date (latest asset)",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
},
|
||||
"hasSharedLink": {
|
||||
@ -15326,7 +15330,9 @@
|
||||
},
|
||||
"lastModifiedAssetTimestamp": {
|
||||
"description": "Last modified asset timestamp",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
},
|
||||
"order": {
|
||||
@ -15338,12 +15344,16 @@
|
||||
},
|
||||
"startDate": {
|
||||
"description": "Start date (earliest asset)",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
},
|
||||
"updatedAt": {
|
||||
"description": "Last update date",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
@ -16618,7 +16628,9 @@
|
||||
},
|
||||
"createdAt": {
|
||||
"description": "The UTC timestamp when the asset was originally uploaded to Immich.",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
},
|
||||
"duplicateId": {
|
||||
@ -16636,12 +16648,16 @@
|
||||
},
|
||||
"fileCreatedAt": {
|
||||
"description": "The actual UTC timestamp when the file was created/captured, preserving timezone information. This is the authoritative timestamp for chronological sorting within timeline groups. Combined with timezone data, this can be used to determine the exact moment the photo was taken.",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
},
|
||||
"fileModifiedAt": {
|
||||
"description": "The UTC timestamp when the file was last modified on the filesystem. This reflects the last time the physical file was changed, which may be different from when the photo was originally taken.",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
},
|
||||
"hasMetadata": {
|
||||
@ -16714,7 +16730,9 @@
|
||||
},
|
||||
"localDateTime": {
|
||||
"description": "The local date and time when the photo/video was taken, derived from EXIF metadata. This represents the photographer's local time regardless of timezone, stored as a timezone-agnostic timestamp. Used for timeline grouping by \"local\" days and months.",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
},
|
||||
"originalFileName": {
|
||||
@ -16787,7 +16805,9 @@
|
||||
},
|
||||
"updatedAt": {
|
||||
"description": "The UTC timestamp when the asset record was last updated in the database. This is automatically maintained by the database and reflects when any field in the asset was last modified.",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
},
|
||||
"visibility": {
|
||||
@ -17614,8 +17634,10 @@
|
||||
"dateTimeOriginal": {
|
||||
"default": null,
|
||||
"description": "Original date/time",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"nullable": true,
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
@ -17703,8 +17725,10 @@
|
||||
"modifyDate": {
|
||||
"default": null,
|
||||
"description": "Modification date/time",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"nullable": true,
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
},
|
||||
"orientation": {
|
||||
@ -19245,7 +19269,9 @@
|
||||
},
|
||||
"profileChangedAt": {
|
||||
"description": "Profile change date",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
},
|
||||
"profileImagePath": {
|
||||
@ -19600,8 +19626,10 @@
|
||||
"properties": {
|
||||
"birthDate": {
|
||||
"description": "Person date of birth",
|
||||
"example": "2024-01-01",
|
||||
"format": "date",
|
||||
"nullable": true,
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$",
|
||||
"type": "string"
|
||||
},
|
||||
"color": {
|
||||
@ -19652,7 +19680,9 @@
|
||||
},
|
||||
"updatedAt": {
|
||||
"description": "Last update date",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string",
|
||||
"x-immich-history": [
|
||||
{
|
||||
@ -19729,8 +19759,10 @@
|
||||
"properties": {
|
||||
"birthDate": {
|
||||
"description": "Person date of birth",
|
||||
"example": "2024-01-01",
|
||||
"format": "date",
|
||||
"nullable": true,
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$",
|
||||
"type": "string"
|
||||
},
|
||||
"color": {
|
||||
@ -19787,7 +19819,9 @@
|
||||
},
|
||||
"updatedAt": {
|
||||
"description": "Last update date",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string",
|
||||
"x-immich-history": [
|
||||
{
|
||||
@ -24814,7 +24848,9 @@
|
||||
},
|
||||
"createdAt": {
|
||||
"description": "Creation date",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
@ -24831,7 +24867,9 @@
|
||||
},
|
||||
"updatedAt": {
|
||||
"description": "Last update date",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
@ -25485,7 +25523,9 @@
|
||||
},
|
||||
"profileChangedAt": {
|
||||
"description": "Profile change date",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
},
|
||||
"profileImagePath": {
|
||||
@ -25767,7 +25807,9 @@
|
||||
},
|
||||
"profileChangedAt": {
|
||||
"description": "Profile change date",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
},
|
||||
"profileImagePath": {
|
||||
|
||||
@ -42,19 +42,19 @@ import { configureUserAgent } from 'src/utils/fetch';
|
||||
|
||||
const common = [...repositories, ...services, GlobalExceptionFilter];
|
||||
|
||||
const configRepository = new ConfigRepository();
|
||||
const { bull, cls, database, otel } = configRepository.getEnv();
|
||||
|
||||
const commonMiddleware = [
|
||||
{ provide: APP_FILTER, useClass: GlobalExceptionFilter },
|
||||
{ provide: APP_PIPE, useClass: ZodValidationPipe },
|
||||
{ provide: APP_INTERCEPTOR, useClass: ZodSerializerInterceptor },
|
||||
...(configRepository.isDev() ? [{ provide: APP_INTERCEPTOR, useClass: ZodSerializerInterceptor }] : []),
|
||||
{ provide: APP_INTERCEPTOR, useClass: LoggingInterceptor },
|
||||
{ provide: APP_INTERCEPTOR, useClass: ErrorInterceptor },
|
||||
];
|
||||
|
||||
const apiMiddleware = [FileUploadInterceptor, ...commonMiddleware, { provide: APP_GUARD, useClass: AuthGuard }];
|
||||
|
||||
const configRepository = new ConfigRepository();
|
||||
const { bull, cls, database, otel } = configRepository.getEnv();
|
||||
|
||||
const commonImports = [
|
||||
ClsModule.forRoot(cls.config),
|
||||
KyselyModule.forRoot(getKyselyConfig(database.config)),
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Patch, Post, Put, Query } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { ZodSerializerDto } from 'nestjs-zod';
|
||||
import { Endpoint, HistoryBuilder } from 'src/decorators';
|
||||
import {
|
||||
AddUsersDto,
|
||||
@ -27,6 +28,7 @@ export class AlbumController {
|
||||
|
||||
@Get()
|
||||
@Authenticated({ permission: Permission.AlbumRead })
|
||||
@ZodSerializerDto([AlbumResponseDto])
|
||||
@Endpoint({
|
||||
summary: 'List all albums',
|
||||
description: 'Retrieve a list of albums available to the authenticated user.',
|
||||
@ -38,6 +40,7 @@ export class AlbumController {
|
||||
|
||||
@Post()
|
||||
@Authenticated({ permission: Permission.AlbumCreate })
|
||||
@ZodSerializerDto(AlbumResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Create an album',
|
||||
description: 'Create a new album. The album can also be created with initial users and assets.',
|
||||
@ -60,6 +63,7 @@ export class AlbumController {
|
||||
|
||||
@Authenticated({ permission: Permission.AlbumRead, sharedLink: true })
|
||||
@Get(':id')
|
||||
@ZodSerializerDto(AlbumResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Retrieve an album',
|
||||
description: 'Retrieve information about a specific album by its ID.',
|
||||
@ -71,6 +75,7 @@ export class AlbumController {
|
||||
|
||||
@Patch(':id')
|
||||
@Authenticated({ permission: Permission.AlbumUpdate })
|
||||
@ZodSerializerDto(AlbumResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Update an album',
|
||||
description:
|
||||
@ -152,6 +157,7 @@ export class AlbumController {
|
||||
|
||||
@Put(':id/users')
|
||||
@Authenticated({ permission: Permission.AlbumUserCreate })
|
||||
@ZodSerializerDto(AlbumResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Share album with users',
|
||||
description: 'Share an album with multiple users. Each user can be given a specific role in the album.',
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import { AssetController } from 'src/controllers/asset.controller';
|
||||
import { mapAsset } from 'src/dtos/asset-response.dto';
|
||||
import { AssetMetadataKey } from 'src/enum';
|
||||
import { AssetService } from 'src/services/asset.service';
|
||||
import request from 'supertest';
|
||||
import { AssetFactory } from 'test/factories/asset.factory';
|
||||
import { getForAsset } from 'test/mappers';
|
||||
import { factory } from 'test/small.factory';
|
||||
import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils';
|
||||
|
||||
@ -183,6 +186,10 @@ describe(AssetController.name, () => {
|
||||
});
|
||||
|
||||
describe('PUT /assets/:id', () => {
|
||||
beforeEach(() => {
|
||||
service.update.mockResolvedValue(mapAsset(getForAsset(AssetFactory.create())));
|
||||
});
|
||||
|
||||
it('should be an authenticated route', async () => {
|
||||
await request(ctx.getHttpServer()).get(`/assets/123`);
|
||||
expect(ctx.authenticate).toHaveBeenCalled();
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, Query } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { ZodSerializerDto } from 'nestjs-zod';
|
||||
import { Endpoint, HistoryBuilder } from 'src/decorators';
|
||||
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
||||
import {
|
||||
@ -79,6 +80,7 @@ export class AssetController {
|
||||
|
||||
@Get(':id')
|
||||
@Authenticated({ permission: Permission.AssetRead, sharedLink: true })
|
||||
@ZodSerializerDto(AssetResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Retrieve an asset',
|
||||
description: 'Retrieve detailed information about a specific asset.',
|
||||
@ -128,6 +130,7 @@ export class AssetController {
|
||||
|
||||
@Put(':id')
|
||||
@Authenticated({ permission: Permission.AssetUpdate })
|
||||
@ZodSerializerDto(AssetResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Update an asset',
|
||||
description: 'Update information of a specific asset.',
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { AuthController } from 'src/controllers/auth.controller';
|
||||
import { LoginResponseDto } from 'src/dtos/auth.dto';
|
||||
import { mapUserAdmin } from 'src/dtos/user.dto';
|
||||
import { AuthService } from 'src/services/auth.service';
|
||||
import request from 'supertest';
|
||||
import { UserFactory } from 'test/factories/user.factory';
|
||||
import { mediumFactory } from 'test/medium.factory';
|
||||
import { errorDto } from 'test/medium/responses';
|
||||
import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils';
|
||||
@ -53,6 +55,7 @@ describe(AuthController.name, () => {
|
||||
|
||||
it('should transform email to lower case', async () => {
|
||||
service.adminSignUp.mockReset();
|
||||
service.adminSignUp.mockResolvedValue(mapUserAdmin(UserFactory.create()));
|
||||
const { status } = await request(ctx.getHttpServer())
|
||||
.post('/auth/admin-sign-up')
|
||||
.send({ name: 'admin', password: 'password', email: 'aDmIn@IMMICH.cloud' });
|
||||
@ -61,6 +64,7 @@ describe(AuthController.name, () => {
|
||||
});
|
||||
|
||||
it('should accept an email with a local domain', async () => {
|
||||
service.adminSignUp.mockResolvedValue(mapUserAdmin(UserFactory.create()));
|
||||
const { status } = await request(ctx.getHttpServer())
|
||||
.post('/auth/admin-sign-up')
|
||||
.send({ name: 'admin', password: 'password', email: 'admin@local' });
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Post, Put, Req, Res } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { Request, Response } from 'express';
|
||||
import { ZodSerializerDto } from 'nestjs-zod';
|
||||
import { Endpoint, HistoryBuilder } from 'src/decorators';
|
||||
import {
|
||||
AuthDto,
|
||||
@ -50,6 +51,7 @@ export class AuthController {
|
||||
}
|
||||
|
||||
@Post('admin-sign-up')
|
||||
@ZodSerializerDto(UserAdminResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Register admin',
|
||||
description: 'Create the first admin user in the system.',
|
||||
@ -74,6 +76,7 @@ export class AuthController {
|
||||
@Post('change-password')
|
||||
@Authenticated({ permission: Permission.AuthChangePassword })
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ZodSerializerDto(UserAdminResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Change password',
|
||||
description: 'Change the password of the current user.',
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, Query } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { ZodSerializerDto } from 'nestjs-zod';
|
||||
import { Endpoint, HistoryBuilder } from 'src/decorators';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import {
|
||||
@ -44,6 +45,7 @@ export class FaceController {
|
||||
|
||||
@Put(':id')
|
||||
@Authenticated({ permission: Permission.FaceUpdate })
|
||||
@ZodSerializerDto(PersonResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Re-assign a face to another person',
|
||||
description: 'Re-assign the face provided in the body to the person identified by the id in the path parameter.',
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Body, Controller, Get, HttpCode, HttpStatus, Post, Redirect, Req, Res } from '@nestjs/common';
|
||||
import { ApiConsumes, ApiTags } from '@nestjs/swagger';
|
||||
import { Request, Response } from 'express';
|
||||
import { ZodSerializerDto } from 'nestjs-zod';
|
||||
import { Endpoint, HistoryBuilder } from 'src/decorators';
|
||||
import {
|
||||
AuthDto,
|
||||
@ -89,6 +90,7 @@ export class OAuthController {
|
||||
@Post('link')
|
||||
@Authenticated()
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ZodSerializerDto(UserAdminResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Link OAuth account',
|
||||
description: 'Link an OAuth account to the authenticated user.',
|
||||
@ -105,6 +107,7 @@ export class OAuthController {
|
||||
@Post('unlink')
|
||||
@Authenticated()
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ZodSerializerDto(UserAdminResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Unlink OAuth account',
|
||||
description: 'Unlink the OAuth account from the authenticated user.',
|
||||
|
||||
@ -14,6 +14,7 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { NextFunction, Response } from 'express';
|
||||
import { ZodSerializerDto } from 'nestjs-zod';
|
||||
import { Endpoint, HistoryBuilder } from 'src/decorators';
|
||||
import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
@ -58,6 +59,7 @@ export class PersonController {
|
||||
|
||||
@Post()
|
||||
@Authenticated({ permission: Permission.PersonCreate })
|
||||
@ZodSerializerDto(PersonResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Create a person',
|
||||
description: 'Create a new person that can have multiple faces assigned to them.',
|
||||
@ -92,6 +94,7 @@ export class PersonController {
|
||||
|
||||
@Get(':id')
|
||||
@Authenticated({ permission: Permission.PersonRead })
|
||||
@ZodSerializerDto(PersonResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Get a person',
|
||||
description: 'Retrieve a person by id.',
|
||||
@ -103,6 +106,7 @@ export class PersonController {
|
||||
|
||||
@Put(':id')
|
||||
@Authenticated({ permission: Permission.PersonUpdate })
|
||||
@ZodSerializerDto(PersonResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Update person',
|
||||
description: 'Update an individual person.',
|
||||
@ -158,6 +162,7 @@ export class PersonController {
|
||||
|
||||
@Put(':id/reassign')
|
||||
@Authenticated({ permission: Permission.PersonReassign })
|
||||
@ZodSerializerDto([PersonResponseDto])
|
||||
@Endpoint({
|
||||
summary: 'Reassign faces',
|
||||
description: 'Bulk reassign a list of faces to a different person.',
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Body, Controller, Get, HttpCode, HttpStatus, Post, Query } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { ZodSerializerDto } from 'nestjs-zod';
|
||||
import { Endpoint, HistoryBuilder } from 'src/decorators';
|
||||
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
@ -54,6 +55,7 @@ export class SearchController {
|
||||
@Post('random')
|
||||
@Authenticated({ permission: Permission.AssetRead })
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ZodSerializerDto([AssetResponseDto])
|
||||
@Endpoint({
|
||||
summary: 'Search random assets',
|
||||
description: 'Retrieve a random selection of assets based on the provided criteria.',
|
||||
@ -66,6 +68,7 @@ export class SearchController {
|
||||
@Post('large-assets')
|
||||
@Authenticated({ permission: Permission.AssetRead })
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ZodSerializerDto([AssetResponseDto])
|
||||
@Endpoint({
|
||||
summary: 'Search large assets',
|
||||
description: 'Search for assets that are considered large based on specified criteria.',
|
||||
@ -100,6 +103,7 @@ export class SearchController {
|
||||
|
||||
@Get('person')
|
||||
@Authenticated({ permission: Permission.PersonRead })
|
||||
@ZodSerializerDto([PersonResponseDto])
|
||||
@Endpoint({
|
||||
summary: 'Search people',
|
||||
description: 'Search for people by name.',
|
||||
@ -122,6 +126,7 @@ export class SearchController {
|
||||
|
||||
@Get('cities')
|
||||
@Authenticated({ permission: Permission.AssetRead })
|
||||
@ZodSerializerDto([AssetResponseDto])
|
||||
@Endpoint({
|
||||
summary: 'Retrieve assets by city',
|
||||
description:
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { ZodSerializerDto } from 'nestjs-zod';
|
||||
import { Endpoint, HistoryBuilder } from 'src/decorators';
|
||||
import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
@ -23,6 +24,7 @@ export class TagController {
|
||||
|
||||
@Post()
|
||||
@Authenticated({ permission: Permission.TagCreate })
|
||||
@ZodSerializerDto(TagResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Create a tag',
|
||||
description: 'Create a new tag by providing a name and optional color.',
|
||||
@ -34,6 +36,7 @@ export class TagController {
|
||||
|
||||
@Get()
|
||||
@Authenticated({ permission: Permission.TagRead })
|
||||
@ZodSerializerDto([TagResponseDto])
|
||||
@Endpoint({
|
||||
summary: 'Retrieve tags',
|
||||
description: 'Retrieve a list of all tags.',
|
||||
@ -45,6 +48,7 @@ export class TagController {
|
||||
|
||||
@Put()
|
||||
@Authenticated({ permission: Permission.TagCreate })
|
||||
@ZodSerializerDto([TagResponseDto])
|
||||
@Endpoint({
|
||||
summary: 'Upsert tags',
|
||||
description: 'Create or update multiple tags in a single request.',
|
||||
@ -67,6 +71,7 @@ export class TagController {
|
||||
|
||||
@Get(':id')
|
||||
@Authenticated({ permission: Permission.TagRead })
|
||||
@ZodSerializerDto(TagResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Retrieve a tag',
|
||||
description: 'Retrieve a specific tag by its ID.',
|
||||
@ -78,6 +83,7 @@ export class TagController {
|
||||
|
||||
@Put(':id')
|
||||
@Authenticated({ permission: Permission.TagUpdate })
|
||||
@ZodSerializerDto(TagResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Update a tag',
|
||||
description: 'Update an existing tag identified by its ID.',
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, Query } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { ZodSerializerDto } from 'nestjs-zod';
|
||||
import { Endpoint, HistoryBuilder } from 'src/decorators';
|
||||
import { AssetStatsDto, AssetStatsResponseDto } from 'src/dtos/asset.dto';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
@ -24,6 +25,7 @@ export class UserAdminController {
|
||||
|
||||
@Get()
|
||||
@Authenticated({ permission: Permission.AdminUserRead, admin: true })
|
||||
@ZodSerializerDto([UserAdminResponseDto])
|
||||
@Endpoint({
|
||||
summary: 'Search users',
|
||||
description: 'Search for users.',
|
||||
@ -35,6 +37,7 @@ export class UserAdminController {
|
||||
|
||||
@Post()
|
||||
@Authenticated({ permission: Permission.AdminUserCreate, admin: true })
|
||||
@ZodSerializerDto(UserAdminResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Create a user',
|
||||
description: 'Create a new user.',
|
||||
@ -46,6 +49,7 @@ export class UserAdminController {
|
||||
|
||||
@Get(':id')
|
||||
@Authenticated({ permission: Permission.AdminUserRead, admin: true })
|
||||
@ZodSerializerDto(UserAdminResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Retrieve a user',
|
||||
description: 'Retrieve a specific user by their ID.',
|
||||
@ -57,6 +61,7 @@ export class UserAdminController {
|
||||
|
||||
@Put(':id')
|
||||
@Authenticated({ permission: Permission.AdminUserUpdate, admin: true })
|
||||
@ZodSerializerDto(UserAdminResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Update a user',
|
||||
description: 'Update an existing user.',
|
||||
@ -72,6 +77,7 @@ export class UserAdminController {
|
||||
|
||||
@Delete(':id')
|
||||
@Authenticated({ permission: Permission.AdminUserDelete, admin: true })
|
||||
@ZodSerializerDto(UserAdminResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Delete a user',
|
||||
description: 'Delete a user.',
|
||||
@ -140,6 +146,7 @@ export class UserAdminController {
|
||||
@Post(':id/restore')
|
||||
@Authenticated({ permission: Permission.AdminUserDelete, admin: true })
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ZodSerializerDto(UserAdminResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Restore a deleted user',
|
||||
description: 'Restore a previously deleted user.',
|
||||
|
||||
@ -15,6 +15,7 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { ApiBody, ApiConsumes, ApiTags } from '@nestjs/swagger';
|
||||
import { NextFunction, Response } from 'express';
|
||||
import { ZodSerializerDto } from 'nestjs-zod';
|
||||
import { Endpoint, HistoryBuilder } from 'src/decorators';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import { LicenseKeyDto, LicenseResponseDto } from 'src/dtos/license.dto';
|
||||
@ -40,6 +41,7 @@ export class UserController {
|
||||
|
||||
@Get()
|
||||
@Authenticated({ permission: Permission.UserRead })
|
||||
@ZodSerializerDto([UserResponseDto])
|
||||
@Endpoint({
|
||||
summary: 'Get all users',
|
||||
description: 'Retrieve a list of all users on the server.',
|
||||
@ -51,6 +53,7 @@ export class UserController {
|
||||
|
||||
@Get('me')
|
||||
@Authenticated({ permission: Permission.UserRead })
|
||||
@ZodSerializerDto(UserAdminResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Get current user',
|
||||
description: 'Retrieve information about the user making the API request.',
|
||||
@ -62,6 +65,7 @@ export class UserController {
|
||||
|
||||
@Put('me')
|
||||
@Authenticated({ permission: Permission.UserUpdate })
|
||||
@ZodSerializerDto(UserAdminResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Update current user',
|
||||
description: 'Update the current user making the API request.',
|
||||
@ -166,6 +170,7 @@ export class UserController {
|
||||
|
||||
@Get(':id')
|
||||
@Authenticated({ permission: Permission.UserRead })
|
||||
@ZodSerializerDto(UserResponseDto)
|
||||
@Endpoint({
|
||||
summary: 'Retrieve a user',
|
||||
description: 'Retrieve a specific user by their ID.',
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Controller, Get, Query } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { ZodSerializerDto } from 'nestjs-zod';
|
||||
import { Endpoint, HistoryBuilder } from 'src/decorators';
|
||||
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
@ -25,6 +26,7 @@ export class ViewController {
|
||||
|
||||
@Get('folder')
|
||||
@Authenticated({ permission: Permission.FolderRead })
|
||||
@ZodSerializerDto([AssetResponseDto])
|
||||
@Endpoint({
|
||||
summary: 'Retrieve assets by original path',
|
||||
description: 'Retrieve assets that are children of a specific folder.',
|
||||
|
||||
@ -11,8 +11,8 @@ describe('mapAlbum', () => {
|
||||
.asset({ localDateTime: startDate }, (builder) => builder.exif())
|
||||
.build();
|
||||
const dto = mapAlbum(getForAlbum(album));
|
||||
expect(dto.startDate).toEqual(startDate.toISOString());
|
||||
expect(dto.endDate).toEqual(endDate.toISOString());
|
||||
expect(dto.startDate).toEqual(startDate);
|
||||
expect(dto.endDate).toEqual(endDate);
|
||||
});
|
||||
|
||||
it('should not set start and end dates for empty assets', () => {
|
||||
|
||||
@ -6,8 +6,7 @@ import { MapAsset } from 'src/dtos/asset-response.dto';
|
||||
import { UserResponseSchema, mapUser } from 'src/dtos/user.dto';
|
||||
import { AlbumUserRole, AlbumUserRoleSchema, AssetOrder, AssetOrderSchema } from 'src/enum';
|
||||
import { MaybeDehydrated } from 'src/types';
|
||||
import { asDateString } from 'src/utils/date';
|
||||
import { stringToBool } from 'src/validation';
|
||||
import { isoDatetimeToDate, stringToBool } from 'src/validation';
|
||||
import z from 'zod';
|
||||
|
||||
const AlbumUserAddSchema = z
|
||||
@ -105,10 +104,8 @@ export const AlbumResponseSchema = z
|
||||
id: z.string().describe('Album ID'),
|
||||
albumName: z.string().describe('Album name'),
|
||||
description: z.string().describe('Album description'),
|
||||
// TODO: use `isoDatetimeToDate` when using `ZodSerializerDto` on the controllers.
|
||||
createdAt: z.string().meta({ format: 'date-time' }).describe('Creation date'),
|
||||
// TODO: use `isoDatetimeToDate` when using `ZodSerializerDto` on the controllers.
|
||||
updatedAt: z.string().meta({ format: 'date-time' }).describe('Last update date'),
|
||||
createdAt: isoDatetimeToDate.describe('Creation date'),
|
||||
updatedAt: isoDatetimeToDate.describe('Last update date'),
|
||||
albumThumbnailAssetId: z.string().nullable().describe('Thumbnail asset ID'),
|
||||
shared: z.boolean().describe('Is shared album'),
|
||||
albumUsers: z
|
||||
@ -119,16 +116,9 @@ export const AlbumResponseSchema = z
|
||||
),
|
||||
hasSharedLink: z.boolean().describe('Has shared link'),
|
||||
assetCount: z.int().min(0).describe('Number of assets'),
|
||||
// TODO: use `isoDatetimeToDate` when using `ZodSerializerDto` on the controllers.
|
||||
lastModifiedAssetTimestamp: z
|
||||
.string()
|
||||
.meta({ format: 'date-time' })
|
||||
.optional()
|
||||
.describe('Last modified asset timestamp'),
|
||||
// TODO: use `isoDatetimeToDate` when using `ZodSerializerDto` on the controllers.
|
||||
startDate: z.string().meta({ format: 'date-time' }).optional().describe('Start date (earliest asset)'),
|
||||
// TODO: use `isoDatetimeToDate` when using `ZodSerializerDto` on the controllers.
|
||||
endDate: z.string().meta({ format: 'date-time' }).optional().describe('End date (latest asset)'),
|
||||
lastModifiedAssetTimestamp: isoDatetimeToDate.optional().describe('Last modified asset timestamp'),
|
||||
startDate: isoDatetimeToDate.optional().describe('Start date (earliest asset)'),
|
||||
endDate: isoDatetimeToDate.optional().describe('End date (latest asset)'),
|
||||
isActivityEnabled: z.boolean().describe('Activity feed enabled'),
|
||||
order: AssetOrderSchema.optional(),
|
||||
contributorCounts: z.array(ContributorCountResponseSchema).optional(),
|
||||
@ -144,7 +134,7 @@ export class UpdateAlbumDto extends createZodDto(UpdateAlbumSchema) {}
|
||||
export class GetAlbumsDto extends createZodDto(GetAlbumsSchema) {}
|
||||
export class AlbumStatisticsResponseDto extends createZodDto(AlbumStatisticsResponseSchema) {}
|
||||
export class UpdateAlbumUserDto extends createZodDto(UpdateAlbumUserSchema) {}
|
||||
export class AlbumResponseDto extends createZodDto(AlbumResponseSchema) {}
|
||||
export class AlbumResponseDto extends createZodDto(AlbumResponseSchema, { codec: true }) {}
|
||||
class AlbumUserResponseDto extends createZodDto(AlbumUserResponseSchema) {}
|
||||
|
||||
export type MapAlbumDto = {
|
||||
@ -190,14 +180,14 @@ export const mapAlbum = (entity: MaybeDehydrated<MapAlbumDto>): AlbumResponseDto
|
||||
albumName: entity.albumName,
|
||||
description: entity.description,
|
||||
albumThumbnailAssetId: entity.albumThumbnailAssetId,
|
||||
createdAt: asDateString(entity.createdAt),
|
||||
updatedAt: asDateString(entity.updatedAt),
|
||||
createdAt: new Date(entity.createdAt),
|
||||
updatedAt: new Date(entity.updatedAt),
|
||||
id: entity.id,
|
||||
albumUsers,
|
||||
shared: hasSharedUser || hasSharedLink,
|
||||
hasSharedLink,
|
||||
startDate: asDateString(startDate),
|
||||
endDate: asDateString(endDate),
|
||||
startDate: startDate ? new Date(startDate) : undefined,
|
||||
endDate: endDate ? new Date(endDate) : undefined,
|
||||
assetCount: entity.assets?.length || 0,
|
||||
isActivityEnabled: entity.isActivityEnabled,
|
||||
order: entity.order,
|
||||
|
||||
@ -25,8 +25,8 @@ import {
|
||||
import { ImageDimensions, MaybeDehydrated } from 'src/types';
|
||||
import { getDimensions } from 'src/utils/asset.util';
|
||||
import { hexOrBufferToBase64 } from 'src/utils/bytes';
|
||||
import { asDateString } from 'src/utils/date';
|
||||
import { mimeTypes } from 'src/utils/mime-types';
|
||||
import { isoDatetimeToDate } from 'src/validation';
|
||||
import z from 'zod';
|
||||
|
||||
const SanitizedAssetResponseSchema = z
|
||||
@ -40,13 +40,9 @@ const SanitizedAssetResponseSchema = z
|
||||
)
|
||||
.nullable(),
|
||||
originalMimeType: z.string().optional().describe('Original MIME type'),
|
||||
// TODO: use `isoDatetimeToDate` when using `ZodSerializerDto` on the controllers.
|
||||
localDateTime: z
|
||||
.string()
|
||||
.meta({ format: 'date-time' })
|
||||
.describe(
|
||||
'The local date and time when the photo/video was taken, derived from EXIF metadata. This represents the photographer\'s local time regardless of timezone, stored as a timezone-agnostic timestamp. Used for timeline grouping by "local" days and months.',
|
||||
),
|
||||
localDateTime: isoDatetimeToDate.describe(
|
||||
'The local date and time when the photo/video was taken, derived from EXIF metadata. This represents the photographer\'s local time regardless of timezone, stored as a timezone-agnostic timestamp. Used for timeline grouping by "local" days and months.',
|
||||
),
|
||||
duration: z.string().nullable().describe('Video/gif duration in hh:mm:ss.SSS format (null for static images)'),
|
||||
livePhotoVideoId: z.string().nullish().describe('Live photo video ID'),
|
||||
hasMetadata: z.boolean().describe('Whether asset has metadata'),
|
||||
@ -55,7 +51,7 @@ const SanitizedAssetResponseSchema = z
|
||||
})
|
||||
.meta({ id: 'SanitizedAssetResponseDto' });
|
||||
|
||||
export class SanitizedAssetResponseDto extends createZodDto(SanitizedAssetResponseSchema) {}
|
||||
export class SanitizedAssetResponseDto extends createZodDto(SanitizedAssetResponseSchema, { codec: true }) {}
|
||||
|
||||
const AssetStackResponseSchema = z
|
||||
.object({
|
||||
@ -67,11 +63,7 @@ const AssetStackResponseSchema = z
|
||||
|
||||
export const AssetResponseSchema = SanitizedAssetResponseSchema.extend(
|
||||
z.object({
|
||||
// TODO: use `isoDatetimeToDate` when using `ZodSerializerDto` on the controllers.
|
||||
createdAt: z
|
||||
.string()
|
||||
.meta({ format: 'date-time' })
|
||||
.describe('The UTC timestamp when the asset was originally uploaded to Immich.'),
|
||||
createdAt: isoDatetimeToDate.describe('The UTC timestamp when the asset was originally uploaded to Immich.'),
|
||||
ownerId: z.string().describe('Owner user ID'),
|
||||
owner: UserResponseSchema.optional(),
|
||||
libraryId: z
|
||||
@ -81,25 +73,15 @@ export const AssetResponseSchema = SanitizedAssetResponseSchema.extend(
|
||||
.meta(new HistoryBuilder().added('v1').deprecated('v1').getExtensions()),
|
||||
originalPath: z.string().describe('Original file path'),
|
||||
originalFileName: z.string().describe('Original file name'),
|
||||
// TODO: use `isoDatetimeToDate` when using `ZodSerializerDto` on the controllers.
|
||||
fileCreatedAt: z
|
||||
.string()
|
||||
.meta({ format: 'date-time' })
|
||||
.describe(
|
||||
'The actual UTC timestamp when the file was created/captured, preserving timezone information. This is the authoritative timestamp for chronological sorting within timeline groups. Combined with timezone data, this can be used to determine the exact moment the photo was taken.',
|
||||
),
|
||||
fileModifiedAt: z
|
||||
.string()
|
||||
.meta({ format: 'date-time' })
|
||||
.describe(
|
||||
'The UTC timestamp when the file was last modified on the filesystem. This reflects the last time the physical file was changed, which may be different from when the photo was originally taken.',
|
||||
),
|
||||
updatedAt: z
|
||||
.string()
|
||||
.meta({ format: 'date-time' })
|
||||
.describe(
|
||||
'The UTC timestamp when the asset record was last updated in the database. This is automatically maintained by the database and reflects when any field in the asset was last modified.',
|
||||
),
|
||||
fileCreatedAt: isoDatetimeToDate.describe(
|
||||
'The actual UTC timestamp when the file was created/captured, preserving timezone information. This is the authoritative timestamp for chronological sorting within timeline groups. Combined with timezone data, this can be used to determine the exact moment the photo was taken.',
|
||||
),
|
||||
fileModifiedAt: isoDatetimeToDate.describe(
|
||||
'The UTC timestamp when the file was last modified on the filesystem. This reflects the last time the physical file was changed, which may be different from when the photo was originally taken.',
|
||||
),
|
||||
updatedAt: isoDatetimeToDate.describe(
|
||||
'The UTC timestamp when the asset record was last updated in the database. This is automatically maintained by the database and reflects when any field in the asset was last modified.',
|
||||
),
|
||||
isFavorite: z.boolean().describe('Is favorite'),
|
||||
isArchived: z.boolean().describe('Is archived'),
|
||||
isTrashed: z.boolean().describe('Is trashed'),
|
||||
@ -124,7 +106,7 @@ export const AssetResponseSchema = SanitizedAssetResponseSchema.extend(
|
||||
}).shape,
|
||||
).meta({ id: 'AssetResponseDto' });
|
||||
|
||||
export class AssetResponseDto extends createZodDto(AssetResponseSchema) {}
|
||||
export class AssetResponseDto extends createZodDto(AssetResponseSchema, { codec: true }) {}
|
||||
|
||||
export type MapAsset = {
|
||||
createdAt: Date;
|
||||
@ -220,7 +202,7 @@ export function mapAsset(entity: MaybeDehydrated<MapAsset>, options: AssetMapOpt
|
||||
type: entity.type,
|
||||
originalMimeType: mimeTypes.lookup(entity.originalFileName),
|
||||
thumbhash: entity.thumbhash ? hexOrBufferToBase64(entity.thumbhash) : null,
|
||||
localDateTime: asDateString(entity.localDateTime),
|
||||
localDateTime: new Date(entity.localDateTime),
|
||||
duration: entity.duration,
|
||||
livePhotoVideoId: entity.livePhotoVideoId,
|
||||
hasMetadata: false,
|
||||
@ -234,7 +216,7 @@ export function mapAsset(entity: MaybeDehydrated<MapAsset>, options: AssetMapOpt
|
||||
|
||||
return {
|
||||
id: entity.id,
|
||||
createdAt: asDateString(entity.createdAt),
|
||||
createdAt: new Date(entity.createdAt),
|
||||
ownerId: entity.ownerId,
|
||||
owner: entity.owner ? mapUser(entity.owner) : undefined,
|
||||
libraryId: entity.libraryId,
|
||||
@ -243,10 +225,10 @@ export function mapAsset(entity: MaybeDehydrated<MapAsset>, options: AssetMapOpt
|
||||
originalFileName: entity.originalFileName,
|
||||
originalMimeType: mimeTypes.lookup(entity.originalFileName),
|
||||
thumbhash: entity.thumbhash ? hexOrBufferToBase64(entity.thumbhash) : null,
|
||||
fileCreatedAt: asDateString(entity.fileCreatedAt),
|
||||
fileModifiedAt: asDateString(entity.fileModifiedAt),
|
||||
localDateTime: asDateString(entity.localDateTime),
|
||||
updatedAt: asDateString(entity.updatedAt),
|
||||
fileCreatedAt: new Date(entity.fileCreatedAt),
|
||||
fileModifiedAt: new Date(entity.fileModifiedAt),
|
||||
localDateTime: new Date(entity.localDateTime),
|
||||
updatedAt: new Date(entity.updatedAt),
|
||||
isFavorite: options.auth?.user.id === entity.ownerId && entity.isFavorite,
|
||||
isArchived: entity.visibility === AssetVisibility.Archive,
|
||||
isTrashed: !!entity.deletedAt,
|
||||
|
||||
@ -21,7 +21,10 @@ export type AuthDto = {
|
||||
|
||||
const LoginCredentialSchema = z
|
||||
.object({
|
||||
email: toEmail.describe('User email').meta({ example: 'testuser@email.com' }),
|
||||
email: toEmail
|
||||
.transform((val) => val.toLowerCase())
|
||||
.describe('User email')
|
||||
.meta({ example: 'testuser@email.com' }),
|
||||
password: z.string().describe('User password').meta({ example: 'password' }),
|
||||
})
|
||||
.meta({ id: 'LoginCredentialDto' });
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { createZodDto } from 'nestjs-zod';
|
||||
import { Exif } from 'src/database';
|
||||
import { MaybeDehydrated } from 'src/types';
|
||||
import { asDateString } from 'src/utils/date';
|
||||
import { isoDatetimeToDate } from 'src/validation';
|
||||
import z from 'zod';
|
||||
|
||||
export const ExifResponseSchema = z
|
||||
@ -12,10 +12,8 @@ export const ExifResponseSchema = z
|
||||
exifImageHeight: z.number().min(0).nullish().default(null).describe('Image height in pixels'),
|
||||
fileSizeInByte: z.int().min(0).nullish().default(null).describe('File size in bytes'),
|
||||
orientation: z.string().nullish().default(null).describe('Image orientation'),
|
||||
// TODO: use `isoDatetimeToDate` when using `ZodSerializerDto` on the controllers.
|
||||
dateTimeOriginal: z.string().meta({ format: 'date-time' }).nullish().default(null).describe('Original date/time'),
|
||||
// TODO: use `isoDatetimeToDate` when using `ZodSerializerDto` on the controllers.
|
||||
modifyDate: z.string().meta({ format: 'date-time' }).nullish().default(null).describe('Modification date/time'),
|
||||
dateTimeOriginal: isoDatetimeToDate.nullish().default(null).describe('Original date/time'),
|
||||
modifyDate: isoDatetimeToDate.nullish().default(null).describe('Modification date/time'),
|
||||
timeZone: z.string().nullish().default(null).describe('Time zone'),
|
||||
lensModel: z.string().nullish().default(null).describe('Lens model'),
|
||||
fNumber: z.number().nullish().default(null).describe('F-number (aperture)'),
|
||||
@ -34,7 +32,7 @@ export const ExifResponseSchema = z
|
||||
.describe('EXIF response')
|
||||
.meta({ id: 'ExifResponseDto' });
|
||||
|
||||
class ExifResponseDto extends createZodDto(ExifResponseSchema) {}
|
||||
class ExifResponseDto extends createZodDto(ExifResponseSchema, { codec: true }) {}
|
||||
|
||||
export function mapExif(entity: MaybeDehydrated<Exif>): ExifResponseDto {
|
||||
return {
|
||||
@ -44,8 +42,8 @@ export function mapExif(entity: MaybeDehydrated<Exif>): ExifResponseDto {
|
||||
exifImageHeight: entity.exifImageHeight,
|
||||
fileSizeInByte: entity.fileSizeInByte ? Number.parseInt(entity.fileSizeInByte.toString()) : null,
|
||||
orientation: entity.orientation,
|
||||
dateTimeOriginal: asDateString(entity.dateTimeOriginal),
|
||||
modifyDate: asDateString(entity.modifyDate),
|
||||
dateTimeOriginal: entity.dateTimeOriginal ? new Date(entity.dateTimeOriginal) : null,
|
||||
modifyDate: entity.modifyDate ? new Date(entity.modifyDate) : null,
|
||||
timeZone: entity.timeZone,
|
||||
lensModel: entity.lensModel,
|
||||
fNumber: entity.fNumber,
|
||||
|
||||
@ -7,9 +7,8 @@ import { AssetEditActionItem } from 'src/dtos/editing.dto';
|
||||
import { SourceTypeSchema } from 'src/enum';
|
||||
import { AssetFaceTable } from 'src/schema/tables/asset-face.table';
|
||||
import { ImageDimensions, MaybeDehydrated } from 'src/types';
|
||||
import { asBirthDateString, asDateString } from 'src/utils/date';
|
||||
import { transformFaceBoundingBox } from 'src/utils/transform';
|
||||
import { emptyStringToNull, hexColor, stringToBool } from 'src/validation';
|
||||
import { emptyStringToNull, hexColor, isoDateToDate, isoDatetimeToDate, stringToBool } from 'src/validation';
|
||||
import z from 'zod';
|
||||
|
||||
const PersonCreateSchema = z
|
||||
@ -60,14 +59,10 @@ const PersonResponseSchema = z
|
||||
.object({
|
||||
id: z.string().describe('Person ID'),
|
||||
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(),
|
||||
birthDate: isoDateToDate.nullable().describe('Person date of birth'),
|
||||
thumbnailPath: z.string().describe('Thumbnail path'),
|
||||
isHidden: z.boolean().describe('Is hidden'),
|
||||
// TODO: use `isoDatetimeToDate` when using `ZodSerializerDto` on the controllers.
|
||||
updatedAt: z
|
||||
.string()
|
||||
.meta({ format: 'date-time' })
|
||||
updatedAt: isoDatetimeToDate
|
||||
.optional()
|
||||
.describe('Last update date')
|
||||
.meta(new HistoryBuilder().added('v1.107.0').stable('v2').getExtensions()),
|
||||
@ -89,7 +84,7 @@ export class PersonUpdateDto extends createZodDto(PersonUpdateSchema) {}
|
||||
export class PeopleUpdateDto extends createZodDto(PeopleUpdateSchema) {}
|
||||
export class MergePersonDto extends createZodDto(MergePersonSchema) {}
|
||||
export class PersonSearchDto extends createZodDto(PersonSearchSchema) {}
|
||||
export class PersonResponseDto extends createZodDto(PersonResponseSchema) {}
|
||||
export class PersonResponseDto extends createZodDto(PersonResponseSchema, { codec: true }) {}
|
||||
|
||||
export const AssetFaceWithoutPersonResponseSchema = z
|
||||
.object({
|
||||
@ -111,7 +106,7 @@ export const PersonWithFacesResponseSchema = PersonResponseSchema.extend({
|
||||
faces: z.array(AssetFaceWithoutPersonResponseSchema),
|
||||
}).meta({ id: 'PersonWithFacesResponseDto' });
|
||||
|
||||
export class PersonWithFacesResponseDto extends createZodDto(PersonWithFacesResponseSchema) {}
|
||||
export class PersonWithFacesResponseDto extends createZodDto(PersonWithFacesResponseSchema, { codec: true }) {}
|
||||
|
||||
const AssetFaceResponseSchema = AssetFaceWithoutPersonResponseSchema.extend({
|
||||
person: PersonResponseSchema.nullable(),
|
||||
@ -184,12 +179,12 @@ export function mapPerson(person: MaybeDehydrated<Person>): PersonResponseDto {
|
||||
return {
|
||||
id: person.id,
|
||||
name: person.name,
|
||||
birthDate: asBirthDateString(person.birthDate),
|
||||
birthDate: person.birthDate ? new Date(person.birthDate) : null,
|
||||
thumbnailPath: person.thumbnailPath,
|
||||
isHidden: person.isHidden,
|
||||
isFavorite: person.isFavorite,
|
||||
color: person.color ?? undefined,
|
||||
updatedAt: asDateString(person.updatedAt),
|
||||
updatedAt: person.updatedAt ? new Date(person.updatedAt) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { createZodDto } from 'nestjs-zod';
|
||||
import { Tag } from 'src/database';
|
||||
import { MaybeDehydrated } from 'src/types';
|
||||
import { asDateString } from 'src/utils/date';
|
||||
import { emptyStringToNull, hexColor } from 'src/validation';
|
||||
import { emptyStringToNull, hexColor, isoDatetimeToDate } from 'src/validation';
|
||||
import z from 'zod';
|
||||
|
||||
const TagCreateSchema = z
|
||||
@ -44,10 +43,8 @@ export const TagResponseSchema = z
|
||||
parentId: z.string().optional().describe('Parent tag ID'),
|
||||
name: z.string().describe('Tag name'),
|
||||
value: z.string().describe('Tag value (full path)'),
|
||||
// TODO: use `isoDatetimeToDate` when using `ZodSerializerDto` on the controllers.
|
||||
createdAt: z.string().meta({ format: 'date-time' }).describe('Creation date'),
|
||||
// TODO: use `isoDatetimeToDate` when using `ZodSerializerDto` on the controllers.
|
||||
updatedAt: z.string().meta({ format: 'date-time' }).describe('Last update date'),
|
||||
createdAt: isoDatetimeToDate.describe('Creation date'),
|
||||
updatedAt: isoDatetimeToDate.describe('Last update date'),
|
||||
color: z.string().optional().describe('Tag color (hex)'),
|
||||
})
|
||||
.meta({ id: 'TagResponseDto' });
|
||||
@ -57,7 +54,7 @@ export class TagUpdateDto extends createZodDto(TagUpdateSchema) {}
|
||||
export class TagUpsertDto extends createZodDto(TagUpsertSchema) {}
|
||||
export class TagBulkAssetsDto extends createZodDto(TagBulkAssetsSchema) {}
|
||||
export class TagBulkAssetsResponseDto extends createZodDto(TagBulkAssetsResponseSchema) {}
|
||||
export class TagResponseDto extends createZodDto(TagResponseSchema) {}
|
||||
export class TagResponseDto extends createZodDto(TagResponseSchema, { codec: true }) {}
|
||||
|
||||
export function mapTag(entity: MaybeDehydrated<Tag>): TagResponseDto {
|
||||
return {
|
||||
@ -65,8 +62,8 @@ export function mapTag(entity: MaybeDehydrated<Tag>): TagResponseDto {
|
||||
parentId: entity.parentId ?? undefined,
|
||||
name: entity.value.split('/').at(-1) as string,
|
||||
value: entity.value,
|
||||
createdAt: asDateString(entity.createdAt),
|
||||
updatedAt: asDateString(entity.updatedAt),
|
||||
createdAt: new Date(entity.createdAt),
|
||||
updatedAt: new Date(entity.updatedAt),
|
||||
color: entity.color ?? undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@ -3,13 +3,15 @@ import { User, UserAdmin } from 'src/database';
|
||||
import { pinCodeRegex } from 'src/dtos/auth.dto';
|
||||
import { UserAvatarColor, UserAvatarColorSchema, UserMetadataKey, UserStatusSchema } from 'src/enum';
|
||||
import { MaybeDehydrated, UserMetadataItem } from 'src/types';
|
||||
import { asDateString } from 'src/utils/date';
|
||||
import { emptyStringToNull, isoDatetimeToDate, sanitizeFilename, stringToBool, toEmail } from 'src/validation';
|
||||
import z from 'zod';
|
||||
|
||||
export const UserUpdateMeSchema = z
|
||||
.object({
|
||||
email: toEmail.optional().describe('User email'),
|
||||
email: toEmail
|
||||
.transform((val) => val.toLowerCase())
|
||||
.optional()
|
||||
.describe('User email'),
|
||||
password: z
|
||||
.string()
|
||||
.optional()
|
||||
@ -29,12 +31,11 @@ export const UserResponseSchema = z
|
||||
email: toEmail.describe('User email'),
|
||||
profileImagePath: z.string().describe('Profile image path'),
|
||||
avatarColor: UserAvatarColorSchema,
|
||||
// TODO: use `isoDatetimeToDate` when using `ZodSerializerDto` on the controllers.
|
||||
profileChangedAt: z.string().meta({ format: 'date-time' }).describe('Profile change date'),
|
||||
profileChangedAt: isoDatetimeToDate.describe('Profile change date'),
|
||||
})
|
||||
.meta({ id: 'UserResponseDto' });
|
||||
|
||||
export class UserResponseDto extends createZodDto(UserResponseSchema) {}
|
||||
export class UserResponseDto extends createZodDto(UserResponseSchema, { codec: true }) {}
|
||||
|
||||
const licenseKeyRegex = /^IM(SV|CL)(-[\dA-Za-z]{4}){8}$/;
|
||||
|
||||
@ -61,7 +62,7 @@ export const mapUser = (entity: MaybeDehydrated<User | UserAdmin>): UserResponse
|
||||
name: entity.name,
|
||||
profileImagePath: entity.profileImagePath,
|
||||
avatarColor: entity.avatarColor ?? emailToAvatarColor(entity.email),
|
||||
profileChangedAt: asDateString(entity.profileChangedAt),
|
||||
profileChangedAt: new Date(entity.profileChangedAt),
|
||||
};
|
||||
};
|
||||
|
||||
@ -76,7 +77,7 @@ export class UserAdminSearchDto extends createZodDto(UserAdminSearchSchema) {}
|
||||
|
||||
export const UserAdminCreateSchema = z
|
||||
.object({
|
||||
email: toEmail.describe('User email'),
|
||||
email: toEmail.transform((val) => val.toLowerCase()).describe('User email'),
|
||||
password: z.string().describe('User password'),
|
||||
name: z.string().describe('User name'),
|
||||
avatarColor: UserAvatarColorSchema.nullish(),
|
||||
@ -96,7 +97,10 @@ export class UserAdminCreateDto extends createZodDto(UserAdminCreateSchema) {}
|
||||
|
||||
const UserAdminUpdateSchema = z
|
||||
.object({
|
||||
email: toEmail.optional().describe('User email'),
|
||||
email: toEmail
|
||||
.transform((val) => val.toLowerCase())
|
||||
.optional()
|
||||
.describe('User email'),
|
||||
password: z.string().optional().describe('User password'),
|
||||
pinCode: emptyStringToNull(z.string().regex(pinCodeRegex).nullable())
|
||||
.optional()
|
||||
@ -135,7 +139,7 @@ const UserAdminResponseSchema = UserResponseSchema.extend({
|
||||
license: UserLicenseSchema.nullable(),
|
||||
}).meta({ id: 'UserAdminResponseDto' });
|
||||
|
||||
export class UserAdminResponseDto extends createZodDto(UserAdminResponseSchema) {}
|
||||
export class UserAdminResponseDto extends createZodDto(UserAdminResponseSchema, { codec: true }) {}
|
||||
|
||||
export function mapUserAdmin(entity: UserAdmin): UserAdminResponseDto {
|
||||
const metadata = entity.metadata || [];
|
||||
|
||||
@ -19,7 +19,6 @@ import { AlbumUserRole, Permission } from 'src/enum';
|
||||
import { AlbumAssetCount, AlbumInfoOptions } from 'src/repositories/album.repository';
|
||||
import { BaseService } from 'src/services/base.service';
|
||||
import { addAssets, removeAssets } from 'src/utils/asset.util';
|
||||
import { asDateString } from 'src/utils/date';
|
||||
import { getPreferences } from 'src/utils/preferences';
|
||||
|
||||
@Injectable()
|
||||
@ -63,11 +62,11 @@ export class AlbumService extends BaseService {
|
||||
return albums.map((album) => ({
|
||||
...mapAlbum(album),
|
||||
sharedLinks: undefined,
|
||||
startDate: asDateString(albumMetadata[album.id]?.startDate ?? undefined),
|
||||
endDate: asDateString(albumMetadata[album.id]?.endDate ?? undefined),
|
||||
startDate: albumMetadata[album.id]?.startDate ?? undefined,
|
||||
endDate: albumMetadata[album.id]?.endDate ?? undefined,
|
||||
assetCount: albumMetadata[album.id]?.assetCount ?? 0,
|
||||
// lastModifiedAssetTimestamp is only used in mobile app, please remove if not need
|
||||
lastModifiedAssetTimestamp: asDateString(albumMetadata[album.id]?.lastModifiedAssetTimestamp ?? undefined),
|
||||
lastModifiedAssetTimestamp: albumMetadata[album.id]?.lastModifiedAssetTimestamp ?? undefined,
|
||||
}));
|
||||
}
|
||||
|
||||
@ -83,10 +82,10 @@ export class AlbumService extends BaseService {
|
||||
|
||||
return {
|
||||
...mapAlbum(album),
|
||||
startDate: asDateString(albumMetadataForIds?.startDate ?? undefined),
|
||||
endDate: asDateString(albumMetadataForIds?.endDate ?? undefined),
|
||||
startDate: albumMetadataForIds?.startDate ?? undefined,
|
||||
endDate: albumMetadataForIds?.endDate ?? undefined,
|
||||
assetCount: albumMetadataForIds?.assetCount ?? 0,
|
||||
lastModifiedAssetTimestamp: asDateString(albumMetadataForIds?.lastModifiedAssetTimestamp ?? undefined),
|
||||
lastModifiedAssetTimestamp: albumMetadataForIds?.lastModifiedAssetTimestamp ?? undefined,
|
||||
contributorCounts: isShared ? await this.albumRepository.getContributorCounts(album.id) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@ -211,11 +211,12 @@ describe(PersonService.name, () => {
|
||||
await expect(sut.update(auth, person.id, { birthDate: '1976-06-30' })).resolves.toEqual({
|
||||
id: person.id,
|
||||
name: person.name,
|
||||
birthDate: '1976-06-30',
|
||||
birthDate: new Date('1976-06-30'),
|
||||
thumbnailPath: person.thumbnailPath,
|
||||
isHidden: false,
|
||||
isFavorite: false,
|
||||
updatedAt: expect.any(String),
|
||||
color: undefined,
|
||||
updatedAt: expect.any(Date),
|
||||
});
|
||||
expect(mocks.person.update).toHaveBeenCalledWith({ id: person.id, birthDate: '1976-06-30' });
|
||||
expect(mocks.job.queue).not.toHaveBeenCalled();
|
||||
@ -485,10 +486,11 @@ describe(PersonService.name, () => {
|
||||
birthDate: person.birthDate,
|
||||
isHidden: person.isHidden,
|
||||
isFavorite: person.isFavorite,
|
||||
color: undefined,
|
||||
id: person.id,
|
||||
name: person.name,
|
||||
thumbnailPath: person.thumbnailPath,
|
||||
updatedAt: expect.any(String),
|
||||
updatedAt: expect.any(Date),
|
||||
});
|
||||
|
||||
expect(mocks.job.queue).not.toHaveBeenCalledWith();
|
||||
@ -848,7 +850,7 @@ describe(PersonService.name, () => {
|
||||
facesRecognizedAt: expect.any(Date),
|
||||
});
|
||||
const facesRecognizedAt = mocks.asset.upsertJobStatus.mock.calls[0][0].facesRecognizedAt as Date;
|
||||
expect(facesRecognizedAt.getTime()).toBeGreaterThan(start);
|
||||
expect(facesRecognizedAt.getTime()).toBeGreaterThanOrEqual(start);
|
||||
});
|
||||
|
||||
it('should create a face with no person and queue recognition job', async () => {
|
||||
|
||||
@ -1,25 +1,5 @@
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
/**
|
||||
* Convert a date to a ISO 8601 datetime string.
|
||||
* @param x - The date to convert.
|
||||
* @returns The ISO 8601 datetime string.
|
||||
* @deprecated Remove this and all references when using `ZodSerializerDto` on the controllers. Then the codec in `isoDatetimeToDate` in validation.ts will handle the conversion instead.
|
||||
*/
|
||||
export const asDateString = <T extends Date | string | undefined | null>(x: T) => {
|
||||
return x instanceof Date ? x.toISOString() : (x as Exclude<T, Date>);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a date to a date string.
|
||||
* @param x - The date to convert.
|
||||
* @returns The date string.
|
||||
* @deprecated Remove this and all references when using `ZodSerializerDto` on the controllers. Then the codec in `isoDateToDate` in validation.ts will handle the conversion instead.
|
||||
*/
|
||||
export const asBirthDateString = (x: Date | string | null): string | null => {
|
||||
return x instanceof Date ? x.toISOString().split('T')[0] : x;
|
||||
};
|
||||
|
||||
export const extractTimeZone = (dateTimeOriginal?: string | null) => {
|
||||
const extractedTimeZone = dateTimeOriginal ? DateTime.fromISO(dateTimeOriginal, { setZone: true }).zone : undefined;
|
||||
return extractedTimeZone?.type === 'fixed' ? extractedTimeZone : undefined;
|
||||
|
||||
@ -5,7 +5,7 @@ import { getExifCount, suggestDuplicate, suggestDuplicateKeepAssetIds } from 'sr
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import type { z } from 'zod';
|
||||
|
||||
type ExifInfoInput = Partial<z.infer<typeof ExifResponseSchema>>;
|
||||
type ExifInfoInput = Partial<z.input<typeof ExifResponseSchema>>;
|
||||
|
||||
const createAsset = (
|
||||
id: string,
|
||||
@ -15,18 +15,18 @@ const createAsset = (
|
||||
id,
|
||||
type: AssetType.Image,
|
||||
thumbhash: null,
|
||||
localDateTime: new Date().toISOString(),
|
||||
localDateTime: new Date(),
|
||||
duration: '0:00:00.00000',
|
||||
hasMetadata: true,
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
createdAt: new Date().toISOString(),
|
||||
createdAt: new Date(),
|
||||
ownerId: 'owner-1',
|
||||
originalPath: '/path/to/asset',
|
||||
originalFileName: 'asset.jpg',
|
||||
fileCreatedAt: new Date().toISOString(),
|
||||
fileModifiedAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
fileCreatedAt: new Date(),
|
||||
fileModifiedAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
isFavorite: false,
|
||||
isArchived: false,
|
||||
isTrashed: false,
|
||||
|
||||
@ -135,15 +135,13 @@ export const isValidInteger = (value: number, options: { min?: number; max?: num
|
||||
* Converts email strings to lowercase and validates against HTML5 email regex
|
||||
* @docs https://zod.dev/api?id=email
|
||||
*/
|
||||
export const toEmail = z
|
||||
.email({
|
||||
pattern: z.regexes.html5Email,
|
||||
error: (iss) => `Invalid input: expected email, received ${typeof iss.input}`,
|
||||
})
|
||||
.transform((val) => val.toLowerCase());
|
||||
export const toEmail = z.email({
|
||||
pattern: z.regexes.html5Email,
|
||||
error: (iss) => `Invalid input: expected email, received ${typeof iss.input}`,
|
||||
});
|
||||
|
||||
/**
|
||||
* Parse ISO 8601 datetime strings to Date objects
|
||||
* Parse ISO 8601 datetime strings to Date objects. Requires `{ codec: true }` when using `createZodDto`.
|
||||
* @docs https://zod.dev/api?id=codec
|
||||
*/
|
||||
export const isoDatetimeToDate = z
|
||||
|
||||
8
server/test/fixtures/tag.stub.ts
vendored
8
server/test/fixtures/tag.stub.ts
vendored
@ -55,15 +55,15 @@ export const tagStub = {
|
||||
export const tagResponseStub = {
|
||||
tag1: Object.freeze<TagResponseDto>({
|
||||
id: 'tag-1',
|
||||
createdAt: '2021-01-01T00:00:00.000Z',
|
||||
updatedAt: '2021-01-01T00:00:00.000Z',
|
||||
createdAt: new Date('2021-01-01T00:00:00.000Z'),
|
||||
updatedAt: new Date('2021-01-01T00:00:00.000Z'),
|
||||
name: 'Tag1',
|
||||
value: 'Tag1',
|
||||
}),
|
||||
color1: Object.freeze<TagResponseDto>({
|
||||
id: 'tag-1',
|
||||
createdAt: '2021-01-01T00:00:00.000Z',
|
||||
updatedAt: '2021-01-01T00:00:00.000Z',
|
||||
createdAt: new Date('2021-01-01T00:00:00.000Z'),
|
||||
updatedAt: new Date('2021-01-01T00:00:00.000Z'),
|
||||
color: '#000000',
|
||||
name: 'Tag1',
|
||||
value: 'Tag1',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user