feat(server): support for read-only assets and importing existing items in the filesystem (#2715)

* Added read-only flag for assets, endpoint to trigger file import vs upload

* updated fixtures with new property

* if upload is 'read-only', ensure there is no existing asset at the designated originalPath

* added test for file import as well as detecting existing image at read-only destination location

* Added storage service test for a case where it should not move read-only assets

* upload doesn't need the read-only flag available, just importing

* default isReadOnly on import endpoint to true

* formatting fixes

* create-asset dto needs isReadOnly, so set it to false by default on create, updated api generation

* updated code to reflect changes in MR

* fixed read stream promise return type

* new index for originalPath, check for existing path on import, reglardless of user, to prevent duplicates

* refactor: import asset

* chore: open api

* chore: tests

* Added externalPath support for individual users, updated UI to allow this to be set by admin

* added missing var for externalPath in ui

* chore: open api

* fix: compilation issues

* fix: server test

* built api, fixed user-response dto to include externalPath

* reverted accidental commit

* bad commit of duplicate externalPath in user response  dto

* fixed tests to include externalPath on expected result

* fix: unit tests

* centralized supported filetypes, perform file type checking of asset and sidecar during file import process

* centralized supported filetype check method to keep regex DRY

* fixed typo

* combined migrations into one

* update api

* Removed externalPath from shared-link code, added column to admin user page whether external paths / import is enabled or not

* update mimetype

* Fixed detect correct mimetype

* revert asset-upload config

* reverted domain.constant

* refactor

* fix mime-type issue

* fix format

---------

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
Alex Phillips
2023-06-21 22:33:20 -04:00
committed by GitHub
parent 7f44d508dc
commit e171fec5aa
55 changed files with 1321 additions and 128 deletions

View File

@@ -50,6 +50,7 @@ export const authStub = {
isAdmin: true,
isPublicUser: false,
isAllowUpload: true,
externalPath: null,
}),
user1: Object.freeze<AuthUserDto>({
id: 'user-id',
@@ -60,6 +61,7 @@ export const authStub = {
isAllowDownload: true,
isShowExif: true,
accessTokenId: 'token-id',
externalPath: null,
}),
user2: Object.freeze<AuthUserDto>({
id: 'user-2',
@@ -70,6 +72,18 @@ export const authStub = {
isAllowDownload: true,
isShowExif: true,
accessTokenId: 'token-id',
externalPath: null,
}),
external1: Object.freeze<AuthUserDto>({
id: 'user-id',
email: 'immich@test.com',
isAdmin: false,
isPublicUser: false,
isAllowUpload: true,
isAllowDownload: true,
isShowExif: true,
accessTokenId: 'token-id',
externalPath: '/data/user1',
}),
adminSharedLink: Object.freeze<AuthUserDto>({
id: 'admin_id',
@@ -111,6 +125,7 @@ export const userEntityStub = {
firstName: 'admin_first_name',
lastName: 'admin_last_name',
storageLabel: 'admin',
externalPath: null,
oauthId: '',
shouldChangePassword: false,
profileImagePath: '',
@@ -126,6 +141,7 @@ export const userEntityStub = {
firstName: 'immich_first_name',
lastName: 'immich_last_name',
storageLabel: null,
externalPath: null,
oauthId: '',
shouldChangePassword: false,
profileImagePath: '',
@@ -141,6 +157,7 @@ export const userEntityStub = {
firstName: 'immich_first_name',
lastName: 'immich_last_name',
storageLabel: null,
externalPath: null,
oauthId: '',
shouldChangePassword: false,
profileImagePath: '',
@@ -156,6 +173,7 @@ export const userEntityStub = {
firstName: 'immich_first_name',
lastName: 'immich_last_name',
storageLabel: 'label-1',
externalPath: null,
oauthId: '',
shouldChangePassword: false,
profileImagePath: '',
@@ -212,6 +230,7 @@ export const assetEntityStub = {
sharedLinks: [],
faces: [],
sidecarPath: null,
isReadOnly: false,
}),
noWebpPath: Object.freeze<AssetEntity>({
id: 'asset-id',
@@ -242,6 +261,7 @@ export const assetEntityStub = {
originalFileName: 'asset-id.ext',
faces: [],
sidecarPath: null,
isReadOnly: false,
}),
noThumbhash: Object.freeze<AssetEntity>({
id: 'asset-id',
@@ -263,6 +283,7 @@ export const assetEntityStub = {
mimeType: null,
isFavorite: true,
isArchived: false,
isReadOnly: false,
duration: null,
isVisible: true,
livePhotoVideo: null,
@@ -293,6 +314,7 @@ export const assetEntityStub = {
mimeType: null,
isFavorite: true,
isArchived: false,
isReadOnly: false,
duration: null,
isVisible: true,
livePhotoVideo: null,
@@ -324,6 +346,7 @@ export const assetEntityStub = {
mimeType: null,
isFavorite: true,
isArchived: false,
isReadOnly: false,
duration: null,
isVisible: true,
livePhotoVideo: null,
@@ -375,6 +398,7 @@ export const assetEntityStub = {
mimeType: null,
isFavorite: false,
isArchived: false,
isReadOnly: false,
duration: null,
isVisible: true,
livePhotoVideo: null,
@@ -408,6 +432,7 @@ export const assetEntityStub = {
mimeType: null,
isFavorite: true,
isArchived: false,
isReadOnly: false,
duration: null,
isVisible: true,
livePhotoVideo: null,
@@ -865,6 +890,7 @@ export const sharedLinkStub = {
updatedAt: today,
isFavorite: false,
isArchived: false,
isReadOnly: false,
mimeType: 'image/jpeg',
smartInfo: {
assetId: 'id_1',

View File

@@ -6,5 +6,6 @@ export const newCryptoRepositoryMock = (): jest.Mocked<ICryptoRepository> => {
compareBcrypt: jest.fn().mockReturnValue(true),
hashBcrypt: jest.fn().mockImplementation((input) => Promise.resolve(`${input} (hashed)`)),
hashSha256: jest.fn().mockImplementation((input) => `${input} (hashed)`),
hashFile: jest.fn().mockImplementation((input) => `${input} (file-hashed)`),
};
};