Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c0de9943e0 |
Generated
+3
-3
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.45",
|
"version": "2.2.43",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.45",
|
"version": "2.2.43",
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-glob": "^3.3.2",
|
"fast-glob": "^3.3.2",
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
},
|
},
|
||||||
"../open-api/typescript-sdk": {
|
"../open-api/typescript-sdk": {
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.125.4",
|
"version": "1.125.2",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.45",
|
"version": "2.2.43",
|
||||||
"description": "Command Line Interface (CLI) for Immich",
|
"description": "Command Line Interface (CLI) for Immich",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": "./dist/index.js",
|
"exports": "./dist/index.js",
|
||||||
|
|||||||
Vendored
-8
@@ -1,12 +1,4 @@
|
|||||||
[
|
[
|
||||||
{
|
|
||||||
"label": "v1.125.4",
|
|
||||||
"url": "https://v1.125.4.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "v1.125.3",
|
|
||||||
"url": "https://v1.125.3.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v1.125.2",
|
"label": "v1.125.2",
|
||||||
"url": "https://v1.125.2.archive.immich.app"
|
"url": "https://v1.125.2.archive.immich.app"
|
||||||
|
|||||||
Generated
+4
-4
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "1.125.4",
|
"version": "1.125.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "1.125.4",
|
"version": "1.125.2",
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3.1.0",
|
"@eslint/eslintrc": "^3.1.0",
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
},
|
},
|
||||||
"../cli": {
|
"../cli": {
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.45",
|
"version": "2.2.43",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -92,7 +92,7 @@
|
|||||||
},
|
},
|
||||||
"../open-api/typescript-sdk": {
|
"../open-api/typescript-sdk": {
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.125.4",
|
"version": "1.125.2",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "1.125.4",
|
"version": "1.125.2",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -22,92 +22,79 @@ const user1NotShared = 'user1NotShared';
|
|||||||
const user2SharedUser = 'user2SharedUser';
|
const user2SharedUser = 'user2SharedUser';
|
||||||
const user2SharedLink = 'user2SharedLink';
|
const user2SharedLink = 'user2SharedLink';
|
||||||
const user2NotShared = 'user2NotShared';
|
const user2NotShared = 'user2NotShared';
|
||||||
const user4DeletedAsset = 'user4DeletedAsset';
|
|
||||||
const user4Empty = 'user4Empty';
|
|
||||||
|
|
||||||
describe('/albums', () => {
|
describe('/albums', () => {
|
||||||
let admin: LoginResponseDto;
|
let admin: LoginResponseDto;
|
||||||
let user1: LoginResponseDto;
|
let user1: LoginResponseDto;
|
||||||
let user1Asset1: AssetMediaResponseDto;
|
let user1Asset1: AssetMediaResponseDto;
|
||||||
let user1Asset2: AssetMediaResponseDto;
|
let user1Asset2: AssetMediaResponseDto;
|
||||||
let user4Asset1: AssetMediaResponseDto;
|
|
||||||
let user1Albums: AlbumResponseDto[];
|
let user1Albums: AlbumResponseDto[];
|
||||||
let user2: LoginResponseDto;
|
let user2: LoginResponseDto;
|
||||||
let user2Albums: AlbumResponseDto[];
|
let user2Albums: AlbumResponseDto[];
|
||||||
let deletedAssetAlbum: AlbumResponseDto;
|
|
||||||
let user3: LoginResponseDto; // deleted
|
let user3: LoginResponseDto; // deleted
|
||||||
let user4: LoginResponseDto;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await utils.resetDatabase();
|
await utils.resetDatabase();
|
||||||
|
|
||||||
admin = await utils.adminSetup();
|
admin = await utils.adminSetup();
|
||||||
|
|
||||||
[user1, user2, user3, user4] = await Promise.all([
|
[user1, user2, user3] = await Promise.all([
|
||||||
utils.userSetup(admin.accessToken, createUserDto.user1),
|
utils.userSetup(admin.accessToken, createUserDto.user1),
|
||||||
utils.userSetup(admin.accessToken, createUserDto.user2),
|
utils.userSetup(admin.accessToken, createUserDto.user2),
|
||||||
utils.userSetup(admin.accessToken, createUserDto.user3),
|
utils.userSetup(admin.accessToken, createUserDto.user3),
|
||||||
utils.userSetup(admin.accessToken, createUserDto.user4),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
[user1Asset1, user1Asset2, user4Asset1] = await Promise.all([
|
[user1Asset1, user1Asset2] = await Promise.all([
|
||||||
utils.createAsset(user1.accessToken, { isFavorite: true }),
|
utils.createAsset(user1.accessToken, { isFavorite: true }),
|
||||||
utils.createAsset(user1.accessToken),
|
utils.createAsset(user1.accessToken),
|
||||||
utils.createAsset(user1.accessToken),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
[user1Albums, user2Albums, deletedAssetAlbum] = await Promise.all([
|
user1Albums = await Promise.all([
|
||||||
Promise.all([
|
utils.createAlbum(user1.accessToken, {
|
||||||
utils.createAlbum(user1.accessToken, {
|
albumName: user1SharedEditorUser,
|
||||||
albumName: user1SharedEditorUser,
|
albumUsers: [{ userId: user2.userId, role: AlbumUserRole.Editor }],
|
||||||
albumUsers: [
|
assetIds: [user1Asset1.id],
|
||||||
{ userId: admin.userId, role: AlbumUserRole.Editor },
|
}),
|
||||||
{ userId: user2.userId, role: AlbumUserRole.Editor },
|
utils.createAlbum(user1.accessToken, {
|
||||||
],
|
albumName: user1SharedLink,
|
||||||
assetIds: [user1Asset1.id],
|
assetIds: [user1Asset1.id],
|
||||||
}),
|
}),
|
||||||
utils.createAlbum(user1.accessToken, {
|
utils.createAlbum(user1.accessToken, {
|
||||||
albumName: user1SharedLink,
|
albumName: user1NotShared,
|
||||||
assetIds: [user1Asset1.id],
|
assetIds: [user1Asset1.id, user1Asset2.id],
|
||||||
}),
|
}),
|
||||||
utils.createAlbum(user1.accessToken, {
|
utils.createAlbum(user1.accessToken, {
|
||||||
albumName: user1NotShared,
|
albumName: user1SharedViewerUser,
|
||||||
assetIds: [user1Asset1.id, user1Asset2.id],
|
albumUsers: [{ userId: user2.userId, role: AlbumUserRole.Viewer }],
|
||||||
}),
|
assetIds: [user1Asset1.id],
|
||||||
utils.createAlbum(user1.accessToken, {
|
|
||||||
albumName: user1SharedViewerUser,
|
|
||||||
albumUsers: [{ userId: user2.userId, role: AlbumUserRole.Viewer }],
|
|
||||||
assetIds: [user1Asset1.id],
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
Promise.all([
|
|
||||||
utils.createAlbum(user2.accessToken, {
|
|
||||||
albumName: user2SharedUser,
|
|
||||||
albumUsers: [
|
|
||||||
{ userId: user1.userId, role: AlbumUserRole.Editor },
|
|
||||||
{ userId: user3.userId, role: AlbumUserRole.Editor },
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
utils.createAlbum(user2.accessToken, { albumName: user2SharedLink }),
|
|
||||||
utils.createAlbum(user2.accessToken, { albumName: user2NotShared }),
|
|
||||||
]),
|
|
||||||
utils.createAlbum(user4.accessToken, { albumName: user4DeletedAsset }),
|
|
||||||
utils.createAlbum(user4.accessToken, { albumName: user4Empty }),
|
|
||||||
utils.createAlbum(user3.accessToken, {
|
|
||||||
albumName: 'Deleted',
|
|
||||||
albumUsers: [{ userId: user1.userId, role: AlbumUserRole.Editor }],
|
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
user2Albums = await Promise.all([
|
||||||
|
utils.createAlbum(user2.accessToken, {
|
||||||
|
albumName: user2SharedUser,
|
||||||
|
albumUsers: [
|
||||||
|
{ userId: user1.userId, role: AlbumUserRole.Editor },
|
||||||
|
{ userId: user3.userId, role: AlbumUserRole.Editor },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
utils.createAlbum(user2.accessToken, { albumName: user2SharedLink }),
|
||||||
|
utils.createAlbum(user2.accessToken, { albumName: user2NotShared }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
await utils.createAlbum(user3.accessToken, {
|
||||||
|
albumName: 'Deleted',
|
||||||
|
albumUsers: [{ userId: user1.userId, role: AlbumUserRole.Editor }],
|
||||||
|
});
|
||||||
|
|
||||||
|
await addAssetsToAlbum(
|
||||||
|
{ id: user2Albums[0].id, bulkIdsDto: { ids: [user1Asset1.id, user1Asset2.id] } },
|
||||||
|
{ headers: asBearerAuth(user1.accessToken) },
|
||||||
|
);
|
||||||
|
|
||||||
|
user2Albums[0] = await getAlbumInfo({ id: user2Albums[0].id }, { headers: asBearerAuth(user2.accessToken) });
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
addAssetsToAlbum(
|
|
||||||
{ id: user2Albums[0].id, bulkIdsDto: { ids: [user1Asset1.id, user1Asset2.id] } },
|
|
||||||
{ headers: asBearerAuth(user1.accessToken) },
|
|
||||||
),
|
|
||||||
addAssetsToAlbum(
|
|
||||||
{ id: deletedAssetAlbum.id, bulkIdsDto: { ids: [user4Asset1.id] } },
|
|
||||||
{ headers: asBearerAuth(user4.accessToken) },
|
|
||||||
),
|
|
||||||
// add shared link to user1SharedLink album
|
// add shared link to user1SharedLink album
|
||||||
utils.createSharedLink(user1.accessToken, {
|
utils.createSharedLink(user1.accessToken, {
|
||||||
type: SharedLinkType.Album,
|
type: SharedLinkType.Album,
|
||||||
@@ -120,11 +107,7 @@ describe('/albums', () => {
|
|||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
[user2Albums[0]] = await Promise.all([
|
await deleteUserAdmin({ id: user3.userId, userAdminDeleteDto: {} }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
getAlbumInfo({ id: user2Albums[0].id }, { headers: asBearerAuth(user2.accessToken) }),
|
|
||||||
deleteUserAdmin({ id: user3.userId, userAdminDeleteDto: {} }, { headers: asBearerAuth(admin.accessToken) }),
|
|
||||||
utils.deleteAssets(user1.accessToken, [user4Asset1.id]),
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /albums', () => {
|
describe('GET /albums', () => {
|
||||||
@@ -301,25 +284,6 @@ describe('/albums', () => {
|
|||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toHaveLength(5);
|
expect(body).toHaveLength(5);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return empty albums and albums where all assets are deleted', async () => {
|
|
||||||
const { status, body } = await request(app).get('/albums').set('Authorization', `Bearer ${user4.accessToken}`);
|
|
||||||
expect(status).toBe(200);
|
|
||||||
expect(body).toEqual(
|
|
||||||
expect.arrayContaining([
|
|
||||||
expect.objectContaining({
|
|
||||||
ownerId: user4.userId,
|
|
||||||
albumName: user4DeletedAsset,
|
|
||||||
shared: false,
|
|
||||||
}),
|
|
||||||
expect.objectContaining({
|
|
||||||
ownerId: user4.userId,
|
|
||||||
albumName: user4Empty,
|
|
||||||
shared: false,
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /albums/:id', () => {
|
describe('GET /albums/:id', () => {
|
||||||
@@ -398,26 +362,6 @@ describe('/albums', () => {
|
|||||||
shared: true,
|
shared: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not count trashed assets', async () => {
|
|
||||||
await utils.deleteAssets(user1.accessToken, [user1Asset2.id]);
|
|
||||||
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.get(`/albums/${user2Albums[0].id}?withoutAssets=true`)
|
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
|
||||||
|
|
||||||
expect(status).toBe(200);
|
|
||||||
expect(body).toEqual({
|
|
||||||
...user2Albums[0],
|
|
||||||
assets: [],
|
|
||||||
assetCount: 1,
|
|
||||||
lastModifiedAssetTimestamp: expect.any(String),
|
|
||||||
endDate: expect.any(String),
|
|
||||||
startDate: expect.any(String),
|
|
||||||
albumUsers: expect.any(Array),
|
|
||||||
shared: true,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /albums/statistics', () => {
|
describe('GET /albums/statistics', () => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "machine-learning"
|
name = "machine-learning"
|
||||||
version = "1.125.4"
|
version = "1.125.2"
|
||||||
description = ""
|
description = ""
|
||||||
authors = ["Hau Tran <alex.tran1502@gmail.com>"]
|
authors = ["Hau Tran <alex.tran1502@gmail.com>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ platform :android do
|
|||||||
task: 'bundle',
|
task: 'bundle',
|
||||||
build_type: 'Release',
|
build_type: 'Release',
|
||||||
properties: {
|
properties: {
|
||||||
"android.injected.version.code" => 180,
|
"android.injected.version.code" => 178,
|
||||||
"android.injected.version.name" => "1.125.4",
|
"android.injected.version.name" => "1.125.2",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
||||||
|
|||||||
@@ -541,7 +541,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 190;
|
CURRENT_PROJECT_VERSION = 189;
|
||||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
@@ -685,7 +685,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 190;
|
CURRENT_PROJECT_VERSION = 189;
|
||||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
@@ -715,7 +715,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 190;
|
CURRENT_PROJECT_VERSION = 189;
|
||||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
@@ -748,7 +748,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 190;
|
CURRENT_PROJECT_VERSION = 189;
|
||||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
@@ -791,7 +791,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 190;
|
CURRENT_PROJECT_VERSION = 189;
|
||||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
@@ -831,7 +831,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 190;
|
CURRENT_PROJECT_VERSION = 189;
|
||||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
|||||||
@@ -78,7 +78,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.125.2</string>
|
<string>1.125.1</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>190</string>
|
<string>189</string>
|
||||||
<key>FLTEnableImpeller</key>
|
<key>FLTEnableImpeller</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ platform :ios do
|
|||||||
desc "iOS Release"
|
desc "iOS Release"
|
||||||
lane :release do
|
lane :release do
|
||||||
increment_version_number(
|
increment_version_number(
|
||||||
version_number: "1.125.4"
|
version_number: "1.125.2"
|
||||||
)
|
)
|
||||||
increment_build_number(
|
increment_build_number(
|
||||||
build_number: latest_testflight_build_number + 1,
|
build_number: latest_testflight_build_number + 1,
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ class UserCircleAvatar extends ConsumerWidget {
|
|||||||
final profileImageUrl =
|
final profileImageUrl =
|
||||||
'${Store.get(StoreKey.serverEndpoint)}/users/${user.id}/profile-image?d=${Random().nextInt(1024)}';
|
'${Store.get(StoreKey.serverEndpoint)}/users/${user.id}/profile-image?d=${Random().nextInt(1024)}';
|
||||||
|
|
||||||
final textIcon = DefaultTextStyle(
|
final textIcon = Text(
|
||||||
|
user.name[0].toUpperCase(),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
@@ -36,7 +37,6 @@ class UserCircleAvatar extends ConsumerWidget {
|
|||||||
? Colors.black
|
? Colors.black
|
||||||
: Colors.white,
|
: Colors.white,
|
||||||
),
|
),
|
||||||
child: Text(user.name[0].toUpperCase()),
|
|
||||||
);
|
);
|
||||||
return CircleAvatar(
|
return CircleAvatar(
|
||||||
backgroundColor: user.avatarColor.toColor(),
|
backgroundColor: user.avatarColor.toColor(),
|
||||||
|
|||||||
Generated
+1
-1
@@ -3,7 +3,7 @@ Immich API
|
|||||||
|
|
||||||
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
|
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
|
||||||
|
|
||||||
- API version: 1.125.4
|
- API version: 1.125.2
|
||||||
- Generator version: 7.8.0
|
- Generator version: 7.8.0
|
||||||
- Build package: org.openapitools.codegen.languages.DartClientCodegen
|
- Build package: org.openapitools.codegen.languages.DartClientCodegen
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ name: immich_mobile
|
|||||||
description: Immich - selfhosted backup media file on mobile phone
|
description: Immich - selfhosted backup media file on mobile phone
|
||||||
|
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 1.125.4+180
|
version: 1.125.2+178
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.3.0 <4.0.0'
|
sdk: '>=3.3.0 <4.0.0'
|
||||||
|
|||||||
@@ -7454,7 +7454,7 @@
|
|||||||
"info": {
|
"info": {
|
||||||
"title": "Immich",
|
"title": "Immich",
|
||||||
"description": "Immich API",
|
"description": "Immich API",
|
||||||
"version": "1.125.4",
|
"version": "1.125.2",
|
||||||
"contact": {}
|
"contact": {}
|
||||||
},
|
},
|
||||||
"tags": [],
|
"tags": [],
|
||||||
|
|||||||
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.125.4",
|
"version": "1.125.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.125.4",
|
"version": "1.125.2",
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oazapfts/runtime": "^1.0.2"
|
"@oazapfts/runtime": "^1.0.2"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.125.4",
|
"version": "1.125.2",
|
||||||
"description": "Auto-generated TypeScript SDK for the Immich API",
|
"description": "Auto-generated TypeScript SDK for the Immich API",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./build/index.js",
|
"main": "./build/index.js",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Immich
|
* Immich
|
||||||
* 1.125.4
|
* 1.125.2
|
||||||
* DO NOT MODIFY - This file has been generated using oazapfts.
|
* DO NOT MODIFY - This file has been generated using oazapfts.
|
||||||
* See https://www.npmjs.com/package/oazapfts
|
* See https://www.npmjs.com/package/oazapfts
|
||||||
*/
|
*/
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "immich",
|
"name": "immich",
|
||||||
"version": "1.125.4",
|
"version": "1.125.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "immich",
|
"name": "immich",
|
||||||
"version": "1.125.4",
|
"version": "1.125.2",
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs/bullmq": "^11.0.0",
|
"@nestjs/bullmq": "^11.0.0",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich",
|
"name": "immich",
|
||||||
"version": "1.125.4",
|
"version": "1.125.2",
|
||||||
"description": "",
|
"description": "",
|
||||||
"author": "",
|
"author": "",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ export const IAlbumRepository = 'IAlbumRepository';
|
|||||||
export interface AlbumAssetCount {
|
export interface AlbumAssetCount {
|
||||||
albumId: string;
|
albumId: string;
|
||||||
assetCount: number;
|
assetCount: number;
|
||||||
startDate: Date | null;
|
startDate: Date | undefined;
|
||||||
endDate: Date | null;
|
endDate: Date | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AlbumInfoOptions {
|
export interface AlbumInfoOptions {
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ select
|
|||||||
(
|
(
|
||||||
select
|
select
|
||||||
"assets".*,
|
"assets".*,
|
||||||
"exif" as "exifInfo"
|
to_json("exif") as "exifInfo"
|
||||||
from
|
from
|
||||||
"assets"
|
"assets"
|
||||||
inner join "exif" on "assets"."id" = "exif"."assetId"
|
inner join "exif" on "assets"."id" = "exif"."assetId"
|
||||||
@@ -180,20 +180,19 @@ select
|
|||||||
) as "albumUsers"
|
) as "albumUsers"
|
||||||
from
|
from
|
||||||
"albums"
|
"albums"
|
||||||
inner join "albums_assets_assets" as "album_assets" on "album_assets"."albumsId" = "albums"."id"
|
left join "albums_assets_assets" as "album_assets" on "album_assets"."albumsId" = "albums"."id"
|
||||||
|
left join "albums_shared_users_users" as "album_users" on "album_users"."albumsId" = "albums"."id"
|
||||||
where
|
where
|
||||||
(
|
(
|
||||||
"albums"."ownerId" = $1
|
(
|
||||||
or exists (
|
"albums"."ownerId" = $1
|
||||||
select
|
and "album_assets"."assetsId" = $2
|
||||||
from
|
)
|
||||||
"albums_shared_users_users" as "album_users"
|
or (
|
||||||
where
|
"album_users"."usersId" = $3
|
||||||
"album_users"."albumsId" = "albums"."id"
|
and "album_assets"."assetsId" = $4
|
||||||
and "album_users"."usersId" = $2
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
and "album_assets"."assetsId" = $3
|
|
||||||
and "albums"."deletedAt" is null
|
and "albums"."deletedAt" is null
|
||||||
order by
|
order by
|
||||||
"albums"."createdAt" desc,
|
"albums"."createdAt" desc,
|
||||||
@@ -201,17 +200,16 @@ order by
|
|||||||
|
|
||||||
-- AlbumRepository.getMetadataForIds
|
-- AlbumRepository.getMetadataForIds
|
||||||
select
|
select
|
||||||
"albums"."id" as "albumId",
|
"albums"."id",
|
||||||
min("assets"."fileCreatedAt") as "startDate",
|
min("assets"."fileCreatedAt") as "startDate",
|
||||||
max("assets"."fileCreatedAt") as "endDate",
|
max("assets"."fileCreatedAt") as "endDate",
|
||||||
count("assets"."id")::int as "assetCount"
|
count("assets"."id") as "assetCount"
|
||||||
from
|
from
|
||||||
"albums"
|
"albums"
|
||||||
inner join "albums_assets_assets" as "album_assets" on "album_assets"."albumsId" = "albums"."id"
|
left join "albums_assets_assets" as "album_assets" on "album_assets"."albumsId" = "albums"."id"
|
||||||
inner join "assets" on "assets"."id" = "album_assets"."assetsId"
|
left join "assets" on "assets"."id" = "album_assets"."assetsId"
|
||||||
where
|
where
|
||||||
"albums"."id" in ($1)
|
"albums"."id" in ($1)
|
||||||
and "assets"."deletedAt" is null
|
|
||||||
group by
|
group by
|
||||||
"albums"."id"
|
"albums"."id"
|
||||||
|
|
||||||
@@ -307,8 +305,8 @@ order by
|
|||||||
"albums"."createdAt" desc
|
"albums"."createdAt" desc
|
||||||
|
|
||||||
-- AlbumRepository.getShared
|
-- AlbumRepository.getShared
|
||||||
select
|
select distinct
|
||||||
"albums".*,
|
on ("albums"."createdAt") "albums".*,
|
||||||
(
|
(
|
||||||
select
|
select
|
||||||
coalesce(json_agg(agg), '[]')
|
coalesce(json_agg(agg), '[]')
|
||||||
@@ -391,26 +389,15 @@ select
|
|||||||
) as "sharedLinks"
|
) as "sharedLinks"
|
||||||
from
|
from
|
||||||
"albums"
|
"albums"
|
||||||
|
left join "albums_shared_users_users" as "shared_albums" on "shared_albums"."albumsId" = "albums"."id"
|
||||||
|
left join "shared_links" on "shared_links"."albumId" = "albums"."id"
|
||||||
where
|
where
|
||||||
(
|
(
|
||||||
exists (
|
"shared_albums"."usersId" = $1
|
||||||
select
|
or "shared_links"."userId" = $2
|
||||||
from
|
or (
|
||||||
"albums_shared_users_users" as "album_users"
|
"albums"."ownerId" = $3
|
||||||
where
|
and "shared_albums"."usersId" is not null
|
||||||
"album_users"."albumsId" = "albums"."id"
|
|
||||||
and (
|
|
||||||
"albums"."ownerId" = $1
|
|
||||||
or "album_users"."usersId" = $2
|
|
||||||
)
|
|
||||||
)
|
|
||||||
or exists (
|
|
||||||
select
|
|
||||||
from
|
|
||||||
"shared_links"
|
|
||||||
where
|
|
||||||
"shared_links"."albumId" = "albums"."id"
|
|
||||||
and "shared_links"."userId" = $3
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
and "albums"."deletedAt" is null
|
and "albums"."deletedAt" is null
|
||||||
@@ -418,8 +405,48 @@ order by
|
|||||||
"albums"."createdAt" desc
|
"albums"."createdAt" desc
|
||||||
|
|
||||||
-- AlbumRepository.getNotShared
|
-- AlbumRepository.getNotShared
|
||||||
select
|
select distinct
|
||||||
"albums".*,
|
on ("albums"."createdAt") "albums".*,
|
||||||
|
(
|
||||||
|
select
|
||||||
|
coalesce(json_agg(agg), '[]')
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select
|
||||||
|
"album_users".*,
|
||||||
|
(
|
||||||
|
select
|
||||||
|
to_json(obj)
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select
|
||||||
|
"id",
|
||||||
|
"email",
|
||||||
|
"createdAt",
|
||||||
|
"profileImagePath",
|
||||||
|
"isAdmin",
|
||||||
|
"shouldChangePassword",
|
||||||
|
"deletedAt",
|
||||||
|
"oauthId",
|
||||||
|
"updatedAt",
|
||||||
|
"storageLabel",
|
||||||
|
"name",
|
||||||
|
"quotaSizeInBytes",
|
||||||
|
"quotaUsageInBytes",
|
||||||
|
"status",
|
||||||
|
"profileChangedAt"
|
||||||
|
from
|
||||||
|
"users"
|
||||||
|
where
|
||||||
|
"users"."id" = "album_users"."usersId"
|
||||||
|
) as obj
|
||||||
|
) as "user"
|
||||||
|
from
|
||||||
|
"albums_shared_users_users" as "album_users"
|
||||||
|
where
|
||||||
|
"album_users"."albumsId" = "albums"."id"
|
||||||
|
) as agg
|
||||||
|
) as "albumUsers",
|
||||||
(
|
(
|
||||||
select
|
select
|
||||||
to_json(obj)
|
to_json(obj)
|
||||||
@@ -446,26 +473,29 @@ select
|
|||||||
where
|
where
|
||||||
"users"."id" = "albums"."ownerId"
|
"users"."id" = "albums"."ownerId"
|
||||||
) as obj
|
) as obj
|
||||||
) as "owner"
|
) as "owner",
|
||||||
|
(
|
||||||
|
select
|
||||||
|
coalesce(json_agg(agg), '[]')
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select
|
||||||
|
*
|
||||||
|
from
|
||||||
|
"shared_links"
|
||||||
|
where
|
||||||
|
"shared_links"."albumId" = "albums"."id"
|
||||||
|
) as agg
|
||||||
|
) as "sharedLinks"
|
||||||
from
|
from
|
||||||
"albums"
|
"albums"
|
||||||
|
left join "albums_shared_users_users" as "shared_albums" on "shared_albums"."albumsId" = "albums"."id"
|
||||||
|
left join "shared_links" on "shared_links"."albumId" = "albums"."id"
|
||||||
where
|
where
|
||||||
"albums"."ownerId" = $1
|
"albums"."ownerId" = $1
|
||||||
|
and "shared_albums"."usersId" is null
|
||||||
|
and "shared_links"."userId" is null
|
||||||
and "albums"."deletedAt" is null
|
and "albums"."deletedAt" is null
|
||||||
and not exists (
|
|
||||||
select
|
|
||||||
from
|
|
||||||
"albums_shared_users_users" as "album_users"
|
|
||||||
where
|
|
||||||
"album_users"."albumsId" = "albums"."id"
|
|
||||||
)
|
|
||||||
and not exists (
|
|
||||||
select
|
|
||||||
from
|
|
||||||
"shared_links"
|
|
||||||
where
|
|
||||||
"shared_links"."albumId" = "albums"."id"
|
|
||||||
)
|
|
||||||
order by
|
order by
|
||||||
"albums"."createdAt" desc
|
"albums"."createdAt" desc
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ offset
|
|||||||
and "assets"."deletedAt" is null
|
and "assets"."deletedAt" is null
|
||||||
and "assets"."id" < $6
|
and "assets"."id" < $6
|
||||||
order by
|
order by
|
||||||
random()
|
"assets"."id"
|
||||||
limit
|
limit
|
||||||
$7
|
$7
|
||||||
)
|
)
|
||||||
@@ -56,7 +56,7 @@ union all
|
|||||||
and "assets"."deletedAt" is null
|
and "assets"."deletedAt" is null
|
||||||
and "assets"."id" > $13
|
and "assets"."id" > $13
|
||||||
order by
|
order by
|
||||||
random()
|
"assets"."id"
|
||||||
limit
|
limit
|
||||||
$14
|
$14
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ const withAssets = (eb: ExpressionBuilder<DB, 'albums'>) => {
|
|||||||
.selectFrom('assets')
|
.selectFrom('assets')
|
||||||
.selectAll('assets')
|
.selectAll('assets')
|
||||||
.innerJoin('exif', 'assets.id', 'exif.assetId')
|
.innerJoin('exif', 'assets.id', 'exif.assetId')
|
||||||
.select((eb) => eb.table('exif').as('exifInfo'))
|
.select((eb) => eb.fn.toJson('exif').as('exifInfo'))
|
||||||
.innerJoin('albums_assets_assets', 'albums_assets_assets.assetsId', 'assets.id')
|
.innerJoin('albums_assets_assets', 'albums_assets_assets.assetsId', 'assets.id')
|
||||||
.whereRef('albums_assets_assets.albumsId', '=', 'albums.id')
|
.whereRef('albums_assets_assets.albumsId', '=', 'albums.id')
|
||||||
.where('assets.deletedAt', 'is', null)
|
.where('assets.deletedAt', 'is', null)
|
||||||
@@ -93,19 +93,14 @@ export class AlbumRepository implements IAlbumRepository {
|
|||||||
return this.db
|
return this.db
|
||||||
.selectFrom('albums')
|
.selectFrom('albums')
|
||||||
.selectAll('albums')
|
.selectAll('albums')
|
||||||
.innerJoin('albums_assets_assets as album_assets', 'album_assets.albumsId', 'albums.id')
|
.leftJoin('albums_assets_assets as album_assets', 'album_assets.albumsId', 'albums.id')
|
||||||
|
.leftJoin('albums_shared_users_users as album_users', 'album_users.albumsId', 'albums.id')
|
||||||
.where((eb) =>
|
.where((eb) =>
|
||||||
eb.or([
|
eb.or([
|
||||||
eb('albums.ownerId', '=', ownerId),
|
eb.and([eb('albums.ownerId', '=', ownerId), eb('album_assets.assetsId', '=', assetId)]),
|
||||||
eb.exists(
|
eb.and([eb('album_users.usersId', '=', ownerId), eb('album_assets.assetsId', '=', assetId)]),
|
||||||
eb
|
|
||||||
.selectFrom('albums_shared_users_users as album_users')
|
|
||||||
.whereRef('album_users.albumsId', '=', 'albums.id')
|
|
||||||
.where('album_users.usersId', '=', ownerId),
|
|
||||||
),
|
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
.where('album_assets.assetsId', '=', assetId)
|
|
||||||
.where('albums.deletedAt', 'is', null)
|
.where('albums.deletedAt', 'is', null)
|
||||||
.orderBy('albums.createdAt', 'desc')
|
.orderBy('albums.createdAt', 'desc')
|
||||||
.select(withOwner)
|
.select(withOwner)
|
||||||
@@ -122,18 +117,25 @@ export class AlbumRepository implements IAlbumRepository {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.db
|
const metadatas = await this.db
|
||||||
.selectFrom('albums')
|
.selectFrom('albums')
|
||||||
.innerJoin('albums_assets_assets as album_assets', 'album_assets.albumsId', 'albums.id')
|
.leftJoin('albums_assets_assets as album_assets', 'album_assets.albumsId', 'albums.id')
|
||||||
.innerJoin('assets', 'assets.id', 'album_assets.assetsId')
|
.leftJoin('assets', 'assets.id', 'album_assets.assetsId')
|
||||||
.select('albums.id as albumId')
|
.select('albums.id')
|
||||||
.select((eb) => eb.fn.min('assets.fileCreatedAt').as('startDate'))
|
.select((eb) => eb.fn.min('assets.fileCreatedAt').as('startDate'))
|
||||||
.select((eb) => eb.fn.max('assets.fileCreatedAt').as('endDate'))
|
.select((eb) => eb.fn.max('assets.fileCreatedAt').as('endDate'))
|
||||||
.select((eb) => sql<number>`${eb.fn.count('assets.id')}::int`.as('assetCount'))
|
.select((eb) => eb.fn.count('assets.id').as('assetCount'))
|
||||||
.where('albums.id', 'in', ids)
|
.where('albums.id', 'in', ids)
|
||||||
.where('assets.deletedAt', 'is', null)
|
.where('assets.deletedAt', 'is', null)
|
||||||
.groupBy('albums.id')
|
.groupBy('albums.id')
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
|
return metadatas.map((metadatas) => ({
|
||||||
|
albumId: metadatas.id,
|
||||||
|
assetCount: Number(metadatas.assetCount),
|
||||||
|
startDate: metadatas.startDate ? new Date(metadatas.startDate) : undefined,
|
||||||
|
endDate: metadatas.endDate ? new Date(metadatas.endDate) : undefined,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
@@ -158,20 +160,14 @@ export class AlbumRepository implements IAlbumRepository {
|
|||||||
return this.db
|
return this.db
|
||||||
.selectFrom('albums')
|
.selectFrom('albums')
|
||||||
.selectAll('albums')
|
.selectAll('albums')
|
||||||
|
.distinctOn('albums.createdAt')
|
||||||
|
.leftJoin('albums_shared_users_users as shared_albums', 'shared_albums.albumsId', 'albums.id')
|
||||||
|
.leftJoin('shared_links', 'shared_links.albumId', 'albums.id')
|
||||||
.where((eb) =>
|
.where((eb) =>
|
||||||
eb.or([
|
eb.or([
|
||||||
eb.exists(
|
eb('shared_albums.usersId', '=', ownerId),
|
||||||
eb
|
eb('shared_links.userId', '=', ownerId),
|
||||||
.selectFrom('albums_shared_users_users as album_users')
|
eb.and([eb('albums.ownerId', '=', ownerId), eb('shared_albums.usersId', 'is not', null)]),
|
||||||
.whereRef('album_users.albumsId', '=', 'albums.id')
|
|
||||||
.where((eb) => eb.or([eb('albums.ownerId', '=', ownerId), eb('album_users.usersId', '=', ownerId)])),
|
|
||||||
),
|
|
||||||
eb.exists(
|
|
||||||
eb
|
|
||||||
.selectFrom('shared_links')
|
|
||||||
.whereRef('shared_links.albumId', '=', 'albums.id')
|
|
||||||
.where('shared_links.userId', '=', ownerId),
|
|
||||||
),
|
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
.where('albums.deletedAt', 'is', null)
|
.where('albums.deletedAt', 'is', null)
|
||||||
@@ -190,21 +186,16 @@ export class AlbumRepository implements IAlbumRepository {
|
|||||||
return this.db
|
return this.db
|
||||||
.selectFrom('albums')
|
.selectFrom('albums')
|
||||||
.selectAll('albums')
|
.selectAll('albums')
|
||||||
|
.distinctOn('albums.createdAt')
|
||||||
|
.leftJoin('albums_shared_users_users as shared_albums', 'shared_albums.albumsId', 'albums.id')
|
||||||
|
.leftJoin('shared_links', 'shared_links.albumId', 'albums.id')
|
||||||
.where('albums.ownerId', '=', ownerId)
|
.where('albums.ownerId', '=', ownerId)
|
||||||
|
.where('shared_albums.usersId', 'is', null)
|
||||||
|
.where('shared_links.userId', 'is', null)
|
||||||
.where('albums.deletedAt', 'is', null)
|
.where('albums.deletedAt', 'is', null)
|
||||||
.where((eb) =>
|
.select(withAlbumUsers)
|
||||||
eb.not(
|
|
||||||
eb.exists(
|
|
||||||
eb
|
|
||||||
.selectFrom('albums_shared_users_users as album_users')
|
|
||||||
.whereRef('album_users.albumsId', '=', 'albums.id'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.where((eb) =>
|
|
||||||
eb.not(eb.exists(eb.selectFrom('shared_links').whereRef('shared_links.albumId', '=', 'albums.id'))),
|
|
||||||
)
|
|
||||||
.select(withOwner)
|
.select(withOwner)
|
||||||
|
.select(withSharedLink)
|
||||||
.orderBy('albums.createdAt', 'desc')
|
.orderBy('albums.createdAt', 'desc')
|
||||||
.execute() as unknown as Promise<AlbumEntity[]>;
|
.execute() as unknown as Promise<AlbumEntity[]>;
|
||||||
}
|
}
|
||||||
@@ -291,6 +282,7 @@ export class AlbumRepository implements IAlbumRepository {
|
|||||||
.selectAll()
|
.selectAll()
|
||||||
.where('id', '=', newAlbum.id)
|
.where('id', '=', newAlbum.id)
|
||||||
.select(withOwner)
|
.select(withOwner)
|
||||||
|
.select(withSharedLink)
|
||||||
.select(withAssets)
|
.select(withAssets)
|
||||||
.select(withAlbumUsers)
|
.select(withAlbumUsers)
|
||||||
.executeTakeFirst() as unknown as Promise<AlbumEntity>;
|
.executeTakeFirst() as unknown as Promise<AlbumEntity>;
|
||||||
@@ -300,7 +292,7 @@ export class AlbumRepository implements IAlbumRepository {
|
|||||||
update(id: string, album: Updateable<Albums>): Promise<AlbumEntity> {
|
update(id: string, album: Updateable<Albums>): Promise<AlbumEntity> {
|
||||||
return this.db
|
return this.db
|
||||||
.updateTable('albums')
|
.updateTable('albums')
|
||||||
.set(album)
|
.set({ ...album, updatedAt: new Date() })
|
||||||
.where('id', '=', id)
|
.where('id', '=', id)
|
||||||
.returningAll('albums')
|
.returningAll('albums')
|
||||||
.returning(withOwner)
|
.returning(withOwner)
|
||||||
@@ -343,6 +335,7 @@ export class AlbumRepository implements IAlbumRepository {
|
|||||||
.select('album_assets.assetsId')
|
.select('album_assets.assetsId')
|
||||||
.orderBy('assets.fileCreatedAt', 'desc')
|
.orderBy('assets.fileCreatedAt', 'desc')
|
||||||
.limit(1),
|
.limit(1),
|
||||||
|
updatedAt: new Date(),
|
||||||
}))
|
}))
|
||||||
.where((eb) =>
|
.where((eb) =>
|
||||||
eb.or([
|
eb.or([
|
||||||
|
|||||||
@@ -72,14 +72,8 @@ export class SearchRepository implements ISearchRepository {
|
|||||||
async searchRandom(size: number, options: AssetSearchOptions): Promise<AssetEntity[]> {
|
async searchRandom(size: number, options: AssetSearchOptions): Promise<AssetEntity[]> {
|
||||||
const uuid = randomUUID();
|
const uuid = randomUUID();
|
||||||
const builder = searchAssetBuilder(this.db, options);
|
const builder = searchAssetBuilder(this.db, options);
|
||||||
const lessThan = builder
|
const lessThan = builder.where('assets.id', '<', uuid).orderBy('assets.id').limit(size);
|
||||||
.where('assets.id', '<', uuid)
|
const greaterThan = builder.where('assets.id', '>', uuid).orderBy('assets.id').limit(size);
|
||||||
.orderBy(sql`random()`)
|
|
||||||
.limit(size);
|
|
||||||
const greaterThan = builder
|
|
||||||
.where('assets.id', '>', uuid)
|
|
||||||
.orderBy(sql`random()`)
|
|
||||||
.limit(size);
|
|
||||||
const { rows } = await sql`${lessThan} union all ${greaterThan} limit ${size}`.execute(this.db);
|
const { rows } = await sql`${lessThan} union all ${greaterThan} limit ${size}`.execute(this.db);
|
||||||
return rows as any as AssetEntity[];
|
return rows as any as AssetEntity[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ describe(AlbumService.name, () => {
|
|||||||
it('gets list of albums for auth user', async () => {
|
it('gets list of albums for auth user', async () => {
|
||||||
albumMock.getOwned.mockResolvedValue([albumStub.empty, albumStub.sharedWithUser]);
|
albumMock.getOwned.mockResolvedValue([albumStub.empty, albumStub.sharedWithUser]);
|
||||||
albumMock.getMetadataForIds.mockResolvedValue([
|
albumMock.getMetadataForIds.mockResolvedValue([
|
||||||
{ albumId: albumStub.empty.id, assetCount: 0, startDate: null, endDate: null },
|
{ albumId: albumStub.empty.id, assetCount: 0, startDate: undefined, endDate: undefined },
|
||||||
{ albumId: albumStub.sharedWithUser.id, assetCount: 0, startDate: null, endDate: null },
|
{ albumId: albumStub.sharedWithUser.id, assetCount: 0, startDate: undefined, endDate: undefined },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const result = await sut.getAll(authStub.admin, {});
|
const result = await sut.getAll(authStub.admin, {});
|
||||||
@@ -82,7 +82,7 @@ describe(AlbumService.name, () => {
|
|||||||
it('gets list of albums that are shared', async () => {
|
it('gets list of albums that are shared', async () => {
|
||||||
albumMock.getShared.mockResolvedValue([albumStub.sharedWithUser]);
|
albumMock.getShared.mockResolvedValue([albumStub.sharedWithUser]);
|
||||||
albumMock.getMetadataForIds.mockResolvedValue([
|
albumMock.getMetadataForIds.mockResolvedValue([
|
||||||
{ albumId: albumStub.sharedWithUser.id, assetCount: 0, startDate: null, endDate: null },
|
{ albumId: albumStub.sharedWithUser.id, assetCount: 0, startDate: undefined, endDate: undefined },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const result = await sut.getAll(authStub.admin, { shared: true });
|
const result = await sut.getAll(authStub.admin, { shared: true });
|
||||||
@@ -94,7 +94,7 @@ describe(AlbumService.name, () => {
|
|||||||
it('gets list of albums that are NOT shared', async () => {
|
it('gets list of albums that are NOT shared', async () => {
|
||||||
albumMock.getNotShared.mockResolvedValue([albumStub.empty]);
|
albumMock.getNotShared.mockResolvedValue([albumStub.empty]);
|
||||||
albumMock.getMetadataForIds.mockResolvedValue([
|
albumMock.getMetadataForIds.mockResolvedValue([
|
||||||
{ albumId: albumStub.empty.id, assetCount: 0, startDate: null, endDate: null },
|
{ albumId: albumStub.empty.id, assetCount: 0, startDate: undefined, endDate: undefined },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const result = await sut.getAll(authStub.admin, { shared: false });
|
const result = await sut.getAll(authStub.admin, { shared: false });
|
||||||
|
|||||||
@@ -55,7 +55,13 @@ export class AlbumService extends BaseService {
|
|||||||
const results = await this.albumRepository.getMetadataForIds(albums.map((album) => album.id));
|
const results = await this.albumRepository.getMetadataForIds(albums.map((album) => album.id));
|
||||||
const albumMetadata: Record<string, AlbumAssetCount> = {};
|
const albumMetadata: Record<string, AlbumAssetCount> = {};
|
||||||
for (const metadata of results) {
|
for (const metadata of results) {
|
||||||
albumMetadata[metadata.albumId] = metadata;
|
const { albumId, assetCount, startDate, endDate } = metadata;
|
||||||
|
albumMetadata[albumId] = {
|
||||||
|
albumId,
|
||||||
|
assetCount,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
@@ -64,9 +70,9 @@ export class AlbumService extends BaseService {
|
|||||||
return {
|
return {
|
||||||
...mapAlbumWithoutAssets(album),
|
...mapAlbumWithoutAssets(album),
|
||||||
sharedLinks: undefined,
|
sharedLinks: undefined,
|
||||||
startDate: albumMetadata[album.id]?.startDate ?? undefined,
|
startDate: albumMetadata[album.id].startDate,
|
||||||
endDate: albumMetadata[album.id]?.endDate ?? undefined,
|
endDate: albumMetadata[album.id].endDate,
|
||||||
assetCount: albumMetadata[album.id]?.assetCount ?? 0,
|
assetCount: albumMetadata[album.id].assetCount,
|
||||||
lastModifiedAssetTimestamp: lastModifiedAsset?.updatedAt,
|
lastModifiedAssetTimestamp: lastModifiedAsset?.updatedAt,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
@@ -83,9 +89,9 @@ export class AlbumService extends BaseService {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...mapAlbum(album, withAssets, auth),
|
...mapAlbum(album, withAssets, auth),
|
||||||
startDate: albumMetadataForIds?.startDate ?? undefined,
|
startDate: albumMetadataForIds.startDate,
|
||||||
endDate: albumMetadataForIds?.endDate ?? undefined,
|
endDate: albumMetadataForIds.endDate,
|
||||||
assetCount: albumMetadataForIds?.assetCount ?? 0,
|
assetCount: albumMetadataForIds.assetCount,
|
||||||
lastModifiedAssetTimestamp: lastModifiedAsset?.updatedAt,
|
lastModifiedAssetTimestamp: lastModifiedAsset?.updatedAt,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+3
-3
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-web",
|
"name": "immich-web",
|
||||||
"version": "1.125.4",
|
"version": "1.125.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "immich-web",
|
"name": "immich-web",
|
||||||
"version": "1.125.4",
|
"version": "1.125.2",
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formatjs/icu-messageformat-parser": "^2.9.8",
|
"@formatjs/icu-messageformat-parser": "^2.9.8",
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
},
|
},
|
||||||
"../open-api/typescript-sdk": {
|
"../open-api/typescript-sdk": {
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.125.4",
|
"version": "1.125.2",
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oazapfts/runtime": "^1.0.2"
|
"@oazapfts/runtime": "^1.0.2"
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-web",
|
"name": "immich-web",
|
||||||
"version": "1.125.4",
|
"version": "1.125.2",
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev --host 0.0.0.0 --port 3000",
|
"dev": "vite dev --host 0.0.0.0 --port 3000",
|
||||||
|
|||||||
@@ -11,11 +11,7 @@
|
|||||||
|
|
||||||
<section class="min-w-screen flex min-h-dvh items-center justify-center relative">
|
<section class="min-w-screen flex min-h-dvh items-center justify-center relative">
|
||||||
<div class="absolute -z-10 w-full h-full flex place-items-center place-content-center">
|
<div class="absolute -z-10 w-full h-full flex place-items-center place-content-center">
|
||||||
<img
|
<img src={immichLogo} class="max-w-screen-md mx-auto h-full mb-2 antialiased -z-10" alt="Immich logo" />
|
||||||
src={immichLogo}
|
|
||||||
class="max-w-screen-md mx-auto h-full mb-2 antialiased -z-10 overflow-hidden"
|
|
||||||
alt="Immich logo"
|
|
||||||
/>
|
|
||||||
<div
|
<div
|
||||||
class="w-full h-[99%] absolute left-0 top-0 backdrop-blur-[200px] bg-transparent dark:bg-immich-dark-bg/20"
|
class="w-full h-[99%] absolute left-0 top-0 backdrop-blur-[200px] bg-transparent dark:bg-immich-dark-bg/20"
|
||||||
></div>
|
></div>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ul class="list-none ml-2">
|
<ul class="list-none ml-2">
|
||||||
{#each Object.entries(items).sort() as [path, tree]}
|
{#each Object.entries(items) as [path, tree]}
|
||||||
{@const value = normalizeTreePath(`${parent}/${path}`)}
|
{@const value = normalizeTreePath(`${parent}/${path}`)}
|
||||||
{@const key = value + getColor(value)}
|
{@const key = value + getColor(value)}
|
||||||
{#key key}
|
{#key key}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class FoldersStore {
|
|||||||
|
|
||||||
const uniquePaths = await getUniqueOriginalPaths();
|
const uniquePaths = await getUniqueOriginalPaths();
|
||||||
this.uniquePaths.push(...uniquePaths);
|
this.uniquePaths.push(...uniquePaths);
|
||||||
|
this.uniquePaths.sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
bustAssetCache() {
|
bustAssetCache() {
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
let pathSegments = $derived(data.path ? data.path.split('/') : []);
|
let pathSegments = $derived(data.path ? data.path.split('/') : []);
|
||||||
let tree = $derived(buildTree(foldersStore.uniquePaths));
|
let tree = $derived(buildTree(foldersStore.uniquePaths));
|
||||||
let currentPath = $derived($page.url.searchParams.get(QueryParameter.PATH) || '');
|
let currentPath = $derived($page.url.searchParams.get(QueryParameter.PATH) || '');
|
||||||
let currentTreeItems = $derived(currentPath ? data.currentFolders : Object.keys(tree).sort());
|
let currentTreeItems = $derived(currentPath ? data.currentFolders : Object.keys(tree));
|
||||||
|
|
||||||
const assetInteraction = new AssetInteraction();
|
const assetInteraction = new AssetInteraction();
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export const load = (async ({ params, url }) => {
|
|||||||
return {
|
return {
|
||||||
asset,
|
asset,
|
||||||
path,
|
path,
|
||||||
currentFolders: Object.keys(tree || {}).sort(),
|
currentFolders: Object.keys(tree || {}),
|
||||||
pathAssets,
|
pathAssets,
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('folders'),
|
title: $t('folders'),
|
||||||
|
|||||||
Reference in New Issue
Block a user