0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-01-21 00:52:43 -05:00

infra(server)!: fix typeorm asset entity relations (#1782)

* fix: add correct relations to asset typeorm entity

* fix: add missing createdAt column to asset entity

* ci: run check to make sure generated API is up-to-date

* ci: cancel workflows that aren't for the latest commit in a branch

* chore: add fvm config for flutter
This commit is contained in:
Zack Pollard 2023-02-19 16:44:53 +00:00 committed by GitHub
parent 000d0a08f4
commit 5ad4e5b614
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
65 changed files with 432 additions and 306 deletions

View file

@ -11,6 +11,10 @@ on:
push:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build-sign-android:
name: Build and sign Android
@ -24,7 +28,7 @@ jobs:
github_ref="${{ github.sha }}"
ref="${input_ref:-$github_ref}"
echo "ref=$ref" >> $GITHUB_OUTPUT
- uses: actions/checkout@v3
with:
ref: ${{ steps.get-ref.outputs.ref }}

View file

@ -4,24 +4,28 @@ on:
types:
- closed
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v3
- name: Cleanup
run: |
gh extension install actions/gh-actions-cache
REPO=${{ github.repository }}
BRANCH=${{ github.ref }}
echo "Fetching list of cache keys"
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1 )
## Setting this to not fail the workflow while deleting cache keys.
## Setting this to not fail the workflow while deleting cache keys.
set +e
echo "Deleting caches..."
for cacheKey in $cacheKeysForPR

View file

@ -20,6 +20,10 @@ on:
schedule:
- cron: '20 13 * * 1'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
analyze:
name: Analyze
@ -48,11 +52,11 @@ jobs:
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
@ -61,7 +65,7 @@ jobs:
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |

View file

@ -5,6 +5,10 @@ on:
push:
branches: ["main"]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
update-sdk-repos:
runs-on: ubuntu-latest

View file

@ -9,6 +9,10 @@ on:
release:
types: [published]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build_and_push:
runs-on: ubuntu-latest

View file

@ -7,6 +7,10 @@ on:
- cron: "0 23 * * *"
workflow_dispatch: # Allow for running this manually.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
j1:
name: github-repo-stats

View file

@ -17,13 +17,17 @@ on:
required: false
type: boolean
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
bump_version:
runs-on: ubuntu-latest
outputs:
ref: ${{ steps.push-tag.outputs.commit_long_sha }}
steps:
- name: Checkout
uses: actions/checkout@v3
@ -42,7 +46,7 @@ jobs:
message: "Version ${{ env.IMMICH_VERSION }}"
tag: ${{ env.IMMICH_VERSION }}
push: true
build_mobile:
uses: ./.github/workflows/build-mobile.yml
needs: bump_version
@ -59,7 +63,7 @@ jobs:
uses: actions/checkout@v3
with:
token: ${{ secrets.ORG_RELEASE_TOKEN }}
- name: Download APK
uses: actions/download-artifact@v3
with:

View file

@ -5,6 +5,10 @@ on:
push:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
mobile-dart-analyze:
name: Run Dart Code Analysis

View file

@ -5,6 +5,10 @@ on:
push:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
e2e-tests:
name: Run end-to-end test suites
@ -54,6 +58,27 @@ jobs:
working-directory: ./mobile
run: flutter test
generated-api-up-to-date:
name: Check generated files are up-to-date
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run API generation
run: cd server && npm ci && npm run api:generate
- name: Find file changes
uses: tj-actions/verify-changed-files@v13.1
id: verify-changed-files
with:
files: |
mobile/openapi
web/src/api/open-api
- name: Verify files have not changed
if: steps.verify-changed-files.outputs.files_changed == 'true'
run: |
echo "ERROR: Generated files not up to date!"
echo "Changed files: ${{ steps.verify-changed-files.outputs.changed_files }}"
exit 1
mobile-integration-tests:
name: Run mobile end-to-end integration tests
runs-on: macos-latest

View file

@ -0,0 +1,4 @@
{
"flutterSdkVersion": "3.7.0",
"flavors": {}
}

3
mobile/.gitignore vendored
View file

@ -32,6 +32,7 @@
.pub-cache/
.pub/
/build/
.fvm/flutter_sdk
# Web related
lib/generated_plugin_registrant.dart
@ -48,4 +49,4 @@ app.*.map.json
/android/app/release
# Fastlane
ios/fastlane/report.xml
ios/fastlane/report.xml

View file

@ -120,8 +120,8 @@ class AlbumViewerPage extends HookConsumerWidget {
}
Widget buildAlbumDateRange(Album album) {
final DateTime startDate = album.assets.first.createdAt;
final DateTime endDate = album.assets.last.createdAt; //Need default.
final DateTime startDate = album.assets.first.fileCreatedAt;
final DateTime endDate = album.assets.last.fileCreatedAt; //Need default.
final String startDateText =
DateFormat(startDate.year == endDate.year ? 'LLL d' : 'LLL d, y')
.format(startDate);

View file

@ -146,7 +146,7 @@ class ExifBottomSheet extends HookConsumerWidget {
buildDate() {
return Text(
DateFormat('date_format'.tr()).format(
assetDetail.createdAt.toLocal(),
assetDetail.fileCreatedAt.toLocal(),
),
style: const TextStyle(
fontWeight: FontWeight.bold,

View file

@ -2,26 +2,26 @@ import 'dart:convert';
class CurrentUploadAsset {
final String id;
final DateTime createdAt;
final DateTime fileCreatedAt;
final String fileName;
final String fileType;
CurrentUploadAsset({
required this.id,
required this.createdAt,
required this.fileCreatedAt,
required this.fileName,
required this.fileType,
});
CurrentUploadAsset copyWith({
String? id,
DateTime? createdAt,
DateTime? fileCreatedAt,
String? fileName,
String? fileType,
}) {
return CurrentUploadAsset(
id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
fileCreatedAt: fileCreatedAt ?? this.fileCreatedAt,
fileName: fileName ?? this.fileName,
fileType: fileType ?? this.fileType,
);
@ -31,7 +31,7 @@ class CurrentUploadAsset {
final result = <String, dynamic>{};
result.addAll({'id': id});
result.addAll({'createdAt': createdAt.millisecondsSinceEpoch});
result.addAll({'fileCreatedAt': fileCreatedAt.millisecondsSinceEpoch});
result.addAll({'fileName': fileName});
result.addAll({'fileType': fileType});
@ -41,7 +41,7 @@ class CurrentUploadAsset {
factory CurrentUploadAsset.fromMap(Map<String, dynamic> map) {
return CurrentUploadAsset(
id: map['id'] ?? '',
createdAt: DateTime.fromMillisecondsSinceEpoch(map['createdAt']),
fileCreatedAt: DateTime.fromMillisecondsSinceEpoch(map['fileCreatedAt']),
fileName: map['fileName'] ?? '',
fileType: map['fileType'] ?? '',
);
@ -54,7 +54,7 @@ class CurrentUploadAsset {
@override
String toString() {
return 'CurrentUploadAsset(id: $id, createdAt: $createdAt, fileName: $fileName, fileType: $fileType)';
return 'CurrentUploadAsset(id: $id, fileCreatedAt: $fileCreatedAt, fileName: $fileName, fileType: $fileType)';
}
@override
@ -63,7 +63,7 @@ class CurrentUploadAsset {
return other is CurrentUploadAsset &&
other.id == id &&
other.createdAt == createdAt &&
other.fileCreatedAt == fileCreatedAt &&
other.fileName == fileName &&
other.fileType == fileType;
}
@ -71,7 +71,7 @@ class CurrentUploadAsset {
@override
int get hashCode {
return id.hashCode ^
createdAt.hashCode ^
fileCreatedAt.hashCode ^
fileName.hashCode ^
fileType.hashCode;
}

View file

@ -2,7 +2,7 @@ import 'package:photo_manager/photo_manager.dart';
class ErrorUploadAsset {
final String id;
final DateTime createdAt;
final DateTime fileCreatedAt;
final String fileName;
final String fileType;
final AssetEntity asset;
@ -10,7 +10,7 @@ class ErrorUploadAsset {
const ErrorUploadAsset({
required this.id,
required this.createdAt,
required this.fileCreatedAt,
required this.fileName,
required this.fileType,
required this.asset,
@ -19,7 +19,7 @@ class ErrorUploadAsset {
ErrorUploadAsset copyWith({
String? id,
DateTime? createdAt,
DateTime? fileCreatedAt,
String? fileName,
String? fileType,
AssetEntity? asset,
@ -27,7 +27,7 @@ class ErrorUploadAsset {
}) {
return ErrorUploadAsset(
id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
fileCreatedAt: fileCreatedAt ?? this.fileCreatedAt,
fileName: fileName ?? this.fileName,
fileType: fileType ?? this.fileType,
asset: asset ?? this.asset,
@ -37,7 +37,7 @@ class ErrorUploadAsset {
@override
String toString() {
return 'ErrorUploadAsset(id: $id, createdAt: $createdAt, fileName: $fileName, fileType: $fileType, asset: $asset, errorMessage: $errorMessage)';
return 'ErrorUploadAsset(id: $id, fileCreatedAt: $fileCreatedAt, fileName: $fileName, fileType: $fileType, asset: $asset, errorMessage: $errorMessage)';
}
@override
@ -46,7 +46,7 @@ class ErrorUploadAsset {
return other is ErrorUploadAsset &&
other.id == id &&
other.createdAt == createdAt &&
other.fileCreatedAt == fileCreatedAt &&
other.fileName == fileName &&
other.fileType == fileType &&
other.asset == asset &&
@ -56,7 +56,7 @@ class ErrorUploadAsset {
@override
int get hashCode {
return id.hashCode ^
createdAt.hashCode ^
fileCreatedAt.hashCode ^
fileName.hashCode ^
fileType.hashCode ^
asset.hashCode ^

View file

@ -55,7 +55,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
selectedAlbumsBackupAssetsIds: const {},
currentUploadAsset: CurrentUploadAsset(
id: '...',
createdAt: DateTime.parse('2020-10-04'),
fileCreatedAt: DateTime.parse('2020-10-04'),
fileName: '...',
fileType: '...',
),

View file

@ -260,8 +260,8 @@ class BackupService {
req.fields['deviceAssetId'] = entity.id;
req.fields['deviceId'] = deviceId;
req.fields['assetType'] = _getAssetType(entity.type);
req.fields['createdAt'] = entity.createDateTime.toIso8601String();
req.fields['modifiedAt'] = entity.modifiedDateTime.toIso8601String();
req.fields['fileCreatedAt'] = entity.createDateTime.toIso8601String();
req.fields['fileModifiedAt'] = entity.modifiedDateTime.toIso8601String();
req.fields['isFavorite'] = entity.isFavorite.toString();
req.fields['fileExtension'] = fileExtension;
req.fields['duration'] = entity.videoDuration.toString();
@ -278,7 +278,7 @@ class BackupService {
setCurrentUploadAssetCb(
CurrentUploadAsset(
id: entity.id,
createdAt: entity.createDateTime.year == 1970
fileCreatedAt: entity.createDateTime.year == 1970
? entity.modifiedDateTime
: entity.createDateTime,
fileName: originalFileName,
@ -308,7 +308,7 @@ class BackupService {
ErrorUploadAsset(
asset: entity,
id: entity.id,
createdAt: entity.createDateTime,
fileCreatedAt: entity.createDateTime,
fileName: originalFileName,
fileType: _getAssetType(entity.type),
errorMessage: error['error'],

View file

@ -20,7 +20,7 @@ class CurrentUploadingAssetInfoBox extends HookConsumerWidget {
String getAssetCreationDate() {
return DateFormat.yMMMMd('en_US').format(
DateTime.parse(
asset.createdAt.toString(),
asset.fileCreatedAt.toString(),
).toLocal(),
);
}

View file

@ -89,7 +89,7 @@ class FailedBackupStatusPage extends HookConsumerWidget {
Text(
DateFormat.yMMMMd('en_US').format(
DateTime.parse(
errorAsset.createdAt.toString(),
errorAsset.fileCreatedAt.toString(),
).toLocal(),
),
style: TextStyle(

View file

@ -82,14 +82,14 @@ class RenderList {
if (groupBy == GroupAssetsBy.day) {
return assets.groupListsBy(
(element) {
final date = element.createdAt.toLocal();
final date = element.fileCreatedAt.toLocal();
return DateTime(date.year, date.month, date.day);
},
);
} else if (groupBy == GroupAssetsBy.month) {
return assets.groupListsBy(
(element) {
final date = element.createdAt.toLocal();
final date = element.fileCreatedAt.toLocal();
return DateTime(date.year, date.month);
},
);

View file

@ -10,8 +10,8 @@ import 'package:path/path.dart' as p;
class Asset {
Asset.remote(AssetResponseDto remote)
: remoteId = remote.id,
createdAt = DateTime.parse(remote.createdAt),
modifiedAt = DateTime.parse(remote.modifiedAt),
fileCreatedAt = DateTime.parse(remote.fileCreatedAt),
fileModifiedAt = DateTime.parse(remote.fileModifiedAt),
durationInSeconds = remote.duration.toDuration().inSeconds,
fileName = p.basename(remote.originalPath),
height = remote.exifInfo?.exifImageHeight?.toInt(),
@ -37,11 +37,11 @@ class Asset {
deviceAssetId = local.id,
deviceId = Hive.box(userInfoBox).get(deviceIdKey),
ownerId = owner,
modifiedAt = local.modifiedDateTime.toUtc(),
fileModifiedAt = local.modifiedDateTime.toUtc(),
isFavorite = local.isFavorite,
createdAt = local.createDateTime.toUtc() {
if (createdAt.year == 1970) {
createdAt = modifiedAt;
fileCreatedAt = local.createDateTime.toUtc() {
if (fileCreatedAt.year == 1970) {
fileCreatedAt = fileModifiedAt;
}
}
@ -51,8 +51,8 @@ class Asset {
required this.deviceAssetId,
required this.deviceId,
required this.ownerId,
required this.createdAt,
required this.modifiedAt,
required this.fileCreatedAt,
required this.fileModifiedAt,
this.latitude,
this.longitude,
required this.durationInSeconds,
@ -74,10 +74,10 @@ class Asset {
width: width!,
height: height!,
duration: durationInSeconds,
createDateSecond: createdAt.millisecondsSinceEpoch ~/ 1000,
createDateSecond: fileCreatedAt.millisecondsSinceEpoch ~/ 1000,
latitude: latitude,
longitude: longitude,
modifiedDateSecond: modifiedAt.millisecondsSinceEpoch ~/ 1000,
modifiedDateSecond: fileModifiedAt.millisecondsSinceEpoch ~/ 1000,
title: fileName,
);
}
@ -94,9 +94,9 @@ class Asset {
String ownerId;
DateTime createdAt;
DateTime fileCreatedAt;
DateTime modifiedAt;
DateTime fileModifiedAt;
double? latitude;
@ -146,8 +146,8 @@ class Asset {
json["deviceAssetId"] = deviceAssetId;
json["deviceId"] = deviceId;
json["ownerId"] = ownerId;
json["createdAt"] = createdAt.millisecondsSinceEpoch;
json["modifiedAt"] = modifiedAt.millisecondsSinceEpoch;
json["fileCreatedAt"] = fileCreatedAt.millisecondsSinceEpoch;
json["fileModifiedAt"] = fileModifiedAt.millisecondsSinceEpoch;
json["latitude"] = latitude;
json["longitude"] = longitude;
json["durationInSeconds"] = durationInSeconds;
@ -171,10 +171,10 @@ class Asset {
deviceAssetId: json["deviceAssetId"],
deviceId: json["deviceId"],
ownerId: json["ownerId"],
createdAt:
DateTime.fromMillisecondsSinceEpoch(json["createdAt"], isUtc: true),
modifiedAt: DateTime.fromMillisecondsSinceEpoch(
json["modifiedAt"],
fileCreatedAt:
DateTime.fromMillisecondsSinceEpoch(json["fileCreatedAt"], isUtc: true),
fileModifiedAt: DateTime.fromMillisecondsSinceEpoch(
json["fileModifiedAt"],
isUtc: true,
),
latitude: json["latitude"],

View file

@ -302,11 +302,11 @@ final assetGroupByMonthYearProvider = StateProvider((ref) {
ref.watch(assetProvider).allAssets.where((e) => e.isRemote).toList();
assets.sortByCompare<DateTime>(
(e) => e.createdAt,
(e) => e.fileCreatedAt,
(a, b) => b.compareTo(a),
);
return assets.groupListsBy(
(element) => DateFormat('MMMM, y').format(element.createdAt.toLocal()),
(element) => DateFormat('MMMM, y').format(element.fileCreatedAt.toLocal()),
);
});

View file

@ -3,7 +3,7 @@ Immich API
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
- API version: 1.47.2
- API version: 1.47.3
- Build package: org.openapitools.codegen.languages.DartClientCodegen
## Requirements

View file

@ -71,7 +71,7 @@ No authorization required
import 'package:openapi/api.dart';
final api_instance = APIKeyApi();
final id = 8.14; // num |
final id = id_example; // String |
try {
api_instance.deleteKey(id);
@ -84,7 +84,7 @@ try {
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**id** | **num**| |
**id** | **String**| |
### Return type
@ -113,7 +113,7 @@ No authorization required
import 'package:openapi/api.dart';
final api_instance = APIKeyApi();
final id = 8.14; // num |
final id = id_example; // String |
try {
final result = api_instance.getKey(id);
@ -127,7 +127,7 @@ try {
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**id** | **num**| |
**id** | **String**| |
### Return type
@ -195,7 +195,7 @@ No authorization required
import 'package:openapi/api.dart';
final api_instance = APIKeyApi();
final id = 8.14; // num |
final id = id_example; // String |
final aPIKeyUpdateDto = APIKeyUpdateDto(); // APIKeyUpdateDto |
try {
@ -210,7 +210,7 @@ try {
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**id** | **num**| |
**id** | **String**| |
**aPIKeyUpdateDto** | [**APIKeyUpdateDto**](APIKeyUpdateDto.md)| |
### Return type

View file

@ -8,7 +8,7 @@ import 'package:openapi/api.dart';
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**id** | **int** | |
**id** | **String** | |
**name** | **String** | |
**createdAt** | **String** | |
**updatedAt** | **String** | |

View file

@ -1109,7 +1109,7 @@ Name | Type | Description | Notes
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **uploadFile**
> AssetFileUploadResponseDto uploadFile(assetType, assetData, deviceAssetId, deviceId, createdAt, modifiedAt, isFavorite, fileExtension, livePhotoData, isVisible, duration)
> AssetFileUploadResponseDto uploadFile(assetType, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, isFavorite, fileExtension, livePhotoData, isVisible, duration)
@ -1130,8 +1130,8 @@ final assetType = ; // AssetTypeEnum |
final assetData = BINARY_DATA_HERE; // MultipartFile |
final deviceAssetId = deviceAssetId_example; // String |
final deviceId = deviceId_example; // String |
final createdAt = createdAt_example; // String |
final modifiedAt = modifiedAt_example; // String |
final fileCreatedAt = fileCreatedAt_example; // String |
final fileModifiedAt = fileModifiedAt_example; // String |
final isFavorite = true; // bool |
final fileExtension = fileExtension_example; // String |
final livePhotoData = BINARY_DATA_HERE; // MultipartFile |
@ -1139,7 +1139,7 @@ final isVisible = true; // bool |
final duration = duration_example; // String |
try {
final result = api_instance.uploadFile(assetType, assetData, deviceAssetId, deviceId, createdAt, modifiedAt, isFavorite, fileExtension, livePhotoData, isVisible, duration);
final result = api_instance.uploadFile(assetType, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, isFavorite, fileExtension, livePhotoData, isVisible, duration);
print(result);
} catch (e) {
print('Exception when calling AssetApi->uploadFile: $e\n');
@ -1154,8 +1154,8 @@ Name | Type | Description | Notes
**assetData** | **MultipartFile**| |
**deviceAssetId** | **String**| |
**deviceId** | **String**| |
**createdAt** | **String**| |
**modifiedAt** | **String**| |
**fileCreatedAt** | **String**| |
**fileModifiedAt** | **String**| |
**isFavorite** | **bool**| |
**fileExtension** | **String**| |
**livePhotoData** | **MultipartFile**| | [optional]

View file

@ -15,8 +15,8 @@ Name | Type | Description | Notes
**deviceId** | **String** | |
**originalPath** | **String** | |
**resizePath** | **String** | |
**createdAt** | **String** | |
**modifiedAt** | **String** | |
**fileCreatedAt** | **String** | |
**fileModifiedAt** | **String** | |
**updatedAt** | **String** | |
**isFavorite** | **bool** | |
**mimeType** | **String** | |

View file

@ -74,11 +74,11 @@ class APIKeyApi {
///
/// Parameters:
///
/// * [num] id (required):
Future<Response> deleteKeyWithHttpInfo(num id,) async {
/// * [String] id (required):
Future<Response> deleteKeyWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations
final path = r'/api-key/{id}'
.replaceAll('{id}', id.toString());
.replaceAll('{id}', id);
// ignore: prefer_final_locals
Object? postBody;
@ -105,8 +105,8 @@ class APIKeyApi {
///
/// Parameters:
///
/// * [num] id (required):
Future<void> deleteKey(num id,) async {
/// * [String] id (required):
Future<void> deleteKey(String id,) async {
final response = await deleteKeyWithHttpInfo(id,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
@ -119,11 +119,11 @@ class APIKeyApi {
///
/// Parameters:
///
/// * [num] id (required):
Future<Response> getKeyWithHttpInfo(num id,) async {
/// * [String] id (required):
Future<Response> getKeyWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations
final path = r'/api-key/{id}'
.replaceAll('{id}', id.toString());
.replaceAll('{id}', id);
// ignore: prefer_final_locals
Object? postBody;
@ -150,8 +150,8 @@ class APIKeyApi {
///
/// Parameters:
///
/// * [num] id (required):
Future<APIKeyResponseDto?> getKey(num id,) async {
/// * [String] id (required):
Future<APIKeyResponseDto?> getKey(String id,) async {
final response = await getKeyWithHttpInfo(id,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
@ -219,13 +219,13 @@ class APIKeyApi {
///
/// Parameters:
///
/// * [num] id (required):
/// * [String] id (required):
///
/// * [APIKeyUpdateDto] aPIKeyUpdateDto (required):
Future<Response> updateKeyWithHttpInfo(num id, APIKeyUpdateDto aPIKeyUpdateDto,) async {
Future<Response> updateKeyWithHttpInfo(String id, APIKeyUpdateDto aPIKeyUpdateDto,) async {
// ignore: prefer_const_declarations
final path = r'/api-key/{id}'
.replaceAll('{id}', id.toString());
.replaceAll('{id}', id);
// ignore: prefer_final_locals
Object? postBody = aPIKeyUpdateDto;
@ -252,10 +252,10 @@ class APIKeyApi {
///
/// Parameters:
///
/// * [num] id (required):
/// * [String] id (required):
///
/// * [APIKeyUpdateDto] aPIKeyUpdateDto (required):
Future<APIKeyResponseDto?> updateKey(num id, APIKeyUpdateDto aPIKeyUpdateDto,) async {
Future<APIKeyResponseDto?> updateKey(String id, APIKeyUpdateDto aPIKeyUpdateDto,) async {
final response = await updateKeyWithHttpInfo(id, aPIKeyUpdateDto,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));

View file

@ -1224,9 +1224,9 @@ class AssetApi {
///
/// * [String] deviceId (required):
///
/// * [String] createdAt (required):
/// * [String] fileCreatedAt (required):
///
/// * [String] modifiedAt (required):
/// * [String] fileModifiedAt (required):
///
/// * [bool] isFavorite (required):
///
@ -1237,7 +1237,7 @@ class AssetApi {
/// * [bool] isVisible:
///
/// * [String] duration:
Future<Response> uploadFileWithHttpInfo(AssetTypeEnum assetType, MultipartFile assetData, String deviceAssetId, String deviceId, String createdAt, String modifiedAt, bool isFavorite, String fileExtension, { MultipartFile? livePhotoData, bool? isVisible, String? duration, }) async {
Future<Response> uploadFileWithHttpInfo(AssetTypeEnum assetType, MultipartFile assetData, String deviceAssetId, String deviceId, String fileCreatedAt, String fileModifiedAt, bool isFavorite, String fileExtension, { MultipartFile? livePhotoData, bool? isVisible, String? duration, }) async {
// ignore: prefer_const_declarations
final path = r'/asset/upload';
@ -1274,13 +1274,13 @@ class AssetApi {
hasFields = true;
mp.fields[r'deviceId'] = parameterToString(deviceId);
}
if (createdAt != null) {
if (fileCreatedAt != null) {
hasFields = true;
mp.fields[r'createdAt'] = parameterToString(createdAt);
mp.fields[r'fileCreatedAt'] = parameterToString(fileCreatedAt);
}
if (modifiedAt != null) {
if (fileModifiedAt != null) {
hasFields = true;
mp.fields[r'modifiedAt'] = parameterToString(modifiedAt);
mp.fields[r'fileModifiedAt'] = parameterToString(fileModifiedAt);
}
if (isFavorite != null) {
hasFields = true;
@ -1325,9 +1325,9 @@ class AssetApi {
///
/// * [String] deviceId (required):
///
/// * [String] createdAt (required):
/// * [String] fileCreatedAt (required):
///
/// * [String] modifiedAt (required):
/// * [String] fileModifiedAt (required):
///
/// * [bool] isFavorite (required):
///
@ -1338,8 +1338,8 @@ class AssetApi {
/// * [bool] isVisible:
///
/// * [String] duration:
Future<AssetFileUploadResponseDto?> uploadFile(AssetTypeEnum assetType, MultipartFile assetData, String deviceAssetId, String deviceId, String createdAt, String modifiedAt, bool isFavorite, String fileExtension, { MultipartFile? livePhotoData, bool? isVisible, String? duration, }) async {
final response = await uploadFileWithHttpInfo(assetType, assetData, deviceAssetId, deviceId, createdAt, modifiedAt, isFavorite, fileExtension, livePhotoData: livePhotoData, isVisible: isVisible, duration: duration, );
Future<AssetFileUploadResponseDto?> uploadFile(AssetTypeEnum assetType, MultipartFile assetData, String deviceAssetId, String deviceId, String fileCreatedAt, String fileModifiedAt, bool isFavorite, String fileExtension, { MultipartFile? livePhotoData, bool? isVisible, String? duration, }) async {
final response = await uploadFileWithHttpInfo(assetType, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, isFavorite, fileExtension, livePhotoData: livePhotoData, isVisible: isVisible, duration: duration, );
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}

View file

@ -19,7 +19,7 @@ class APIKeyResponseDto {
required this.updatedAt,
});
int id;
String id;
String name;
@ -73,7 +73,7 @@ class APIKeyResponseDto {
}());
return APIKeyResponseDto(
id: mapValueOfType<int>(json, r'id')!,
id: mapValueOfType<String>(json, r'id')!,
name: mapValueOfType<String>(json, r'name')!,
createdAt: mapValueOfType<String>(json, r'createdAt')!,
updatedAt: mapValueOfType<String>(json, r'updatedAt')!,

View file

@ -20,8 +20,8 @@ class AssetResponseDto {
required this.deviceId,
required this.originalPath,
required this.resizePath,
required this.createdAt,
required this.modifiedAt,
required this.fileCreatedAt,
required this.fileModifiedAt,
required this.updatedAt,
required this.isFavorite,
required this.mimeType,
@ -48,9 +48,9 @@ class AssetResponseDto {
String? resizePath;
String createdAt;
String fileCreatedAt;
String modifiedAt;
String fileModifiedAt;
String updatedAt;
@ -93,8 +93,8 @@ class AssetResponseDto {
other.deviceId == deviceId &&
other.originalPath == originalPath &&
other.resizePath == resizePath &&
other.createdAt == createdAt &&
other.modifiedAt == modifiedAt &&
other.fileCreatedAt == fileCreatedAt &&
other.fileModifiedAt == fileModifiedAt &&
other.updatedAt == updatedAt &&
other.isFavorite == isFavorite &&
other.mimeType == mimeType &&
@ -116,8 +116,8 @@ class AssetResponseDto {
(deviceId.hashCode) +
(originalPath.hashCode) +
(resizePath == null ? 0 : resizePath!.hashCode) +
(createdAt.hashCode) +
(modifiedAt.hashCode) +
(fileCreatedAt.hashCode) +
(fileModifiedAt.hashCode) +
(updatedAt.hashCode) +
(isFavorite.hashCode) +
(mimeType == null ? 0 : mimeType!.hashCode) +
@ -130,7 +130,7 @@ class AssetResponseDto {
(tags.hashCode);
@override
String toString() => 'AssetResponseDto[type=$type, id=$id, deviceAssetId=$deviceAssetId, ownerId=$ownerId, deviceId=$deviceId, originalPath=$originalPath, resizePath=$resizePath, createdAt=$createdAt, modifiedAt=$modifiedAt, updatedAt=$updatedAt, isFavorite=$isFavorite, mimeType=$mimeType, duration=$duration, webpPath=$webpPath, encodedVideoPath=$encodedVideoPath, exifInfo=$exifInfo, smartInfo=$smartInfo, livePhotoVideoId=$livePhotoVideoId, tags=$tags]';
String toString() => 'AssetResponseDto[type=$type, id=$id, deviceAssetId=$deviceAssetId, ownerId=$ownerId, deviceId=$deviceId, originalPath=$originalPath, resizePath=$resizePath, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, updatedAt=$updatedAt, isFavorite=$isFavorite, mimeType=$mimeType, duration=$duration, webpPath=$webpPath, encodedVideoPath=$encodedVideoPath, exifInfo=$exifInfo, smartInfo=$smartInfo, livePhotoVideoId=$livePhotoVideoId, tags=$tags]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
@ -145,8 +145,8 @@ class AssetResponseDto {
} else {
// json[r'resizePath'] = null;
}
json[r'createdAt'] = this.createdAt;
json[r'modifiedAt'] = this.modifiedAt;
json[r'fileCreatedAt'] = this.fileCreatedAt;
json[r'fileModifiedAt'] = this.fileModifiedAt;
json[r'updatedAt'] = this.updatedAt;
json[r'isFavorite'] = this.isFavorite;
if (this.mimeType != null) {
@ -210,8 +210,8 @@ class AssetResponseDto {
deviceId: mapValueOfType<String>(json, r'deviceId')!,
originalPath: mapValueOfType<String>(json, r'originalPath')!,
resizePath: mapValueOfType<String>(json, r'resizePath'),
createdAt: mapValueOfType<String>(json, r'createdAt')!,
modifiedAt: mapValueOfType<String>(json, r'modifiedAt')!,
fileCreatedAt: mapValueOfType<String>(json, r'fileCreatedAt')!,
fileModifiedAt: mapValueOfType<String>(json, r'fileModifiedAt')!,
updatedAt: mapValueOfType<String>(json, r'updatedAt')!,
isFavorite: mapValueOfType<bool>(json, r'isFavorite')!,
mimeType: mapValueOfType<String>(json, r'mimeType'),
@ -278,8 +278,8 @@ class AssetResponseDto {
'deviceId',
'originalPath',
'resizePath',
'createdAt',
'modifiedAt',
'fileCreatedAt',
'fileModifiedAt',
'updatedAt',
'isFavorite',
'mimeType',

View file

@ -26,14 +26,14 @@ void main() {
//
//
//Future deleteKey(num id) async
//Future deleteKey(String id) async
test('test deleteKey', () async {
// TODO
});
//
//
//Future<APIKeyResponseDto> getKey(num id) async
//Future<APIKeyResponseDto> getKey(String id) async
test('test getKey', () async {
// TODO
});
@ -47,7 +47,7 @@ void main() {
//
//
//Future<APIKeyResponseDto> updateKey(num id, APIKeyUpdateDto aPIKeyUpdateDto) async
//Future<APIKeyResponseDto> updateKey(String id, APIKeyUpdateDto aPIKeyUpdateDto) async
test('test updateKey', () async {
// TODO
});

View file

@ -16,7 +16,7 @@ void main() {
// final instance = APIKeyResponseDto();
group('test APIKeyResponseDto', () {
// int id
// String id
test('to test the property `id`', () async {
// TODO
});

View file

@ -173,7 +173,7 @@ void main() {
//
//
//Future<AssetFileUploadResponseDto> uploadFile(AssetTypeEnum assetType, MultipartFile assetData, String deviceAssetId, String deviceId, String createdAt, String modifiedAt, bool isFavorite, String fileExtension, { MultipartFile livePhotoData, bool isVisible, String duration }) async
//Future<AssetFileUploadResponseDto> uploadFile(AssetTypeEnum assetType, MultipartFile assetData, String deviceAssetId, String deviceId, String fileCreatedAt, String fileModifiedAt, bool isFavorite, String fileExtension, { MultipartFile livePhotoData, bool isVisible, String duration }) async
test('test uploadFile', () async {
// TODO
});

View file

@ -51,13 +51,13 @@ void main() {
// TODO
});
// String createdAt
test('to test the property `createdAt`', () async {
// String fileCreatedAt
test('to test the property `fileCreatedAt`', () async {
// TODO
});
// String modifiedAt
test('to test the property `modifiedAt`', () async {
// String fileModifiedAt
test('to test the property `fileModifiedAt`', () async {
// TODO
});

View file

@ -1513,5 +1513,5 @@ packages:
source: hosted
version: "3.1.1"
sdks:
dart: ">=2.19.0 <3.0.0"
dart: ">=2.19.0 <4.0.0"
flutter: ">=3.3.0"

View file

@ -16,8 +16,8 @@ void main() {
deviceAssetId: '$i',
deviceId: '',
ownerId: '',
createdAt: date,
modifiedAt: date,
fileCreatedAt: date,
fileModifiedAt: date,
durationInSeconds: 0,
fileName: '',
isFavorite: false,
@ -29,25 +29,25 @@ void main() {
assets.addAll(
testAssets.sublist(0, 5).map((e) {
e.createdAt = DateTime(2022, 1, 5);
e.fileCreatedAt = DateTime(2022, 1, 5);
return e;
}).toList(),
);
assets.addAll(
testAssets.sublist(5, 10).map((e) {
e.createdAt = DateTime(2022, 1, 10);
e.fileCreatedAt = DateTime(2022, 1, 10);
return e;
}).toList(),
);
assets.addAll(
testAssets.sublist(10, 15).map((e) {
e.createdAt = DateTime(2022, 2, 17);
e.fileCreatedAt = DateTime(2022, 2, 17);
return e;
}).toList(),
);
assets.addAll(
testAssets.sublist(15, 30).map((e) {
e.createdAt = DateTime(2022, 10, 15);
e.fileCreatedAt = DateTime(2022, 10, 15);
return e;
}).toList(),
);

View file

@ -79,7 +79,7 @@ export class AlbumRepository implements IAlbumRepository {
const queryProperties: FindManyOptions<AlbumEntity> = {
relations: { sharedUsers: true, assets: true, sharedLinks: true, owner: true },
order: { assets: { createdAt: 'ASC' }, createdAt: 'ASC' },
order: { assets: { fileCreatedAt: 'ASC' }, createdAt: 'ASC' },
};
let albumsQuery: Promise<AlbumEntity[]>;
@ -123,7 +123,7 @@ export class AlbumRepository implements IAlbumRepository {
const albums = await this.albumRepository.find({
where: { ownerId: userId, assets: { id: assetId } },
relations: { owner: true, assets: true, sharedUsers: true },
order: { assets: { createdAt: 'ASC' } },
order: { assets: { fileCreatedAt: 'ASC' } },
});
return albums;
@ -142,7 +142,7 @@ export class AlbumRepository implements IAlbumRepository {
},
order: {
assets: {
createdAt: 'ASC',
fileCreatedAt: 'ASC',
},
},
});

View file

@ -19,7 +19,9 @@ import { AssetSearchDto } from './dto/asset-search.dto';
export interface IAssetRepository {
get(id: string): Promise<AssetEntity | null>;
create(asset: Omit<AssetEntity, 'id'>): Promise<AssetEntity>;
create(
asset: Omit<AssetEntity, 'id' | 'createdAt' | 'updatedAt' | 'ownerId' | 'livePhotoVideoId'>,
): Promise<AssetEntity>;
remove(asset: AssetEntity): Promise<void>;
update(userId: string, asset: AssetEntity, dto: UpdateAssetDto): Promise<AssetEntity>;
@ -112,13 +114,13 @@ export class AssetRepository implements IAssetRepository {
.getMany();
}
async getAssetCountByUserId(userId: string): Promise<AssetCountByUserIdResponseDto> {
async getAssetCountByUserId(ownerId: string): Promise<AssetCountByUserIdResponseDto> {
// Get asset count by AssetType
const items = await this.assetRepository
.createQueryBuilder('asset')
.select(`COUNT(asset.id)`, 'count')
.addSelect(`asset.type`, 'type')
.where('"userId" = :userId', { userId: userId })
.where('"ownerId" = :ownerId', { ownerId: ownerId })
.andWhere('asset.isVisible = true')
.groupBy('asset.type')
.getRawMany();
@ -149,7 +151,7 @@ export class AssetRepository implements IAssetRepository {
// Get asset entity from a list of time buckets
return await this.assetRepository
.createQueryBuilder('asset')
.where('asset.userId = :userId', { userId: userId })
.where('asset.ownerId = :userId', { userId: userId })
.andWhere(`date_trunc('month', "createdAt") IN (:...buckets)`, {
buckets: [...getAssetByTimeBucketDto.timeBucket],
})
@ -167,7 +169,7 @@ export class AssetRepository implements IAssetRepository {
.createQueryBuilder('asset')
.select(`COUNT(asset.id)::int`, 'count')
.addSelect(`date_trunc('month', "createdAt")`, 'timeBucket')
.where('"userId" = :userId', { userId: userId })
.where('"ownerId" = :userId', { userId: userId })
.andWhere('asset.resizePath is not NULL')
.andWhere('asset.isVisible = true')
.groupBy(`date_trunc('month', "createdAt")`)
@ -178,7 +180,7 @@ export class AssetRepository implements IAssetRepository {
.createQueryBuilder('asset')
.select(`COUNT(asset.id)::int`, 'count')
.addSelect(`date_trunc('day', "createdAt")`, 'timeBucket')
.where('"userId" = :userId', { userId: userId })
.where('"ownerId" = :userId', { userId: userId })
.andWhere('asset.resizePath is not NULL')
.andWhere('asset.isVisible = true')
.groupBy(`date_trunc('day', "createdAt")`)
@ -192,7 +194,7 @@ export class AssetRepository implements IAssetRepository {
async getSearchPropertiesByUserId(userId: string): Promise<SearchPropertiesDto[]> {
return await this.assetRepository
.createQueryBuilder('asset')
.where('asset.userId = :userId', { userId: userId })
.where('asset.ownerId = :userId', { userId: userId })
.andWhere('asset.isVisible = true')
.leftJoin('asset.exifInfo', 'ei')
.leftJoin('asset.smartInfo', 'si')
@ -216,7 +218,7 @@ export class AssetRepository implements IAssetRepository {
SELECT DISTINCT ON (unnest(si.objects)) a.id, unnest(si.objects) as "object", a."resizePath", a."deviceAssetId", a."deviceId"
FROM assets a
LEFT JOIN smart_info si ON a.id = si."assetId"
WHERE a."userId" = $1
WHERE a."ownerId" = $1
AND a."isVisible" = true
AND si.objects IS NOT NULL
`,
@ -230,7 +232,7 @@ export class AssetRepository implements IAssetRepository {
SELECT DISTINCT ON (e.city) a.id, e.city, a."resizePath", a."deviceAssetId", a."deviceId"
FROM assets a
LEFT JOIN exif e ON a.id = e."assetId"
WHERE a."userId" = $1
WHERE a."ownerId" = $1
AND a."isVisible" = true
AND e.city IS NOT NULL
AND a.type = 'IMAGE';
@ -255,12 +257,12 @@ export class AssetRepository implements IAssetRepository {
/**
* Get all assets belong to the user on the database
* @param userId
* @param ownerId
*/
async getAllByUserId(userId: string, dto: AssetSearchDto): Promise<AssetEntity[]> {
async getAllByUserId(ownerId: string, dto: AssetSearchDto): Promise<AssetEntity[]> {
return this.assetRepository.find({
where: {
userId,
ownerId,
resizePath: Not(IsNull()),
isVisible: true,
isFavorite: dto.isFavorite,
@ -271,7 +273,7 @@ export class AssetRepository implements IAssetRepository {
},
skip: dto.skip || 0,
order: {
createdAt: 'DESC',
fileCreatedAt: 'DESC',
},
});
}
@ -280,7 +282,9 @@ export class AssetRepository implements IAssetRepository {
return this.assetRepository.findOne({ where: { id } });
}
async create(asset: Omit<AssetEntity, 'id'>): Promise<AssetEntity> {
async create(
asset: Omit<AssetEntity, 'id' | 'createdAt' | 'updatedAt' | 'ownerId' | 'livePhotoVideoId'>,
): Promise<AssetEntity> {
return this.assetRepository.save(asset);
}
@ -304,16 +308,16 @@ export class AssetRepository implements IAssetRepository {
/**
* Get assets by device's Id on the database
* @param userId
* @param ownerId
* @param deviceId
*
* @returns Promise<string[]> - Array of assetIds belong to the device
*/
async getAllByDeviceId(userId: string, deviceId: string): Promise<string[]> {
async getAllByDeviceId(ownerId: string, deviceId: string): Promise<string[]> {
const rows = await this.assetRepository.find({
where: {
userId: userId,
deviceId: deviceId,
ownerId,
deviceId,
isVisible: true,
},
select: ['deviceAssetId'],
@ -326,14 +330,14 @@ export class AssetRepository implements IAssetRepository {
/**
* Get asset by checksum on the database
* @param userId
* @param ownerId
* @param checksum
*
*/
getAssetByChecksum(userId: string, checksum: Buffer): Promise<AssetEntity> {
getAssetByChecksum(ownerId: string, checksum: Buffer): Promise<AssetEntity> {
return this.assetRepository.findOneOrFail({
where: {
userId,
ownerId,
checksum,
},
relations: ['exifInfo'],
@ -341,7 +345,7 @@ export class AssetRepository implements IAssetRepository {
}
async getExistingAssets(
userId: string,
ownerId: string,
checkDuplicateAssetDto: CheckExistingAssetsDto,
): Promise<CheckExistingAssetsResponseDto> {
const existingAssets = await this.assetRepository.find({
@ -349,17 +353,17 @@ export class AssetRepository implements IAssetRepository {
where: {
deviceAssetId: In(checkDuplicateAssetDto.deviceAssetIds),
deviceId: checkDuplicateAssetDto.deviceId,
userId,
ownerId,
},
});
return new CheckExistingAssetsResponseDto(existingAssets.map((a) => a.deviceAssetId));
}
async countByIdAndUser(assetId: string, userId: string): Promise<number> {
async countByIdAndUser(assetId: string, ownerId: string): Promise<number> {
return await this.assetRepository.count({
where: {
id: assetId,
userId,
ownerId,
},
});
}

View file

@ -1,6 +1,5 @@
import { timeUtils } from '@app/common';
import { AuthUserDto, IJobRepository, JobName } from '@app/domain';
import { AssetEntity } from '@app/infra/db/entities';
import { AssetEntity, UserEntity } from '@app/infra/db/entities';
import { StorageService } from '@app/storage';
import { IAssetRepository } from './asset-repository';
import { CreateAssetDto, UploadFile } from './dto/create-asset.dto';
@ -19,24 +18,23 @@ export class AssetCore {
livePhotoAssetId?: string,
): Promise<AssetEntity> {
let asset = await this.repository.create({
userId: authUser.id,
owner: { id: authUser.id } as UserEntity,
mimeType: file.mimeType,
checksum: file.checksum || null,
originalPath: file.originalPath,
createdAt: timeUtils.checkValidTimestamp(dto.createdAt) ? dto.createdAt : new Date().toISOString(),
modifiedAt: timeUtils.checkValidTimestamp(dto.modifiedAt) ? dto.modifiedAt : new Date().toISOString(),
updatedAt: new Date().toISOString(),
deviceAssetId: dto.deviceAssetId,
deviceId: dto.deviceId,
fileCreatedAt: dto.fileCreatedAt,
fileModifiedAt: dto.fileModifiedAt,
type: dto.assetType,
isFavorite: dto.isFavorite,
duration: dto.duration || null,
isVisible: dto.isVisible ?? true,
livePhotoVideoId: livePhotoAssetId || null,
livePhotoVideo: livePhotoAssetId != null ? ({ id: livePhotoAssetId } as AssetEntity) : null,
resizePath: null,
webpPath: null,
encodedVideoPath: null,

View file

@ -27,8 +27,8 @@ const _getCreateAssetDto = (): CreateAssetDto => {
createAssetDto.deviceAssetId = 'deviceAssetId';
createAssetDto.deviceId = 'deviceId';
createAssetDto.assetType = AssetType.OTHER;
createAssetDto.createdAt = '2022-06-19T23:41:36.910Z';
createAssetDto.modifiedAt = '2022-06-19T23:41:36.910Z';
createAssetDto.fileCreatedAt = '2022-06-19T23:41:36.910Z';
createAssetDto.fileModifiedAt = '2022-06-19T23:41:36.910Z';
createAssetDto.isFavorite = false;
createAssetDto.duration = '0:00:00.000000';
@ -39,14 +39,15 @@ const _getAsset_1 = () => {
const asset_1 = new AssetEntity();
asset_1.id = 'id_1';
asset_1.userId = 'user_id_1';
asset_1.ownerId = 'user_id_1';
asset_1.deviceAssetId = 'device_asset_id_1';
asset_1.deviceId = 'device_id_1';
asset_1.type = AssetType.VIDEO;
asset_1.originalPath = 'fake_path/asset_1.jpeg';
asset_1.resizePath = '';
asset_1.createdAt = '2022-06-19T23:41:36.910Z';
asset_1.modifiedAt = '2022-06-19T23:41:36.910Z';
asset_1.fileModifiedAt = '2022-06-19T23:41:36.910Z';
asset_1.fileCreatedAt = '2022-06-19T23:41:36.910Z';
asset_1.updatedAt = '2022-06-19T23:41:36.910Z';
asset_1.isFavorite = false;
asset_1.mimeType = 'image/jpeg';
asset_1.webpPath = '';
@ -59,14 +60,15 @@ const _getAsset_2 = () => {
const asset_2 = new AssetEntity();
asset_2.id = 'id_2';
asset_2.userId = 'user_id_1';
asset_2.ownerId = 'user_id_1';
asset_2.deviceAssetId = 'device_asset_id_2';
asset_2.deviceId = 'device_id_1';
asset_2.type = AssetType.VIDEO;
asset_2.originalPath = 'fake_path/asset_2.jpeg';
asset_2.resizePath = '';
asset_2.createdAt = '2022-06-19T23:41:36.910Z';
asset_2.modifiedAt = '2022-06-19T23:41:36.910Z';
asset_2.fileModifiedAt = '2022-06-19T23:41:36.910Z';
asset_2.fileCreatedAt = '2022-06-19T23:41:36.910Z';
asset_2.updatedAt = '2022-06-19T23:41:36.910Z';
asset_2.isFavorite = false;
asset_2.mimeType = 'image/jpeg';
asset_2.webpPath = '';
@ -292,7 +294,7 @@ describe('AssetService', () => {
const asset = {
id: 'live-photo-asset',
originalPath: file.originalPath,
userId: authStub.user1.id,
ownerId: authStub.user1.id,
type: AssetType.IMAGE,
isVisible: true,
} as AssetEntity;
@ -307,7 +309,7 @@ describe('AssetService', () => {
const livePhotoAsset = {
id: 'live-photo-motion',
originalPath: livePhotoFile.originalPath,
userId: authStub.user1.id,
ownerId: authStub.user1.id,
type: AssetType.VIDEO,
isVisible: false,
} as AssetEntity;

View file

@ -518,7 +518,7 @@ export class AssetService {
where: {
deviceAssetId: checkDuplicateAssetDto.deviceAssetId,
deviceId: checkDuplicateAssetDto.deviceId,
userId: authUser.id,
ownerId: authUser.id,
},
});

View file

@ -16,10 +16,10 @@ export class CreateAssetDto {
assetType!: AssetType;
@IsNotEmpty()
createdAt!: string;
fileCreatedAt!: string;
@IsNotEmpty()
modifiedAt!: string;
fileModifiedAt!: string;
@IsNotEmpty()
isFavorite!: boolean;

View file

@ -159,8 +159,8 @@ export class MetadataExtractionProcessor {
return exifDate.toDate();
};
const createdAt = exifToDate(exifData?.DateTimeOriginal ?? exifData?.CreateDate ?? asset.createdAt);
const modifyDate = exifToDate(exifData?.ModifyDate ?? asset.modifiedAt);
const fileCreatedAt = exifToDate(exifData?.DateTimeOriginal ?? exifData?.CreateDate ?? asset.fileCreatedAt);
const fileModifiedAt = exifToDate(exifData?.ModifyDate ?? asset.fileModifiedAt);
const fileStats = fs.statSync(asset.originalPath);
const fileSizeInBytes = fileStats.size;
@ -174,8 +174,8 @@ export class MetadataExtractionProcessor {
newExif.exifImageWidth = exifData?.ExifImageWidth || exifData?.ImageWidth || null;
newExif.exposureTime = exifData?.ExposureTime || null;
newExif.orientation = exifData?.Orientation?.toString() || null;
newExif.dateTimeOriginal = createdAt;
newExif.modifyDate = modifyDate;
newExif.dateTimeOriginal = fileCreatedAt;
newExif.modifyDate = fileModifiedAt;
newExif.lensModel = exifData?.LensModel || null;
newExif.fNumber = exifData?.FNumber || null;
newExif.focalLength = exifData?.FocalLength ? parseFloat(exifData.FocalLength) : null;
@ -186,7 +186,7 @@ export class MetadataExtractionProcessor {
await this.assetRepository.save({
id: asset.id,
createdAt: createdAt?.toISOString(),
fileCreatedAt: fileCreatedAt?.toISOString(),
});
if (newExif.livePhotoCID && !asset.livePhotoVideoId) {
@ -273,7 +273,7 @@ export class MetadataExtractionProcessor {
}),
);
let durationString = asset.duration;
let createdAt = asset.createdAt;
let fileCreatedAt = asset.fileCreatedAt;
if (data.format.duration) {
durationString = this.extractDuration(data.format.duration);
@ -282,14 +282,10 @@ export class MetadataExtractionProcessor {
const videoTags = data.format.tags;
if (videoTags) {
if (videoTags['com.apple.quicktime.creationdate']) {
createdAt = String(videoTags['com.apple.quicktime.creationdate']);
fileCreatedAt = String(videoTags['com.apple.quicktime.creationdate']);
} else if (videoTags['creation_time']) {
createdAt = String(videoTags['creation_time']);
} else {
createdAt = asset.createdAt;
fileCreatedAt = String(videoTags['creation_time']);
}
} else {
createdAt = asset.createdAt;
}
const exifData = await exiftool.read<ImmichTags>(asset.originalPath).catch((e) => {
@ -302,7 +298,7 @@ export class MetadataExtractionProcessor {
newExif.description = '';
newExif.imageName = path.parse(fileName).name || null;
newExif.fileSizeInByte = data.format.size || null;
newExif.dateTimeOriginal = createdAt ? new Date(createdAt) : null;
newExif.dateTimeOriginal = fileCreatedAt ? new Date(fileCreatedAt) : null;
newExif.modifyDate = null;
newExif.latitude = null;
newExif.longitude = null;
@ -382,8 +378,9 @@ export class MetadataExtractionProcessor {
}
await this.exifRepository.upsert(newExif, { conflictPaths: ['assetId'] });
await this.assetRepository.update({ id: asset.id }, { duration: durationString, createdAt: createdAt });
await this.assetRepository.update({ id: asset.id }, { duration: durationString, fileCreatedAt });
} catch (err) {
``;
// do nothing
console.log('Error in video metadata extraction', err);
}

View file

@ -40,7 +40,7 @@ export class ThumbnailGeneratorProcessor {
const { asset } = job.data;
const sanitizedDeviceId = sanitize(String(asset.deviceId));
const resizePath = join(basePath, asset.userId, 'thumb', sanitizedDeviceId);
const resizePath = join(basePath, asset.ownerId, 'thumb', sanitizedDeviceId);
if (!existsSync(resizePath)) {
mkdirSync(resizePath, { recursive: true });
@ -75,7 +75,7 @@ export class ThumbnailGeneratorProcessor {
await this.machineLearningQueue.add(JobName.IMAGE_TAGGING, { asset });
await this.machineLearningQueue.add(JobName.OBJECT_DETECTION, { asset });
this.wsCommunicationGateway.server.to(asset.userId).emit('on_upload_success', JSON.stringify(mapAsset(asset)));
this.wsCommunicationGateway.server.to(asset.ownerId).emit('on_upload_success', JSON.stringify(mapAsset(asset)));
}
if (asset.type == AssetType.VIDEO) {
@ -106,7 +106,7 @@ export class ThumbnailGeneratorProcessor {
await this.machineLearningQueue.add(JobName.IMAGE_TAGGING, { asset });
await this.machineLearningQueue.add(JobName.OBJECT_DETECTION, { asset });
this.wsCommunicationGateway.server.to(asset.userId).emit('on_upload_success', JSON.stringify(mapAsset(asset)));
this.wsCommunicationGateway.server.to(asset.ownerId).emit('on_upload_success', JSON.stringify(mapAsset(asset)));
}
}

View file

@ -61,7 +61,7 @@ export class UserDeletionProcessor {
await this.albumRepository.remove(albums);
await this.apiKeyRepository.delete({ userId: user.id });
await this.assetRepository.delete({ userId: user.id });
await this.assetRepository.delete({ ownerId: user.id });
await this.userRepository.remove(user);
} catch (error: any) {
this.logger.error(`Failed to remove user`);

View file

@ -22,7 +22,7 @@ export class VideoTranscodeProcessor {
async videoConversion(job: Job<IVideoConversionProcessor>) {
const { asset } = job.data;
const basePath = APP_UPLOAD_LOCATION;
const encodedVideoPath = `${basePath}/${asset.userId}/encoded-video`;
const encodedVideoPath = `${basePath}/${asset.ownerId}/encoded-video`;
if (!existsSync(encodedVideoPath)) {
mkdirSync(encodedVideoPath, { recursive: true });

View file

@ -3324,10 +3324,10 @@
"type": "string",
"nullable": true
},
"createdAt": {
"fileCreatedAt": {
"type": "string"
},
"modifiedAt": {
"fileModifiedAt": {
"type": "string"
},
"updatedAt": {
@ -3376,8 +3376,8 @@
"deviceId",
"originalPath",
"resizePath",
"createdAt",
"modifiedAt",
"fileCreatedAt",
"fileModifiedAt",
"updatedAt",
"isFavorite",
"mimeType",
@ -3817,10 +3817,10 @@
"deviceId": {
"type": "string"
},
"createdAt": {
"fileCreatedAt": {
"type": "string"
},
"modifiedAt": {
"fileModifiedAt": {
"type": "string"
},
"isFavorite": {
@ -3841,8 +3841,8 @@
"assetData",
"deviceAssetId",
"deviceId",
"createdAt",
"modifiedAt",
"fileCreatedAt",
"fileModifiedAt",
"isFavorite",
"fileExtension"
]

View file

@ -14,8 +14,8 @@ export class AssetResponseDto {
type!: AssetType;
originalPath!: string;
resizePath!: string | null;
createdAt!: string;
modifiedAt!: string;
fileCreatedAt!: string;
fileModifiedAt!: string;
updatedAt!: string;
isFavorite!: boolean;
mimeType!: string | null;
@ -32,13 +32,13 @@ export function mapAsset(entity: AssetEntity): AssetResponseDto {
return {
id: entity.id,
deviceAssetId: entity.deviceAssetId,
ownerId: entity.userId,
ownerId: entity.ownerId,
deviceId: entity.deviceId,
type: entity.type,
originalPath: entity.originalPath,
resizePath: entity.resizePath,
createdAt: entity.createdAt,
modifiedAt: entity.modifiedAt,
fileCreatedAt: entity.fileCreatedAt,
fileModifiedAt: entity.fileModifiedAt,
updatedAt: entity.updatedAt,
isFavorite: entity.isFavorite,
mimeType: entity.mimeType,
@ -56,13 +56,13 @@ export function mapAssetWithoutExif(entity: AssetEntity): AssetResponseDto {
return {
id: entity.id,
deviceAssetId: entity.deviceAssetId,
ownerId: entity.userId,
ownerId: entity.ownerId,
deviceId: entity.deviceId,
type: entity.type,
originalPath: entity.originalPath,
resizePath: entity.resizePath,
createdAt: entity.createdAt,
modifiedAt: entity.modifiedAt,
fileCreatedAt: entity.fileCreatedAt,
fileModifiedAt: entity.fileModifiedAt,
updatedAt: entity.updatedAt,
isFavorite: entity.isFavorite,
mimeType: entity.mimeType,

View file

@ -95,20 +95,23 @@ export const assetEntityStub = {
image: Object.freeze<AssetEntity>({
id: 'asset-id',
deviceAssetId: 'device-asset-id',
modifiedAt: today.toISOString(),
createdAt: today.toISOString(),
userId: 'user-id',
fileModifiedAt: today.toISOString(),
fileCreatedAt: today.toISOString(),
owner: userEntityStub.user1,
ownerId: 'user-id',
deviceId: 'device-id',
originalPath: '/original/path',
resizePath: null,
type: AssetType.IMAGE,
webpPath: null,
encodedVideoPath: null,
createdAt: today.toISOString(),
updatedAt: today.toISOString(),
mimeType: null,
isFavorite: true,
duration: null,
isVisible: true,
livePhotoVideo: null,
livePhotoVideoId: null,
tags: [],
sharedLinks: [],
@ -146,8 +149,8 @@ const assetResponse: AssetResponseDto = {
type: AssetType.VIDEO,
originalPath: 'fake_path/jpeg',
resizePath: '',
createdAt: today.toISOString(),
modifiedAt: today.toISOString(),
fileModifiedAt: today.toISOString(),
fileCreatedAt: today.toISOString(),
updatedAt: today.toISOString(),
isFavorite: false,
mimeType: 'image/jpeg',
@ -374,14 +377,16 @@ export const sharedLinkStub = {
assets: [
{
id: 'id_1',
userId: 'user_id_1',
owner: userEntityStub.user1,
ownerId: 'user_id_1',
deviceAssetId: 'device_asset_id_1',
deviceId: 'device_id_1',
type: AssetType.VIDEO,
originalPath: 'fake_path/jpeg',
resizePath: '',
fileModifiedAt: today.toISOString(),
fileCreatedAt: today.toISOString(),
createdAt: today.toISOString(),
modifiedAt: today.toISOString(),
updatedAt: today.toISOString(),
isFavorite: false,
mimeType: 'image/jpeg',
@ -396,6 +401,7 @@ export const sharedLinkStub = {
encodedVideoPath: '',
duration: null,
isVisible: true,
livePhotoVideo: null,
livePhotoVideoId: null,
exifInfo: {
livePhotoCID: null,

View file

@ -1,9 +1,12 @@
import {
Column,
CreateDateColumn,
Entity,
Index,
JoinColumn,
JoinTable,
ManyToMany,
ManyToOne,
OneToOne,
PrimaryGeneratedColumn,
Unique,
@ -13,9 +16,10 @@ import { ExifEntity } from './exif.entity';
import { SharedLinkEntity } from './shared-link.entity';
import { SmartInfoEntity } from './smart-info.entity';
import { TagEntity } from './tag.entity';
import { UserEntity } from './user.entity';
@Entity('assets')
@Unique('UQ_userid_checksum', ['userId', 'checksum'])
@Unique('UQ_userid_checksum', ['owner', 'checksum'])
export class AssetEntity {
@PrimaryGeneratedColumn('uuid')
id!: string;
@ -23,8 +27,11 @@ export class AssetEntity {
@Column()
deviceAssetId!: string;
@ManyToOne(() => UserEntity, { eager: true, onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false })
owner!: UserEntity;
@Column()
userId!: string;
ownerId!: string;
@Column()
deviceId!: string;
@ -44,15 +51,18 @@ export class AssetEntity {
@Column({ type: 'varchar', nullable: true, default: '' })
encodedVideoPath!: string | null;
@Column({ type: 'timestamptz' })
@CreateDateColumn({ type: 'timestamptz' })
createdAt!: string;
@Column({ type: 'timestamptz' })
modifiedAt!: string;
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt!: string;
@Column({ type: 'timestamptz' })
fileCreatedAt!: string;
@Column({ type: 'timestamptz' })
fileModifiedAt!: string;
@Column({ type: 'boolean', default: false })
isFavorite!: boolean;
@ -69,7 +79,11 @@ export class AssetEntity {
@Column({ type: 'boolean', default: true })
isVisible!: boolean;
@Column({ type: 'uuid', nullable: true })
@OneToOne(() => AssetEntity, { nullable: true, onUpdate: 'CASCADE', onDelete: 'SET NULL' })
@JoinColumn()
livePhotoVideo!: AssetEntity | null;
@Column({ nullable: true })
livePhotoVideoId!: string | null;
@OneToOne(() => ExifEntity, (exifEntity) => exifEntity.asset)
@ -78,12 +92,11 @@ export class AssetEntity {
@OneToOne(() => SmartInfoEntity, (smartInfoEntity) => smartInfoEntity.asset)
smartInfo?: SmartInfoEntity;
// https://github.com/typeorm/typeorm/blob/master/docs/many-to-many-relations.md
@ManyToMany(() => TagEntity, (tag) => tag.assets, { cascade: true })
@ManyToMany(() => TagEntity, (tag) => tag.assets, { cascade: true, eager: true })
@JoinTable({ name: 'tag_asset' })
tags!: TagEntity[];
@ManyToMany(() => SharedLinkEntity, (link) => link.assets, { cascade: true })
@ManyToMany(() => SharedLinkEntity, (link) => link.assets, { cascade: true, eager: true })
@JoinTable({ name: 'shared_link__asset' })
sharedLinks!: SharedLinkEntity[];
}

View file

@ -0,0 +1,30 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class FixAssetRelations1676680127415 implements MigrationInterface {
name = 'FixAssetRelations1676680127415'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "modifiedAt" TO "fileModifiedAt"`);
await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "createdAt" TO "fileCreatedAt"`);
await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "userId" TO "ownerId"`);
await queryRunner.query(`ALTER TABLE assets ALTER COLUMN "ownerId" TYPE uuid USING "ownerId"::uuid;`);
await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_16294b83fa8c0149719a1f631ef" UNIQUE ("livePhotoVideoId")`);
await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_2c5ac0d6fb58b238fd2068de67d" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`);
await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_16294b83fa8c0149719a1f631ef" FOREIGN KEY ("livePhotoVideoId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_16294b83fa8c0149719a1f631ef"`);
await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_2c5ac0d6fb58b238fd2068de67d"`);
await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_16294b83fa8c0149719a1f631ef"`);
await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "fileCreatedAt" TO "createdAt"`);
await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "fileModifiedAt" TO "modifiedAt"`);
await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "ownerId" TO "userId"`);
await queryRunner.query(`ALTER TABLE assets ALTER COLUMN "userId" TYPE varchar`);
}
}

View file

@ -0,0 +1,14 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class AssetCreatedAtField1676721296440 implements MigrationInterface {
name = 'AssetCreatedAtField1676721296440'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "assets" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "createdAt"`);
}
}

View file

@ -31,11 +31,11 @@ export class SharedLinkRepository implements ISharedLinkRepository {
order: {
createdAt: 'DESC',
assets: {
createdAt: 'ASC',
fileCreatedAt: 'ASC',
},
album: {
assets: {
createdAt: 'ASC',
fileCreatedAt: 'ASC',
},
},
},

View file

@ -50,7 +50,7 @@ export class StorageService {
const source = asset.originalPath;
const ext = path.extname(source).split('.').pop() as string;
const sanitized = sanitize(path.basename(filename, `.${ext}`));
const rootPath = path.join(APP_UPLOAD_LOCATION, asset.userId);
const rootPath = path.join(APP_UPLOAD_LOCATION, asset.ownerId);
const storagePath = this.render(this.storageTemplate, asset, sanitized, ext);
const fullPath = path.normalize(path.join(rootPath, storagePath));
let destination = `${fullPath}.${ext}`;
@ -132,7 +132,7 @@ export class StorageService {
this.render(
template,
{
createdAt: new Date().toISOString(),
fileCreatedAt: new Date().toISOString(),
originalPath: '/upload/test/IMG_123.jpg',
type: AssetType.IMAGE,
} as AssetEntity,
@ -161,7 +161,7 @@ export class StorageService {
const fileType = asset.type == AssetType.IMAGE ? 'IMG' : 'VID';
const fileTypeFull = asset.type == AssetType.IMAGE ? 'IMAGE' : 'VIDEO';
const dt = luxon.DateTime.fromISO(new Date(asset.createdAt).toISOString());
const dt = luxon.DateTime.fromISO(new Date(asset.fileCreatedAt).toISOString());
const dateTokens = [
...supportedYearTokens,

View file

@ -6,7 +6,7 @@
"packages": {
"": {
"name": "immich",
"version": "1.46.1",
"version": "1.47.3",
"license": "UNLICENSED",
"dependencies": {
"@nestjs/bull": "^0.6.2",

View file

@ -4,7 +4,7 @@
* Immich
* Immich API
*
* The version of the OpenAPI document: 1.47.2
* The version of the OpenAPI document: 1.47.3
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
@ -61,10 +61,10 @@ export interface APIKeyCreateResponseDto {
export interface APIKeyResponseDto {
/**
*
* @type {number}
* @type {string}
* @memberof APIKeyResponseDto
*/
'id': number;
'id': string;
/**
*
* @type {string}
@ -467,13 +467,13 @@ export interface AssetResponseDto {
* @type {string}
* @memberof AssetResponseDto
*/
'createdAt': string;
'fileCreatedAt': string;
/**
*
* @type {string}
* @memberof AssetResponseDto
*/
'modifiedAt': string;
'fileModifiedAt': string;
/**
*
* @type {string}
@ -2356,11 +2356,11 @@ export const APIKeyApiAxiosParamCreator = function (configuration?: Configuratio
},
/**
*
* @param {number} id
* @param {string} id
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
deleteKey: async (id: number, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
deleteKey: async (id: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'id' is not null or undefined
assertParamExists('deleteKey', 'id', id)
const localVarPath = `/api-key/{id}`
@ -2389,11 +2389,11 @@ export const APIKeyApiAxiosParamCreator = function (configuration?: Configuratio
},
/**
*
* @param {number} id
* @param {string} id
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getKey: async (id: number, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
getKey: async (id: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'id' is not null or undefined
assertParamExists('getKey', 'id', id)
const localVarPath = `/api-key/{id}`
@ -2451,12 +2451,12 @@ export const APIKeyApiAxiosParamCreator = function (configuration?: Configuratio
},
/**
*
* @param {number} id
* @param {string} id
* @param {APIKeyUpdateDto} aPIKeyUpdateDto
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
updateKey: async (id: number, aPIKeyUpdateDto: APIKeyUpdateDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
updateKey: async (id: string, aPIKeyUpdateDto: APIKeyUpdateDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'id' is not null or undefined
assertParamExists('updateKey', 'id', id)
// verify required parameter 'aPIKeyUpdateDto' is not null or undefined
@ -2510,21 +2510,21 @@ export const APIKeyApiFp = function(configuration?: Configuration) {
},
/**
*
* @param {number} id
* @param {string} id
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async deleteKey(id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
async deleteKey(id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.deleteKey(id, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
*
* @param {number} id
* @param {string} id
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async getKey(id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<APIKeyResponseDto>> {
async getKey(id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<APIKeyResponseDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getKey(id, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
@ -2539,12 +2539,12 @@ export const APIKeyApiFp = function(configuration?: Configuration) {
},
/**
*
* @param {number} id
* @param {string} id
* @param {APIKeyUpdateDto} aPIKeyUpdateDto
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async updateKey(id: number, aPIKeyUpdateDto: APIKeyUpdateDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<APIKeyResponseDto>> {
async updateKey(id: string, aPIKeyUpdateDto: APIKeyUpdateDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<APIKeyResponseDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.updateKey(id, aPIKeyUpdateDto, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
@ -2569,20 +2569,20 @@ export const APIKeyApiFactory = function (configuration?: Configuration, basePat
},
/**
*
* @param {number} id
* @param {string} id
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
deleteKey(id: number, options?: any): AxiosPromise<void> {
deleteKey(id: string, options?: any): AxiosPromise<void> {
return localVarFp.deleteKey(id, options).then((request) => request(axios, basePath));
},
/**
*
* @param {number} id
* @param {string} id
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getKey(id: number, options?: any): AxiosPromise<APIKeyResponseDto> {
getKey(id: string, options?: any): AxiosPromise<APIKeyResponseDto> {
return localVarFp.getKey(id, options).then((request) => request(axios, basePath));
},
/**
@ -2595,12 +2595,12 @@ export const APIKeyApiFactory = function (configuration?: Configuration, basePat
},
/**
*
* @param {number} id
* @param {string} id
* @param {APIKeyUpdateDto} aPIKeyUpdateDto
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
updateKey(id: number, aPIKeyUpdateDto: APIKeyUpdateDto, options?: any): AxiosPromise<APIKeyResponseDto> {
updateKey(id: string, aPIKeyUpdateDto: APIKeyUpdateDto, options?: any): AxiosPromise<APIKeyResponseDto> {
return localVarFp.updateKey(id, aPIKeyUpdateDto, options).then((request) => request(axios, basePath));
},
};
@ -2626,23 +2626,23 @@ export class APIKeyApi extends BaseAPI {
/**
*
* @param {number} id
* @param {string} id
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof APIKeyApi
*/
public deleteKey(id: number, options?: AxiosRequestConfig) {
public deleteKey(id: string, options?: AxiosRequestConfig) {
return APIKeyApiFp(this.configuration).deleteKey(id, options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @param {number} id
* @param {string} id
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof APIKeyApi
*/
public getKey(id: number, options?: AxiosRequestConfig) {
public getKey(id: string, options?: AxiosRequestConfig) {
return APIKeyApiFp(this.configuration).getKey(id, options).then((request) => request(this.axios, this.basePath));
}
@ -2658,13 +2658,13 @@ export class APIKeyApi extends BaseAPI {
/**
*
* @param {number} id
* @param {string} id
* @param {APIKeyUpdateDto} aPIKeyUpdateDto
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof APIKeyApi
*/
public updateKey(id: number, aPIKeyUpdateDto: APIKeyUpdateDto, options?: AxiosRequestConfig) {
public updateKey(id: string, aPIKeyUpdateDto: APIKeyUpdateDto, options?: AxiosRequestConfig) {
return APIKeyApiFp(this.configuration).updateKey(id, aPIKeyUpdateDto, options).then((request) => request(this.axios, this.basePath));
}
}
@ -4432,8 +4432,8 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
* @param {any} assetData
* @param {string} deviceAssetId
* @param {string} deviceId
* @param {string} createdAt
* @param {string} modifiedAt
* @param {string} fileCreatedAt
* @param {string} fileModifiedAt
* @param {boolean} isFavorite
* @param {string} fileExtension
* @param {any} [livePhotoData]
@ -4442,7 +4442,7 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
uploadFile: async (assetType: AssetTypeEnum, assetData: any, deviceAssetId: string, deviceId: string, createdAt: string, modifiedAt: string, isFavorite: boolean, fileExtension: string, livePhotoData?: any, isVisible?: boolean, duration?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
uploadFile: async (assetType: AssetTypeEnum, assetData: any, deviceAssetId: string, deviceId: string, fileCreatedAt: string, fileModifiedAt: string, isFavorite: boolean, fileExtension: string, livePhotoData?: any, isVisible?: boolean, duration?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'assetType' is not null or undefined
assertParamExists('uploadFile', 'assetType', assetType)
// verify required parameter 'assetData' is not null or undefined
@ -4451,10 +4451,10 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
assertParamExists('uploadFile', 'deviceAssetId', deviceAssetId)
// verify required parameter 'deviceId' is not null or undefined
assertParamExists('uploadFile', 'deviceId', deviceId)
// verify required parameter 'createdAt' is not null or undefined
assertParamExists('uploadFile', 'createdAt', createdAt)
// verify required parameter 'modifiedAt' is not null or undefined
assertParamExists('uploadFile', 'modifiedAt', modifiedAt)
// verify required parameter 'fileCreatedAt' is not null or undefined
assertParamExists('uploadFile', 'fileCreatedAt', fileCreatedAt)
// verify required parameter 'fileModifiedAt' is not null or undefined
assertParamExists('uploadFile', 'fileModifiedAt', fileModifiedAt)
// verify required parameter 'isFavorite' is not null or undefined
assertParamExists('uploadFile', 'isFavorite', isFavorite)
// verify required parameter 'fileExtension' is not null or undefined
@ -4497,12 +4497,12 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
localVarFormParams.append('deviceId', deviceId as any);
}
if (createdAt !== undefined) {
localVarFormParams.append('createdAt', createdAt as any);
if (fileCreatedAt !== undefined) {
localVarFormParams.append('fileCreatedAt', fileCreatedAt as any);
}
if (modifiedAt !== undefined) {
localVarFormParams.append('modifiedAt', modifiedAt as any);
if (fileModifiedAt !== undefined) {
localVarFormParams.append('fileModifiedAt', fileModifiedAt as any);
}
if (isFavorite !== undefined) {
@ -4772,8 +4772,8 @@ export const AssetApiFp = function(configuration?: Configuration) {
* @param {any} assetData
* @param {string} deviceAssetId
* @param {string} deviceId
* @param {string} createdAt
* @param {string} modifiedAt
* @param {string} fileCreatedAt
* @param {string} fileModifiedAt
* @param {boolean} isFavorite
* @param {string} fileExtension
* @param {any} [livePhotoData]
@ -4782,8 +4782,8 @@ export const AssetApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async uploadFile(assetType: AssetTypeEnum, assetData: any, deviceAssetId: string, deviceId: string, createdAt: string, modifiedAt: string, isFavorite: boolean, fileExtension: string, livePhotoData?: any, isVisible?: boolean, duration?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AssetFileUploadResponseDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.uploadFile(assetType, assetData, deviceAssetId, deviceId, createdAt, modifiedAt, isFavorite, fileExtension, livePhotoData, isVisible, duration, options);
async uploadFile(assetType: AssetTypeEnum, assetData: any, deviceAssetId: string, deviceId: string, fileCreatedAt: string, fileModifiedAt: string, isFavorite: boolean, fileExtension: string, livePhotoData?: any, isVisible?: boolean, duration?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AssetFileUploadResponseDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.uploadFile(assetType, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, isFavorite, fileExtension, livePhotoData, isVisible, duration, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
}
@ -5002,8 +5002,8 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
* @param {any} assetData
* @param {string} deviceAssetId
* @param {string} deviceId
* @param {string} createdAt
* @param {string} modifiedAt
* @param {string} fileCreatedAt
* @param {string} fileModifiedAt
* @param {boolean} isFavorite
* @param {string} fileExtension
* @param {any} [livePhotoData]
@ -5012,8 +5012,8 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
uploadFile(assetType: AssetTypeEnum, assetData: any, deviceAssetId: string, deviceId: string, createdAt: string, modifiedAt: string, isFavorite: boolean, fileExtension: string, livePhotoData?: any, isVisible?: boolean, duration?: string, options?: any): AxiosPromise<AssetFileUploadResponseDto> {
return localVarFp.uploadFile(assetType, assetData, deviceAssetId, deviceId, createdAt, modifiedAt, isFavorite, fileExtension, livePhotoData, isVisible, duration, options).then((request) => request(axios, basePath));
uploadFile(assetType: AssetTypeEnum, assetData: any, deviceAssetId: string, deviceId: string, fileCreatedAt: string, fileModifiedAt: string, isFavorite: boolean, fileExtension: string, livePhotoData?: any, isVisible?: boolean, duration?: string, options?: any): AxiosPromise<AssetFileUploadResponseDto> {
return localVarFp.uploadFile(assetType, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, isFavorite, fileExtension, livePhotoData, isVisible, duration, options).then((request) => request(axios, basePath));
},
};
};
@ -5275,8 +5275,8 @@ export class AssetApi extends BaseAPI {
* @param {any} assetData
* @param {string} deviceAssetId
* @param {string} deviceId
* @param {string} createdAt
* @param {string} modifiedAt
* @param {string} fileCreatedAt
* @param {string} fileModifiedAt
* @param {boolean} isFavorite
* @param {string} fileExtension
* @param {any} [livePhotoData]
@ -5286,8 +5286,8 @@ export class AssetApi extends BaseAPI {
* @throws {RequiredError}
* @memberof AssetApi
*/
public uploadFile(assetType: AssetTypeEnum, assetData: any, deviceAssetId: string, deviceId: string, createdAt: string, modifiedAt: string, isFavorite: boolean, fileExtension: string, livePhotoData?: any, isVisible?: boolean, duration?: string, options?: AxiosRequestConfig) {
return AssetApiFp(this.configuration).uploadFile(assetType, assetData, deviceAssetId, deviceId, createdAt, modifiedAt, isFavorite, fileExtension, livePhotoData, isVisible, duration, options).then((request) => request(this.axios, this.basePath));
public uploadFile(assetType: AssetTypeEnum, assetData: any, deviceAssetId: string, deviceId: string, fileCreatedAt: string, fileModifiedAt: string, isFavorite: boolean, fileExtension: string, livePhotoData?: any, isVisible?: boolean, duration?: string, options?: AxiosRequestConfig) {
return AssetApiFp(this.configuration).uploadFile(assetType, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, isFavorite, fileExtension, livePhotoData, isVisible, duration, options).then((request) => request(this.axios, this.basePath));
}
}

View file

@ -4,7 +4,7 @@
* Immich
* Immich API
*
* The version of the OpenAPI document: 1.47.2
* The version of the OpenAPI document: 1.47.3
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

View file

@ -4,7 +4,7 @@
* Immich
* Immich API
*
* The version of the OpenAPI document: 1.47.2
* The version of the OpenAPI document: 1.47.3
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

View file

@ -4,7 +4,7 @@
* Immich
* Immich API
*
* The version of the OpenAPI document: 1.47.2
* The version of the OpenAPI document: 1.47.3
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

View file

@ -4,7 +4,7 @@
* Immich
* Immich API
*
* The version of the OpenAPI document: 1.47.2
* The version of the OpenAPI document: 1.47.3
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

View file

@ -96,8 +96,8 @@
};
const getDateRange = () => {
const startDate = new Date(album.assets[0].createdAt);
const endDate = new Date(album.assets[album.assetCount - 1].createdAt);
const startDate = new Date(album.assets[0].fileCreatedAt);
const endDate = new Date(album.assets[album.assetCount - 1].fileCreatedAt);
const startDateString = startDate.toLocaleDateString(locale, albumDateFormat);
const endDateString = endDate.toLocaleDateString(locale, albumDateFormat);

View file

@ -31,7 +31,7 @@
let hoveredDateGroup = '';
$: assetsGroupByDate = lodash
.chain(assets)
.groupBy((a) => new Date(a.createdAt).toLocaleDateString(locale, groupDateFormat))
.groupBy((a) => new Date(a.fileCreatedAt).toLocaleDateString(locale, groupDateFormat))
.sortBy((group) => assets.indexOf(group[0]))
.value();
@ -114,7 +114,7 @@
bind:clientHeight={actualBucketHeight}
>
{#each assetsGroupByDate as assetsInDateGroup, groupIndex (assetsInDateGroup[0].id)}
{@const dateGroupTitle = new Date(assetsInDateGroup[0].createdAt).toLocaleDateString(
{@const dateGroupTitle = new Date(assetsInDateGroup[0].fileCreatedAt).toLocaleDateString(
locale,
groupDateFormat
)}

View file

@ -65,7 +65,7 @@ function createAssetInteractionStore() {
const navigateAsset = async (direction: 'next' | 'previous') => {
// Flatten and sort the asset by date if there are new assets
if (assetSortedByDate.length === 0 || savedAssetLength !== _assetGridState.assets.length) {
assetSortedByDate = sortBy(_assetGridState.assets, (a) => a.createdAt);
assetSortedByDate = sortBy(_assetGridState.assets, (a) => a.fileCreatedAt);
savedAssetLength = _assetGridState.assets.length;
}

View file

@ -69,7 +69,7 @@ async function fileUploader(
const assetType = mimeType.split('/')[0].toUpperCase();
const fileExtension = getFilenameExtension(asset.name);
const formData = new FormData();
const createdAt = new Date(asset.lastModified).toISOString();
const fileCreatedAt = new Date(asset.lastModified).toISOString();
const deviceAssetId = 'web' + '-' + asset.name + '-' + asset.lastModified;
try {
@ -83,10 +83,10 @@ async function fileUploader(
formData.append('assetType', assetType);
// Get Asset Created Date
formData.append('createdAt', createdAt);
formData.append('fileCreatedAt', fileCreatedAt);
// Get Asset Modified At
formData.append('modifiedAt', new Date(asset.lastModified).toISOString());
formData.append('fileModifiedAt', new Date(asset.lastModified).toISOString());
// Set Asset is Favorite to false
formData.append('isFavorite', 'false');