Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c3d9f2ce34 | |||
| 69cedef772 | |||
| 1e509d97f6 | |||
| c7ddd0b44a | |||
| c3a8ddaaf2 | |||
| 526cf23a9e | |||
| e1ed7fa6ed | |||
| 0b6cd74e4d | |||
| 7ca53ba507 | |||
| a96f41aa11 | |||
| ddd73b9911 | |||
| 6f37ab6a9e | |||
| e5667f09c7 | |||
| 668632c398 | |||
| 5d6716d265 | |||
| b6cad7715f | |||
| 48da4c9317 | |||
| a1d9619a6e | |||
| 5dd9a2f850 | |||
| 058b5ea5ca | |||
| 441b009a0b | |||
| cb903db308 | |||
| 03ceca8552 | |||
| 53609d45fe | |||
| 4af8433aad | |||
| 7c978571e0 | |||
| efdf1b49f4 | |||
| f46abbb5b5 | |||
| d8b602f757 | |||
| 59507e557e | |||
| 174de979db | |||
| 862d6d9abe | |||
| bd6c5e1b1c | |||
| b80cc0d90f | |||
| 438344fc8f | |||
| 39141d3f1c | |||
| 28bc7f318e | |||
| 6bfe54788f | |||
| 67468ea367 | |||
| 40327ad987 | |||
| d18bc7007a | |||
| 4cc11efd04 | |||
| 18fcc3569f | |||
| fcbc1ba399 | |||
| 5e6ac87eaf | |||
| 40854f358c | |||
| 51a11d0cb6 |
@@ -16,10 +16,28 @@ concurrency:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
pre-job:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
should_run: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- id: found_paths
|
||||||
|
uses: dorny/paths-filter@v3
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
mobile:
|
||||||
|
- 'mobile/**'
|
||||||
|
- name: Check if we should force jobs to run
|
||||||
|
id: should_force
|
||||||
|
run: echo "should_force=${{ github.event_name == 'workflow_call' || github.event_name == 'workflow_dispatch' }}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
build-sign-android:
|
build-sign-android:
|
||||||
name: Build and sign Android
|
name: Build and sign Android
|
||||||
|
needs: pre-job
|
||||||
# Skip when PR from a fork
|
# Skip when PR from a fork
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' }}
|
if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' && needs.pre-job.outputs.should_run == 'true' }}
|
||||||
runs-on: macos-14
|
runs-on: macos-14
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
+132
-30
@@ -17,47 +17,58 @@ permissions:
|
|||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_and_push:
|
pre-job:
|
||||||
name: Build and Push
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
should_run_server: ${{ steps.found_paths.outputs.server == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
|
should_run_ml: ${{ steps.found_paths.outputs.machine-learning == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- id: found_paths
|
||||||
|
uses: dorny/paths-filter@v3
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
server:
|
||||||
|
- 'server/**'
|
||||||
|
- 'openapi/**'
|
||||||
|
- 'web/**'
|
||||||
|
machine-learning:
|
||||||
|
- 'machine-learning/**'
|
||||||
|
|
||||||
|
- name: Check if we should force jobs to run
|
||||||
|
id: should_force
|
||||||
|
run: echo "should_force=${{ github.event_name == 'workflow_dispatch' || github.event_name == 'release' }}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
build_and_push_ml:
|
||||||
|
name: Build and Push ML
|
||||||
|
needs: pre-job
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run_ml == 'true' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
image: immich-machine-learning
|
||||||
|
context: machine-learning
|
||||||
|
file: machine-learning/Dockerfile
|
||||||
strategy:
|
strategy:
|
||||||
# Prevent a failure in one image from stopping the other builds
|
# Prevent a failure in one image from stopping the other builds
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- image: immich-machine-learning
|
- platforms: linux/amd64,linux/arm64
|
||||||
context: machine-learning
|
|
||||||
file: machine-learning/Dockerfile
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
device: cpu
|
device: cpu
|
||||||
|
|
||||||
- image: immich-machine-learning
|
- platforms: linux/amd64
|
||||||
context: machine-learning
|
|
||||||
file: machine-learning/Dockerfile
|
|
||||||
platforms: linux/amd64
|
|
||||||
device: cuda
|
device: cuda
|
||||||
suffix: -cuda
|
suffix: -cuda
|
||||||
|
|
||||||
- image: immich-machine-learning
|
- platforms: linux/amd64
|
||||||
context: machine-learning
|
|
||||||
file: machine-learning/Dockerfile
|
|
||||||
platforms: linux/amd64
|
|
||||||
device: openvino
|
device: openvino
|
||||||
suffix: -openvino
|
suffix: -openvino
|
||||||
|
|
||||||
- image: immich-machine-learning
|
- platforms: linux/arm64
|
||||||
context: machine-learning
|
|
||||||
file: machine-learning/Dockerfile
|
|
||||||
platforms: linux/arm64
|
|
||||||
device: armnn
|
device: armnn
|
||||||
suffix: -armnn
|
suffix: -armnn
|
||||||
|
|
||||||
- image: immich-server
|
|
||||||
context: .
|
|
||||||
file: server/Dockerfile
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
device: cpu
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -93,8 +104,8 @@ jobs:
|
|||||||
# Disable latest tag
|
# Disable latest tag
|
||||||
latest=false
|
latest=false
|
||||||
images: |
|
images: |
|
||||||
name=ghcr.io/${{ github.repository_owner }}/${{matrix.image}}
|
name=ghcr.io/${{ github.repository_owner }}/${{env.image}}
|
||||||
name=altran1502/${{matrix.image}},enable=${{ github.event_name == 'release' }}
|
name=altran1502/${{env.image}},enable=${{ github.event_name == 'release' }}
|
||||||
tags: |
|
tags: |
|
||||||
# Tag with branch name
|
# Tag with branch name
|
||||||
type=ref,event=branch,suffix=${{ matrix.suffix }}
|
type=ref,event=branch,suffix=${{ matrix.suffix }}
|
||||||
@@ -111,18 +122,109 @@ jobs:
|
|||||||
# Essentially just ignore the cache output (PR can't write to registry cache)
|
# Essentially just ignore the cache output (PR can't write to registry cache)
|
||||||
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
||||||
else
|
else
|
||||||
echo "cache-to=type=registry,mode=max,ref=ghcr.io/${{ github.repository_owner }}/immich-build-cache:${{ matrix.image }}" >> $GITHUB_OUTPUT
|
echo "cache-to=type=registry,mode=max,ref=ghcr.io/${{ github.repository_owner }}/immich-build-cache:${{ env.image }}" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Build and push image
|
- name: Build and push image
|
||||||
uses: docker/build-push-action@v6.7.0
|
uses: docker/build-push-action@v6.7.0
|
||||||
with:
|
with:
|
||||||
context: ${{ matrix.context }}
|
context: ${{ env.context }}
|
||||||
file: ${{ matrix.file }}
|
file: ${{ env.file }}
|
||||||
platforms: ${{ matrix.platforms }}
|
platforms: ${{ matrix.platforms }}
|
||||||
# Skip pushing when PR from a fork
|
# Skip pushing when PR from a fork
|
||||||
push: ${{ !github.event.pull_request.head.repo.fork }}
|
push: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/immich-build-cache:${{matrix.image}}
|
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/immich-build-cache:${{env.image}}
|
||||||
|
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
||||||
|
tags: ${{ steps.metadata.outputs.tags }}
|
||||||
|
labels: ${{ steps.metadata.outputs.labels }}
|
||||||
|
build-args: |
|
||||||
|
DEVICE=${{ matrix.device }}
|
||||||
|
BUILD_ID=${{ github.run_id }}
|
||||||
|
BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.metadata.outputs.tags }}
|
||||||
|
BUILD_SOURCE_REF=${{ github.ref_name }}
|
||||||
|
BUILD_SOURCE_COMMIT=${{ github.sha }}
|
||||||
|
|
||||||
|
|
||||||
|
build_and_push_server:
|
||||||
|
name: Build and Push Server
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: pre-job
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
||||||
|
env:
|
||||||
|
image: immich-server
|
||||||
|
context: .
|
||||||
|
file: server/Dockerfile
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- platforms: linux/amd64,linux/arm64
|
||||||
|
device: cpu
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3.2.0
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3.6.1
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
# Only push to Docker Hub when making a release
|
||||||
|
if: ${{ github.event_name == 'release' }}
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
# Skip when PR from a fork
|
||||||
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Generate docker image tags
|
||||||
|
id: metadata
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
flavor: |
|
||||||
|
# Disable latest tag
|
||||||
|
latest=false
|
||||||
|
images: |
|
||||||
|
name=ghcr.io/${{ github.repository_owner }}/${{env.image}}
|
||||||
|
name=altran1502/${{env.image}},enable=${{ github.event_name == 'release' }}
|
||||||
|
tags: |
|
||||||
|
# Tag with branch name
|
||||||
|
type=ref,event=branch,suffix=${{ matrix.suffix }}
|
||||||
|
# Tag with pr-number
|
||||||
|
type=ref,event=pr,suffix=${{ matrix.suffix }}
|
||||||
|
# Tag with git tag on release
|
||||||
|
type=ref,event=tag,suffix=${{ matrix.suffix }}
|
||||||
|
type=raw,value=release,enable=${{ github.event_name == 'release' }},suffix=${{ matrix.suffix }}
|
||||||
|
|
||||||
|
- name: Determine build cache output
|
||||||
|
id: cache-target
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||||
|
# Essentially just ignore the cache output (PR can't write to registry cache)
|
||||||
|
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "cache-to=type=registry,mode=max,ref=ghcr.io/${{ github.repository_owner }}/immich-build-cache:${{ env.image }}" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build and push image
|
||||||
|
uses: docker/build-push-action@v6.7.0
|
||||||
|
with:
|
||||||
|
context: ${{ env.context }}
|
||||||
|
file: ${{ env.file }}
|
||||||
|
platforms: ${{ matrix.platforms }}
|
||||||
|
# Skip pushing when PR from a fork
|
||||||
|
push: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
|
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/immich-build-cache:${{env.image}}
|
||||||
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
||||||
tags: ${{ steps.metadata.outputs.tags }}
|
tags: ${{ steps.metadata.outputs.tags }}
|
||||||
labels: ${{ steps.metadata.outputs.labels }}
|
labels: ${{ steps.metadata.outputs.labels }}
|
||||||
|
|||||||
@@ -2,12 +2,8 @@ name: Docs build
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths:
|
|
||||||
- "docs/**"
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths:
|
|
||||||
- "docs/**"
|
|
||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
@@ -16,7 +12,26 @@ concurrency:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
pre-job:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
should_run: ${{ steps.found_paths.outputs.docs == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- id: found_paths
|
||||||
|
uses: dorny/paths-filter@v3
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
docs:
|
||||||
|
- 'docs/**'
|
||||||
|
- name: Check if we should force jobs to run
|
||||||
|
id: should_force
|
||||||
|
run: echo "should_force=${{ github.event_name == 'release' }}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
build:
|
build:
|
||||||
|
needs: pre-job
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
|
|||||||
@@ -10,10 +10,28 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
parameters: ${{ steps.parameters.outputs.result }}
|
parameters: ${{ steps.parameters.outputs.result }}
|
||||||
|
artifact: ${{ steps.get-artifact.outputs.result }}
|
||||||
steps:
|
steps:
|
||||||
- if: ${{ github.event.workflow_run.conclusion == 'failure' }}
|
- if: ${{ github.event.workflow_run.conclusion != 'success' }}
|
||||||
run: echo 'The triggering workflow failed' && exit 1
|
run: echo 'The triggering workflow did not succeed' && exit 1
|
||||||
|
- name: Get artifact
|
||||||
|
id: get-artifact
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
run_id: context.payload.workflow_run.id,
|
||||||
|
});
|
||||||
|
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
|
||||||
|
return artifact.name == "docs-build-output"
|
||||||
|
})[0];
|
||||||
|
if (!matchArtifact) {
|
||||||
|
console.log("No artifact found with the name docs-build-output, build job was skipped")
|
||||||
|
return { found: false };
|
||||||
|
}
|
||||||
|
return { found: true, id: matchArtifact.id };
|
||||||
- name: Determine deploy parameters
|
- name: Determine deploy parameters
|
||||||
id: parameters
|
id: parameters
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v7
|
||||||
@@ -75,7 +93,7 @@ jobs:
|
|||||||
deploy:
|
deploy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: checks
|
needs: checks
|
||||||
if: ${{ fromJson(needs.checks.outputs.parameters).shouldDeploy }}
|
if: ${{ fromJson(needs.checks.outputs.artifact).found && fromJson(needs.checks.outputs.parameters).shouldDeploy }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -98,18 +116,11 @@ jobs:
|
|||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v7
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
let artifact = ${{ needs.checks.outputs.artifact }};
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
run_id: context.payload.workflow_run.id,
|
|
||||||
});
|
|
||||||
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
|
|
||||||
return artifact.name == "docs-build-output"
|
|
||||||
})[0];
|
|
||||||
let download = await github.rest.actions.downloadArtifact({
|
let download = await github.rest.actions.downloadArtifact({
|
||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
artifact_id: matchArtifact.id,
|
artifact_id: artifact.id,
|
||||||
archive_format: 'zip',
|
archive_format: 'zip',
|
||||||
});
|
});
|
||||||
let fs = require('fs');
|
let fs = require('fs');
|
||||||
|
|||||||
@@ -10,8 +10,27 @@ concurrency:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
pre-job:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
should_run: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- id: found_paths
|
||||||
|
uses: dorny/paths-filter@v3
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
mobile:
|
||||||
|
- 'mobile/**'
|
||||||
|
- name: Check if we should force jobs to run
|
||||||
|
id: should_force
|
||||||
|
run: echo "should_force=${{ github.event_name == 'release' }}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
mobile-dart-analyze:
|
mobile-dart-analyze:
|
||||||
name: Run Dart Code Analysis
|
name: Run Dart Code Analysis
|
||||||
|
needs: pre-job
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run == 'true' }}
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
|||||||
+123
-12
@@ -10,8 +10,47 @@ concurrency:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
pre-job:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
should_run_web: ${{ steps.found_paths.outputs.web == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
|
should_run_server: ${{ steps.found_paths.outputs.server == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
|
should_run_cli: ${{ steps.found_paths.outputs.cli == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
|
should_run_e2e: ${{ steps.found_paths.outputs.e2e == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
|
should_run_mobile: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
|
should_run_ml: ${{ steps.found_paths.outputs.machine-learning == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
|
should_run_e2e_web: ${{ steps.found_paths.outputs.e2e == 'true' || steps.found_paths.outputs.web == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
|
should_run_e2e_server_cli: ${{ steps.found_paths.outputs.e2e == 'true' || steps.found_paths.outputs.server == 'true' || steps.found_paths.outputs.cli == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- id: found_paths
|
||||||
|
uses: dorny/paths-filter@v3
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
web:
|
||||||
|
- 'web/**'
|
||||||
|
- 'open-api/typescript-sdk/**'
|
||||||
|
server:
|
||||||
|
- 'server/**'
|
||||||
|
cli:
|
||||||
|
- 'cli/**'
|
||||||
|
- 'open-api/typescript-sdk/**'
|
||||||
|
e2e:
|
||||||
|
- 'e2e/**'
|
||||||
|
mobile:
|
||||||
|
- 'mobile/**'
|
||||||
|
machine-learning:
|
||||||
|
- 'machine-learning/**'
|
||||||
|
|
||||||
|
- name: Check if we should force jobs to run
|
||||||
|
id: should_force
|
||||||
|
run: echo "should_force=${{ github.event_name == 'workflow_dispatch' }}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
server-unit-tests:
|
server-unit-tests:
|
||||||
name: Server
|
name: Server
|
||||||
|
needs: pre-job
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -47,6 +86,8 @@ jobs:
|
|||||||
|
|
||||||
cli-unit-tests:
|
cli-unit-tests:
|
||||||
name: CLI
|
name: CLI
|
||||||
|
needs: pre-job
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run_cli == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -86,6 +127,8 @@ jobs:
|
|||||||
|
|
||||||
cli-unit-tests-win:
|
cli-unit-tests-win:
|
||||||
name: CLI (Windows)
|
name: CLI (Windows)
|
||||||
|
needs: pre-job
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run_cli == 'true' }}
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -118,6 +161,8 @@ jobs:
|
|||||||
|
|
||||||
web-unit-tests:
|
web-unit-tests:
|
||||||
name: Web
|
name: Web
|
||||||
|
needs: pre-job
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run_web == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -159,13 +204,54 @@ jobs:
|
|||||||
run: npm run test:cov
|
run: npm run test:cov
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
e2e-tests:
|
e2e-tests-lint:
|
||||||
name: End-to-End Tests
|
name: End-to-End Lint
|
||||||
|
needs: pre-job
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run_e2e == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./e2e
|
working-directory: ./e2e
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: './e2e/.nvmrc'
|
||||||
|
|
||||||
|
- name: Run setup typescript-sdk
|
||||||
|
run: npm ci && npm run build
|
||||||
|
working-directory: ./open-api/typescript-sdk
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
|
- name: Run linter
|
||||||
|
run: npm run lint
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
|
- name: Run formatter
|
||||||
|
run: npm run format
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
|
- name: Run tsc
|
||||||
|
run: npm run check
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
|
e2e-tests-server-cli:
|
||||||
|
name: End-to-End Tests (Server & CLI)
|
||||||
|
needs: pre-job
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run_e2e_server_cli == 'true' }}
|
||||||
|
runs-on: mich
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./e2e
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -191,16 +277,41 @@ jobs:
|
|||||||
run: npm ci
|
run: npm ci
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run linter
|
- name: Docker build
|
||||||
run: npm run lint
|
run: docker compose build
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run formatter
|
- name: Run e2e tests (api & cli)
|
||||||
run: npm run format
|
run: npm run test
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run tsc
|
e2e-tests-web:
|
||||||
run: npm run check
|
name: End-to-End Tests (Web)
|
||||||
|
needs: pre-job
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run_e2e_web == 'true' }}
|
||||||
|
runs-on: mich
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./e2e
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: 'recursive'
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: './e2e/.nvmrc'
|
||||||
|
|
||||||
|
- name: Run setup typescript-sdk
|
||||||
|
run: npm ci && npm run build
|
||||||
|
working-directory: ./open-api/typescript-sdk
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
@@ -211,16 +322,14 @@ jobs:
|
|||||||
run: docker compose build
|
run: docker compose build
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run e2e tests (api & cli)
|
|
||||||
run: npm run test
|
|
||||||
if: ${{ !cancelled() }}
|
|
||||||
|
|
||||||
- name: Run e2e tests (web)
|
- name: Run e2e tests (web)
|
||||||
run: npx playwright test
|
run: npx playwright test
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
mobile-unit-tests:
|
mobile-unit-tests:
|
||||||
name: Mobile
|
name: Mobile
|
||||||
|
needs: pre-job
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run_mobile == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@@ -235,6 +344,8 @@ jobs:
|
|||||||
|
|
||||||
ml-unit-tests:
|
ml-unit-tests:
|
||||||
name: Machine Learning
|
name: Machine Learning
|
||||||
|
needs: pre-job
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run_ml == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export default [
|
|||||||
'unicorn/import-style': 'off',
|
'unicorn/import-style': 'off',
|
||||||
curly: 2,
|
curly: 2,
|
||||||
'prettier/prettier': 0,
|
'prettier/prettier': 0,
|
||||||
|
'object-shorthand': ['error', 'always'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
Generated
+62
-126
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.16",
|
"version": "2.2.17",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.16",
|
"version": "2.2.17",
|
||||||
"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",
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"@types/cli-progress": "^3.11.0",
|
"@types/cli-progress": "^3.11.0",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/mock-fs": "^4.13.1",
|
"@types/mock-fs": "^4.13.1",
|
||||||
"@types/node": "^20.16.1",
|
"@types/node": "^20.16.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||||
"@typescript-eslint/parser": "^8.0.0",
|
"@typescript-eslint/parser": "^8.0.0",
|
||||||
"@vitest/coverage-v8": "^2.0.5",
|
"@vitest/coverage-v8": "^2.0.5",
|
||||||
@@ -52,14 +52,14 @@
|
|||||||
},
|
},
|
||||||
"../open-api/typescript-sdk": {
|
"../open-api/typescript-sdk": {
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.113.0",
|
"version": "1.113.1",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"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"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.16.1",
|
"@types/node": "^20.16.2",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -727,9 +727,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/config-array": {
|
"node_modules/@eslint/config-array": {
|
||||||
"version": "0.17.1",
|
"version": "0.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz",
|
||||||
"integrity": "sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==",
|
"integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -825,9 +825,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "9.9.0",
|
"version": "9.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.1.tgz",
|
||||||
"integrity": "sha512-hhetes6ZHP3BlXLxmd8K2SNgkhNSi+UcecbnwWKwpP7kyi/uC75DJ1lOOBO3xrC4jyojtGE3YxKZPHfk4yrgug==",
|
"integrity": "sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1324,9 +1324,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "20.16.1",
|
"version": "20.16.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.3.tgz",
|
||||||
"integrity": "sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ==",
|
"integrity": "sha512-/wdGiWRkMOm53gAsSyFMXFZHbVg7C6CbkrzHNpaHoYfsUWPg7m6ZRKtvQjgvQ9i8WT540a3ydRlRQbxjY30XxQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1340,17 +1340,17 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "8.2.0",
|
"version": "8.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.3.0.tgz",
|
||||||
"integrity": "sha512-02tJIs655em7fvt9gps/+4k4OsKULYGtLBPJfOsmOq1+3cdClYiF0+d6mHu6qDnTcg88wJBkcPLpQhq7FyDz0A==",
|
"integrity": "sha512-FLAIn63G5KH+adZosDYiutqkOkYEx0nvcwNNfJAf+c7Ae/H35qWwTYvPZUKFj5AS+WfHG/WJJfWnDnyNUlp8UA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/regexpp": "^4.10.0",
|
"@eslint-community/regexpp": "^4.10.0",
|
||||||
"@typescript-eslint/scope-manager": "8.2.0",
|
"@typescript-eslint/scope-manager": "8.3.0",
|
||||||
"@typescript-eslint/type-utils": "8.2.0",
|
"@typescript-eslint/type-utils": "8.3.0",
|
||||||
"@typescript-eslint/utils": "8.2.0",
|
"@typescript-eslint/utils": "8.3.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.2.0",
|
"@typescript-eslint/visitor-keys": "8.3.0",
|
||||||
"graphemer": "^1.4.0",
|
"graphemer": "^1.4.0",
|
||||||
"ignore": "^5.3.1",
|
"ignore": "^5.3.1",
|
||||||
"natural-compare": "^1.4.0",
|
"natural-compare": "^1.4.0",
|
||||||
@@ -1374,16 +1374,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "8.2.0",
|
"version": "8.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.3.0.tgz",
|
||||||
"integrity": "sha512-j3Di+o0lHgPrb7FxL3fdEy6LJ/j2NE8u+AP/5cQ9SKb+JLH6V6UHDqJ+e0hXBkHP1wn1YDFjYCS9LBQsZDlDEg==",
|
"integrity": "sha512-h53RhVyLu6AtpUzVCYLPhZGL5jzTD9fZL+SYf/+hYOx2bDkyQXztXSc4tbvKYHzfMXExMLiL9CWqJmVz6+78IQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.2.0",
|
"@typescript-eslint/scope-manager": "8.3.0",
|
||||||
"@typescript-eslint/types": "8.2.0",
|
"@typescript-eslint/types": "8.3.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.2.0",
|
"@typescript-eslint/typescript-estree": "8.3.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.2.0",
|
"@typescript-eslint/visitor-keys": "8.3.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1403,14 +1403,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/scope-manager": {
|
"node_modules/@typescript-eslint/scope-manager": {
|
||||||
"version": "8.2.0",
|
"version": "8.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.3.0.tgz",
|
||||||
"integrity": "sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==",
|
"integrity": "sha512-mz2X8WcN2nVu5Hodku+IR8GgCOl4C0G/Z1ruaWN4dgec64kDBabuXyPAr+/RgJtumv8EEkqIzf3X2U5DUKB2eg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.2.0",
|
"@typescript-eslint/types": "8.3.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.2.0"
|
"@typescript-eslint/visitor-keys": "8.3.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -1421,14 +1421,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/type-utils": {
|
"node_modules/@typescript-eslint/type-utils": {
|
||||||
"version": "8.2.0",
|
"version": "8.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.3.0.tgz",
|
||||||
"integrity": "sha512-g1CfXGFMQdT5S+0PSO0fvGXUaiSkl73U1n9LTK5aRAFnPlJ8dLKkXr4AaLFvPedW8lVDoMgLLE3JN98ZZfsj0w==",
|
"integrity": "sha512-wrV6qh//nLbfXZQoj32EXKmwHf4b7L+xXLrP3FZ0GOUU72gSvLjeWUl5J5Ue5IwRxIV1TfF73j/eaBapxx99Lg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/typescript-estree": "8.2.0",
|
"@typescript-eslint/typescript-estree": "8.3.0",
|
||||||
"@typescript-eslint/utils": "8.2.0",
|
"@typescript-eslint/utils": "8.3.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"ts-api-utils": "^1.3.0"
|
"ts-api-utils": "^1.3.0"
|
||||||
},
|
},
|
||||||
@@ -1446,9 +1446,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/types": {
|
"node_modules/@typescript-eslint/types": {
|
||||||
"version": "8.2.0",
|
"version": "8.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.3.0.tgz",
|
||||||
"integrity": "sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==",
|
"integrity": "sha512-y6sSEeK+facMaAyixM36dQ5NVXTnKWunfD1Ft4xraYqxP0lC0POJmIaL/mw72CUMqjY9qfyVfXafMeaUj0noWw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1460,16 +1460,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree": {
|
"node_modules/@typescript-eslint/typescript-estree": {
|
||||||
"version": "8.2.0",
|
"version": "8.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.3.0.tgz",
|
||||||
"integrity": "sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==",
|
"integrity": "sha512-Mq7FTHl0R36EmWlCJWojIC1qn/ZWo2YiWYc1XVtasJ7FIgjo0MVv9rZWXEE7IK2CGrtwe1dVOxWwqXUdNgfRCA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.2.0",
|
"@typescript-eslint/types": "8.3.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.2.0",
|
"@typescript-eslint/visitor-keys": "8.3.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"globby": "^11.1.0",
|
"fast-glob": "^3.3.2",
|
||||||
"is-glob": "^4.0.3",
|
"is-glob": "^4.0.3",
|
||||||
"minimatch": "^9.0.4",
|
"minimatch": "^9.0.4",
|
||||||
"semver": "^7.6.0",
|
"semver": "^7.6.0",
|
||||||
@@ -1489,16 +1489,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/utils": {
|
"node_modules/@typescript-eslint/utils": {
|
||||||
"version": "8.2.0",
|
"version": "8.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.3.0.tgz",
|
||||||
"integrity": "sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==",
|
"integrity": "sha512-F77WwqxIi/qGkIGOGXNBLV7nykwfjLsdauRB/DOFPdv6LTF3BHHkBpq81/b5iMPSF055oO2BiivDJV4ChvNtXA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.4.0",
|
"@eslint-community/eslint-utils": "^4.4.0",
|
||||||
"@typescript-eslint/scope-manager": "8.2.0",
|
"@typescript-eslint/scope-manager": "8.3.0",
|
||||||
"@typescript-eslint/types": "8.2.0",
|
"@typescript-eslint/types": "8.3.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.2.0"
|
"@typescript-eslint/typescript-estree": "8.3.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -1512,13 +1512,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/visitor-keys": {
|
"node_modules/@typescript-eslint/visitor-keys": {
|
||||||
"version": "8.2.0",
|
"version": "8.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.3.0.tgz",
|
||||||
"integrity": "sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==",
|
"integrity": "sha512-RmZwrTbQ9QveF15m/Cl28n0LXD6ea2CjkhH5rQ55ewz3H24w+AMCJHPVYaZ8/0HoG8Z3cLLFFycRXxeO2tz9FA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.2.0",
|
"@typescript-eslint/types": "8.3.0",
|
||||||
"eslint-visitor-keys": "^3.4.3"
|
"eslint-visitor-keys": "^3.4.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1705,16 +1705,6 @@
|
|||||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/array-union": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/assertion-error": {
|
"node_modules/assertion-error": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
|
||||||
@@ -2034,19 +2024,6 @@
|
|||||||
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
|
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/dir-glob": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
|
||||||
"integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"path-type": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/eastasianwidth": {
|
"node_modules/eastasianwidth": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||||
@@ -2135,17 +2112,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "9.9.0",
|
"version": "9.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.1.tgz",
|
||||||
"integrity": "sha512-JfiKJrbx0506OEerjK2Y1QlldtBxkAlLxT5OEcRF8uaQ86noDe2k31Vw9rnSWv+MXZHj7OOUV/dA0AhdLFcyvA==",
|
"integrity": "sha512-dHvhrbfr4xFQ9/dq+jcVneZMyRYLjggWjk6RVsIiHsP8Rz6yZ8LvZ//iU4TrZF+SXWG+JkNF2OyiZRvzgRDqMg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.2.0",
|
||||||
"@eslint-community/regexpp": "^4.11.0",
|
"@eslint-community/regexpp": "^4.11.0",
|
||||||
"@eslint/config-array": "^0.17.1",
|
"@eslint/config-array": "^0.18.0",
|
||||||
"@eslint/eslintrc": "^3.1.0",
|
"@eslint/eslintrc": "^3.1.0",
|
||||||
"@eslint/js": "9.9.0",
|
"@eslint/js": "9.9.1",
|
||||||
"@humanwhocodes/module-importer": "^1.0.1",
|
"@humanwhocodes/module-importer": "^1.0.1",
|
||||||
"@humanwhocodes/retry": "^0.3.0",
|
"@humanwhocodes/retry": "^0.3.0",
|
||||||
"@nodelib/fs.walk": "^1.2.8",
|
"@nodelib/fs.walk": "^1.2.8",
|
||||||
@@ -2666,27 +2643,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/globby": {
|
|
||||||
"version": "11.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
|
|
||||||
"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"array-union": "^2.1.0",
|
|
||||||
"dir-glob": "^3.0.1",
|
|
||||||
"fast-glob": "^3.2.9",
|
|
||||||
"ignore": "^5.2.0",
|
|
||||||
"merge2": "^1.4.1",
|
|
||||||
"slash": "^3.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/globrex": {
|
"node_modules/globrex": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
|
||||||
@@ -3437,16 +3393,6 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/path-type": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/pathe": {
|
"node_modules/pathe": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
|
||||||
@@ -3880,16 +3826,6 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/slash": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.16",
|
"version": "2.2.17",
|
||||||
"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",
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
"@types/cli-progress": "^3.11.0",
|
"@types/cli-progress": "^3.11.0",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/mock-fs": "^4.13.1",
|
"@types/mock-fs": "^4.13.1",
|
||||||
"@types/node": "^20.16.1",
|
"@types/node": "^20.16.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||||
"@typescript-eslint/parser": "^8.0.0",
|
"@typescript-eslint/parser": "^8.0.0",
|
||||||
"@vitest/coverage-v8": "^2.0.5",
|
"@vitest/coverage-v8": "^2.0.5",
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ resource "cloudflare_record" "immich_app_release_domain" {
|
|||||||
proxied = true
|
proxied = true
|
||||||
ttl = 1
|
ttl = 1
|
||||||
type = "CNAME"
|
type = "CNAME"
|
||||||
value = data.terraform_remote_state.cloudflare_immich_app_docs.outputs.immich_app_branch_pages_hostname
|
content = data.terraform_remote_state.cloudflare_immich_app_docs.outputs.immich_app_branch_pages_hostname
|
||||||
zone_id = data.terraform_remote_state.cloudflare_account.outputs.immich_app_zone_id
|
zone_id = data.terraform_remote_state.cloudflare_account.outputs.immich_app_zone_id
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ resource "cloudflare_record" "immich_app_branch_subdomain" {
|
|||||||
proxied = true
|
proxied = true
|
||||||
ttl = 1
|
ttl = 1
|
||||||
type = "CNAME"
|
type = "CNAME"
|
||||||
value = "${replace(var.prefix_name, "/\\/|\\./", "-")}.${local.is_release ? data.terraform_remote_state.cloudflare_account.outputs.immich_app_archive_pages_project_subdomain : data.terraform_remote_state.cloudflare_account.outputs.immich_app_preview_pages_project_subdomain}"
|
content = "${replace(var.prefix_name, "/\\/|\\./", "-")}.${local.is_release ? data.terraform_remote_state.cloudflare_account.outputs.immich_app_archive_pages_project_subdomain : data.terraform_remote_state.cloudflare_account.outputs.immich_app_preview_pages_project_subdomain}"
|
||||||
zone_id = data.terraform_remote_state.cloudflare_account.outputs.immich_app_zone_id
|
zone_id = data.terraform_remote_state.cloudflare_account.outputs.immich_app_zone_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ services:
|
|||||||
command: ['./run.sh', '-disable-reporting']
|
command: ['./run.sh', '-disable-reporting']
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
image: grafana/grafana:11.1.4-ubuntu@sha256:8e74fb7eed4d59fb5595acd0576c21411167f6b6401426ae29f2e8f9f71b68f6
|
image: grafana/grafana:11.2.0-ubuntu@sha256:8e2c13739563c3da9d45de96c6bcb63ba617cac8c571c060112c7fc8ad6914e9
|
||||||
volumes:
|
volumes:
|
||||||
- grafana-data:/var/lib/grafana
|
- grafana-data:/var/lib/grafana
|
||||||
|
|
||||||
|
|||||||
@@ -154,21 +154,21 @@ Configuration of Authorised redirect URIs (Google Console)
|
|||||||
|
|
||||||
Configuration of OAuth in Immich System Settings
|
Configuration of OAuth in Immich System Settings
|
||||||
|
|
||||||
| Setting | Value |
|
| Setting | Value |
|
||||||
| ---------------------------- | ------------------------------------------------------------------------------------------------------ |
|
| ---------------------------- | ---------------------------------------------------------------------------- |
|
||||||
| Issuer URL | [https://accounts.google.com](https://accounts.google.com) |
|
| Issuer URL | `https://accounts.google.com` |
|
||||||
| Client ID | 7\***\*\*\*\*\*\*\***\*\*\***\*\*\*\*\*\*\***vuls.apps.googleusercontent.com |
|
| Client ID | 7\***\*\*\*\*\*\*\***\*\*\***\*\*\*\*\*\*\***vuls.apps.googleusercontent.com |
|
||||||
| Client Secret | G\***\*\*\*\*\*\*\***\*\*\***\*\*\*\*\*\*\***OO |
|
| Client Secret | G\***\*\*\*\*\*\*\***\*\*\***\*\*\*\*\*\*\***OO |
|
||||||
| Scope | openid email profile |
|
| Scope | openid email profile |
|
||||||
| Signing Algorithm | RS256 |
|
| Signing Algorithm | RS256 |
|
||||||
| Storage Label Claim | preferred_username |
|
| Storage Label Claim | preferred_username |
|
||||||
| Storage Quota Claim | immich_quota |
|
| Storage Quota Claim | immich_quota |
|
||||||
| Default Storage Quota (GiB) | 0 (0 for unlimited quota) |
|
| Default Storage Quota (GiB) | 0 (0 for unlimited quota) |
|
||||||
| Button Text | Sign in with Google (optional) |
|
| Button Text | Sign in with Google (optional) |
|
||||||
| Auto Register | Enabled (optional) |
|
| Auto Register | Enabled (optional) |
|
||||||
| Auto Launch | Enabled |
|
| Auto Launch | Enabled |
|
||||||
| Mobile Redirect URI Override | Enabled (required) |
|
| Mobile Redirect URI Override | Enabled (required) |
|
||||||
| Mobile Redirect URI | [https://demo.immich.app/api/oauth/mobile-redirect](https://demo.immich.app/api/oauth/mobile-redirect) |
|
| Mobile Redirect URI | `https://example.immich.app/api/oauth/mobile-redirect` |
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|||||||
@@ -104,8 +104,8 @@ The `immich-server` container will need access to the gallery. Modify your docke
|
|||||||
immich-server:
|
immich-server:
|
||||||
volumes:
|
volumes:
|
||||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||||
+ - /mnt/nas/christmas-trip:/mnt/nas/christmas-trip:ro
|
+ - /mnt/nas/christmas-trip:/mnt/media/christmas-trip:ro
|
||||||
+ - /home/user/old-pics:/home/user/old-pics:ro
|
+ - /home/user/old-pics:/mnt/media/old-pics:ro
|
||||||
+ - /mnt/media/videos:/mnt/media/videos:ro
|
+ - /mnt/media/videos:/mnt/media/videos:ro
|
||||||
+ - /mnt/media/videos2:/mnt/media/videos2 # the files in this folder can be deleted, as it does not end with :ro
|
+ - /mnt/media/videos2:/mnt/media/videos2 # the files in this folder can be deleted, as it does not end with :ro
|
||||||
+ - "C:/Users/user_name/Desktop/my media:/mnt/media/my-media:ro" # import path in Windows system.
|
+ - "C:/Users/user_name/Desktop/my media:/mnt/media/my-media:ro" # import path in Windows system.
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ A reverse proxy is a service that sits between web servers and clients. A revers
|
|||||||
|
|
||||||
If you're hosting your own reverse proxy, [Nginx](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/) is a great option. An example configuration for Nginx is provided [here](/docs/administration/reverse-proxy.md).
|
If you're hosting your own reverse proxy, [Nginx](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/) is a great option. An example configuration for Nginx is provided [here](/docs/administration/reverse-proxy.md).
|
||||||
|
|
||||||
You'll also need your own certificate to authenticate https connections. If you're making Immich publicly accesible, [Let's Encrypt](https://letsencrypt.org/) can provide a free certificate for your domain and is the recommended option. Alternatively, a [self-signed certificate](https://en.wikipedia.org/wiki/Self-signed_certificate) allows you to encrypt your connection to Immich, but it raises a security warning on the client's browser.
|
You'll also need your own certificate to authenticate https connections. If you're making Immich publicly accessible, [Let's Encrypt](https://letsencrypt.org/) can provide a free certificate for your domain and is the recommended option. Alternatively, a [self-signed certificate](https://en.wikipedia.org/wiki/Self-signed_certificate) allows you to encrypt your connection to Immich, but it raises a security warning on the client's browser.
|
||||||
|
|
||||||
A remote reverse proxy like [Cloudflare](https://www.cloudflare.com/learning/cdn/glossary/reverse-proxy/) increases security by hiding the server IP address, which makes targeted attacks like [DDoS](https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/) harder.
|
A remote reverse proxy like [Cloudflare](https://www.cloudflare.com/learning/cdn/glossary/reverse-proxy/) increases security by hiding the server IP address, which makes targeted attacks like [DDoS](https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/) harder.
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ When `DB_URL` is defined, the `DB_HOSTNAME`, `DB_PORT`, `DB_USERNAME`, `DB_PASSW
|
|||||||
All `REDIS_` variables must be provided to all Immich workers, including `api` and `microservices`.
|
All `REDIS_` variables must be provided to all Immich workers, including `api` and `microservices`.
|
||||||
|
|
||||||
`REDIS_URL` must start with `ioredis://` and then include a `base64` encoded JSON string for the configuration.
|
`REDIS_URL` must start with `ioredis://` and then include a `base64` encoded JSON string for the configuration.
|
||||||
More info can be found in the upstream [ioredis][redis-api] documentation.
|
More info can be found in the upstream [ioredis] documentation.
|
||||||
|
|
||||||
When `REDIS_URL` or `REDIS_SOCKET` are defined, the `REDIS_HOSTNAME`, `REDIS_PORT`, `REDIS_USERNAME`, `REDIS_PASSWORD`, and `REDIS_DBINDEX` variables are ignored.
|
When `REDIS_URL` or `REDIS_SOCKET` are defined, the `REDIS_HOSTNAME`, `REDIS_PORT`, `REDIS_USERNAME`, `REDIS_PASSWORD`, and `REDIS_DBINDEX` variables are ignored.
|
||||||
:::
|
:::
|
||||||
@@ -226,4 +226,4 @@ to use use a Docker secret for the password in the Redis container.
|
|||||||
[docker-secrets-example]: https://github.com/docker-library/redis/issues/46#issuecomment-335326234
|
[docker-secrets-example]: https://github.com/docker-library/redis/issues/46#issuecomment-335326234
|
||||||
[docker-secrets-docs]: https://github.com/docker-library/docs/tree/master/postgres#docker-secrets
|
[docker-secrets-docs]: https://github.com/docker-library/docs/tree/master/postgres#docker-secrets
|
||||||
[docker-secrets]: https://docs.docker.com/engine/swarm/secrets/
|
[docker-secrets]: https://docs.docker.com/engine/swarm/secrets/
|
||||||
[redis-api]: https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository
|
[ioredis]: https://ioredis.readthedocs.io/en/latest/README/#connect-to-redis
|
||||||
|
|||||||
Generated
+4
-3
@@ -13698,9 +13698,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prism-react-renderer": {
|
"node_modules/prism-react-renderer": {
|
||||||
"version": "2.3.1",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.4.0.tgz",
|
||||||
"integrity": "sha512-Rdf+HzBLR7KYjzpJ1rSoxT9ioO85nZngQEoFIhL07XhtJHlCU3SOz0GJ6+qvMyQe0Se+BV3qpe6Yd/NmQF5Juw==",
|
"integrity": "sha512-327BsVCD/unU4CNLZTWVHyUHKnsqcvj2qbPlQ8MiBE2eq2rgctjigPA1Gp9HLF83kZ20zNN6jgizHJeEsyFYOw==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/prismjs": "^1.26.0",
|
"@types/prismjs": "^1.26.0",
|
||||||
"clsx": "^2.0.0"
|
"clsx": "^2.0.0"
|
||||||
|
|||||||
@@ -38,6 +38,11 @@ const projects: CommunityProjectProps[] = [
|
|||||||
description: 'Lightroom plugin to publish photos from Lightroom collections to Immich albums.',
|
description: 'Lightroom plugin to publish photos from Lightroom collections to Immich albums.',
|
||||||
url: 'https://github.com/midzelis/mi.Immich.Publisher',
|
url: 'https://github.com/midzelis/mi.Immich.Publisher',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Lightroom Immich Plugin: lrc-immich-plugin',
|
||||||
|
description: 'Another Lightroom plugin to publish or export photos from Lightroom to Immich.',
|
||||||
|
url: 'https://github.com/bmachek/lrc-immich-plugin',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'Immich Duplicate Finder',
|
title: 'Immich Duplicate Finder',
|
||||||
description: 'Webapp that uses machine learning to identify near-duplicate images.',
|
description: 'Webapp that uses machine learning to identify near-duplicate images.',
|
||||||
|
|||||||
Vendored
+4
@@ -1,4 +1,8 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"label": "v1.113.1",
|
||||||
|
"url": "https://v1.113.1.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.113.0",
|
"label": "v1.113.0",
|
||||||
"url": "https://v1.113.0.archive.immich.app"
|
"url": "https://v1.113.0.archive.immich.app"
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ export default [
|
|||||||
'unicorn/prefer-top-level-await': 'off',
|
'unicorn/prefer-top-level-await': 'off',
|
||||||
'unicorn/prefer-event-target': 'off',
|
'unicorn/prefer-event-target': 'off',
|
||||||
'unicorn/no-thenable': 'off',
|
'unicorn/no-thenable': 'off',
|
||||||
|
'object-shorthand': ['error', 'always'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
Generated
+76
-134
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "1.113.0",
|
"version": "1.113.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "1.113.0",
|
"version": "1.113.1",
|
||||||
"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",
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||||
"@playwright/test": "^1.44.1",
|
"@playwright/test": "^1.44.1",
|
||||||
"@types/luxon": "^3.4.2",
|
"@types/luxon": "^3.4.2",
|
||||||
"@types/node": "^20.16.1",
|
"@types/node": "^20.16.2",
|
||||||
"@types/oidc-provider": "^8.5.1",
|
"@types/oidc-provider": "^8.5.1",
|
||||||
"@types/pg": "^8.11.0",
|
"@types/pg": "^8.11.0",
|
||||||
"@types/pngjs": "^6.0.4",
|
"@types/pngjs": "^6.0.4",
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
},
|
},
|
||||||
"../cli": {
|
"../cli": {
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.16",
|
"version": "2.2.17",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
"@types/cli-progress": "^3.11.0",
|
"@types/cli-progress": "^3.11.0",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/mock-fs": "^4.13.1",
|
"@types/mock-fs": "^4.13.1",
|
||||||
"@types/node": "^20.16.1",
|
"@types/node": "^20.16.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||||
"@typescript-eslint/parser": "^8.0.0",
|
"@typescript-eslint/parser": "^8.0.0",
|
||||||
"@vitest/coverage-v8": "^2.0.5",
|
"@vitest/coverage-v8": "^2.0.5",
|
||||||
@@ -92,14 +92,14 @@
|
|||||||
},
|
},
|
||||||
"../open-api/typescript-sdk": {
|
"../open-api/typescript-sdk": {
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.113.0",
|
"version": "1.113.1",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"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"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.16.1",
|
"@types/node": "^20.16.2",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -747,9 +747,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/config-array": {
|
"node_modules/@eslint/config-array": {
|
||||||
"version": "0.17.1",
|
"version": "0.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz",
|
||||||
"integrity": "sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==",
|
"integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -799,9 +799,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "9.9.0",
|
"version": "9.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.1.tgz",
|
||||||
"integrity": "sha512-hhetes6ZHP3BlXLxmd8K2SNgkhNSi+UcecbnwWKwpP7kyi/uC75DJ1lOOBO3xrC4jyojtGE3YxKZPHfk4yrgug==",
|
"integrity": "sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1516,9 +1516,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "20.16.1",
|
"version": "20.16.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.3.tgz",
|
||||||
"integrity": "sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ==",
|
"integrity": "sha512-/wdGiWRkMOm53gAsSyFMXFZHbVg7C6CbkrzHNpaHoYfsUWPg7m6ZRKtvQjgvQ9i8WT540a3ydRlRQbxjY30XxQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1543,10 +1543,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/pg": {
|
"node_modules/@types/pg": {
|
||||||
"version": "8.11.6",
|
"version": "8.11.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.8.tgz",
|
||||||
"integrity": "sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ==",
|
"integrity": "sha512-IqpCf8/569txXN/HoP5i1LjXfKZWL76Yr2R77xgeIICUbAYHeoaEZFhYHo2uDftecLWrTJUq63JvQu8q3lnDyA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"pg-protocol": "*",
|
"pg-protocol": "*",
|
||||||
@@ -1558,6 +1559,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.2.tgz",
|
||||||
"integrity": "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==",
|
"integrity": "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pg-int8": "1.0.1",
|
"pg-int8": "1.0.1",
|
||||||
"pg-numeric": "1.0.2",
|
"pg-numeric": "1.0.2",
|
||||||
@@ -1576,6 +1578,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz",
|
||||||
"integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==",
|
"integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
@@ -1585,6 +1588,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz",
|
||||||
"integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==",
|
"integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"obuf": "~1.1.2"
|
"obuf": "~1.1.2"
|
||||||
},
|
},
|
||||||
@@ -1597,6 +1601,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz",
|
||||||
"integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==",
|
"integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
@@ -1606,6 +1611,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz",
|
||||||
"integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==",
|
"integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
@@ -1674,17 +1680,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "8.2.0",
|
"version": "8.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.3.0.tgz",
|
||||||
"integrity": "sha512-02tJIs655em7fvt9gps/+4k4OsKULYGtLBPJfOsmOq1+3cdClYiF0+d6mHu6qDnTcg88wJBkcPLpQhq7FyDz0A==",
|
"integrity": "sha512-FLAIn63G5KH+adZosDYiutqkOkYEx0nvcwNNfJAf+c7Ae/H35qWwTYvPZUKFj5AS+WfHG/WJJfWnDnyNUlp8UA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/regexpp": "^4.10.0",
|
"@eslint-community/regexpp": "^4.10.0",
|
||||||
"@typescript-eslint/scope-manager": "8.2.0",
|
"@typescript-eslint/scope-manager": "8.3.0",
|
||||||
"@typescript-eslint/type-utils": "8.2.0",
|
"@typescript-eslint/type-utils": "8.3.0",
|
||||||
"@typescript-eslint/utils": "8.2.0",
|
"@typescript-eslint/utils": "8.3.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.2.0",
|
"@typescript-eslint/visitor-keys": "8.3.0",
|
||||||
"graphemer": "^1.4.0",
|
"graphemer": "^1.4.0",
|
||||||
"ignore": "^5.3.1",
|
"ignore": "^5.3.1",
|
||||||
"natural-compare": "^1.4.0",
|
"natural-compare": "^1.4.0",
|
||||||
@@ -1708,16 +1714,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "8.2.0",
|
"version": "8.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.3.0.tgz",
|
||||||
"integrity": "sha512-j3Di+o0lHgPrb7FxL3fdEy6LJ/j2NE8u+AP/5cQ9SKb+JLH6V6UHDqJ+e0hXBkHP1wn1YDFjYCS9LBQsZDlDEg==",
|
"integrity": "sha512-h53RhVyLu6AtpUzVCYLPhZGL5jzTD9fZL+SYf/+hYOx2bDkyQXztXSc4tbvKYHzfMXExMLiL9CWqJmVz6+78IQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.2.0",
|
"@typescript-eslint/scope-manager": "8.3.0",
|
||||||
"@typescript-eslint/types": "8.2.0",
|
"@typescript-eslint/types": "8.3.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.2.0",
|
"@typescript-eslint/typescript-estree": "8.3.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.2.0",
|
"@typescript-eslint/visitor-keys": "8.3.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1737,14 +1743,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/scope-manager": {
|
"node_modules/@typescript-eslint/scope-manager": {
|
||||||
"version": "8.2.0",
|
"version": "8.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.3.0.tgz",
|
||||||
"integrity": "sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==",
|
"integrity": "sha512-mz2X8WcN2nVu5Hodku+IR8GgCOl4C0G/Z1ruaWN4dgec64kDBabuXyPAr+/RgJtumv8EEkqIzf3X2U5DUKB2eg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.2.0",
|
"@typescript-eslint/types": "8.3.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.2.0"
|
"@typescript-eslint/visitor-keys": "8.3.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -1755,14 +1761,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/type-utils": {
|
"node_modules/@typescript-eslint/type-utils": {
|
||||||
"version": "8.2.0",
|
"version": "8.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.3.0.tgz",
|
||||||
"integrity": "sha512-g1CfXGFMQdT5S+0PSO0fvGXUaiSkl73U1n9LTK5aRAFnPlJ8dLKkXr4AaLFvPedW8lVDoMgLLE3JN98ZZfsj0w==",
|
"integrity": "sha512-wrV6qh//nLbfXZQoj32EXKmwHf4b7L+xXLrP3FZ0GOUU72gSvLjeWUl5J5Ue5IwRxIV1TfF73j/eaBapxx99Lg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/typescript-estree": "8.2.0",
|
"@typescript-eslint/typescript-estree": "8.3.0",
|
||||||
"@typescript-eslint/utils": "8.2.0",
|
"@typescript-eslint/utils": "8.3.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"ts-api-utils": "^1.3.0"
|
"ts-api-utils": "^1.3.0"
|
||||||
},
|
},
|
||||||
@@ -1780,9 +1786,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/types": {
|
"node_modules/@typescript-eslint/types": {
|
||||||
"version": "8.2.0",
|
"version": "8.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.3.0.tgz",
|
||||||
"integrity": "sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==",
|
"integrity": "sha512-y6sSEeK+facMaAyixM36dQ5NVXTnKWunfD1Ft4xraYqxP0lC0POJmIaL/mw72CUMqjY9qfyVfXafMeaUj0noWw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1794,16 +1800,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree": {
|
"node_modules/@typescript-eslint/typescript-estree": {
|
||||||
"version": "8.2.0",
|
"version": "8.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.3.0.tgz",
|
||||||
"integrity": "sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==",
|
"integrity": "sha512-Mq7FTHl0R36EmWlCJWojIC1qn/ZWo2YiWYc1XVtasJ7FIgjo0MVv9rZWXEE7IK2CGrtwe1dVOxWwqXUdNgfRCA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.2.0",
|
"@typescript-eslint/types": "8.3.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.2.0",
|
"@typescript-eslint/visitor-keys": "8.3.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"globby": "^11.1.0",
|
"fast-glob": "^3.3.2",
|
||||||
"is-glob": "^4.0.3",
|
"is-glob": "^4.0.3",
|
||||||
"minimatch": "^9.0.4",
|
"minimatch": "^9.0.4",
|
||||||
"semver": "^7.6.0",
|
"semver": "^7.6.0",
|
||||||
@@ -1849,16 +1855,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/utils": {
|
"node_modules/@typescript-eslint/utils": {
|
||||||
"version": "8.2.0",
|
"version": "8.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.3.0.tgz",
|
||||||
"integrity": "sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==",
|
"integrity": "sha512-F77WwqxIi/qGkIGOGXNBLV7nykwfjLsdauRB/DOFPdv6LTF3BHHkBpq81/b5iMPSF055oO2BiivDJV4ChvNtXA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.4.0",
|
"@eslint-community/eslint-utils": "^4.4.0",
|
||||||
"@typescript-eslint/scope-manager": "8.2.0",
|
"@typescript-eslint/scope-manager": "8.3.0",
|
||||||
"@typescript-eslint/types": "8.2.0",
|
"@typescript-eslint/types": "8.3.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.2.0"
|
"@typescript-eslint/typescript-estree": "8.3.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -1872,13 +1878,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/visitor-keys": {
|
"node_modules/@typescript-eslint/visitor-keys": {
|
||||||
"version": "8.2.0",
|
"version": "8.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.3.0.tgz",
|
||||||
"integrity": "sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==",
|
"integrity": "sha512-RmZwrTbQ9QveF15m/Cl28n0LXD6ea2CjkhH5rQ55ewz3H24w+AMCJHPVYaZ8/0HoG8Z3cLLFFycRXxeO2tz9FA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.2.0",
|
"@typescript-eslint/types": "8.3.0",
|
||||||
"eslint-visitor-keys": "^3.4.3"
|
"eslint-visitor-keys": "^3.4.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -2112,16 +2118,6 @@
|
|||||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/array-union": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/asap": {
|
"node_modules/asap": {
|
||||||
"version": "2.0.6",
|
"version": "2.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
|
||||||
@@ -2725,19 +2721,6 @@
|
|||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/dir-glob": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
|
||||||
"integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"path-type": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/eastasianwidth": {
|
"node_modules/eastasianwidth": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||||
@@ -2889,17 +2872,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "9.9.0",
|
"version": "9.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.1.tgz",
|
||||||
"integrity": "sha512-JfiKJrbx0506OEerjK2Y1QlldtBxkAlLxT5OEcRF8uaQ86noDe2k31Vw9rnSWv+MXZHj7OOUV/dA0AhdLFcyvA==",
|
"integrity": "sha512-dHvhrbfr4xFQ9/dq+jcVneZMyRYLjggWjk6RVsIiHsP8Rz6yZ8LvZ//iU4TrZF+SXWG+JkNF2OyiZRvzgRDqMg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.2.0",
|
||||||
"@eslint-community/regexpp": "^4.11.0",
|
"@eslint-community/regexpp": "^4.11.0",
|
||||||
"@eslint/config-array": "^0.17.1",
|
"@eslint/config-array": "^0.18.0",
|
||||||
"@eslint/eslintrc": "^3.1.0",
|
"@eslint/eslintrc": "^3.1.0",
|
||||||
"@eslint/js": "9.9.0",
|
"@eslint/js": "9.9.1",
|
||||||
"@humanwhocodes/module-importer": "^1.0.1",
|
"@humanwhocodes/module-importer": "^1.0.1",
|
||||||
"@humanwhocodes/retry": "^0.3.0",
|
"@humanwhocodes/retry": "^0.3.0",
|
||||||
"@nodelib/fs.walk": "^1.2.8",
|
"@nodelib/fs.walk": "^1.2.8",
|
||||||
@@ -3582,27 +3565,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/globby": {
|
|
||||||
"version": "11.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
|
|
||||||
"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"array-union": "^2.1.0",
|
|
||||||
"dir-glob": "^3.0.1",
|
|
||||||
"fast-glob": "^3.2.9",
|
|
||||||
"ignore": "^5.2.0",
|
|
||||||
"merge2": "^1.4.1",
|
|
||||||
"slash": "^3.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/gopd": {
|
"node_modules/gopd": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||||
@@ -4134,9 +4096,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jose": {
|
"node_modules/jose": {
|
||||||
"version": "5.7.0",
|
"version": "5.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/jose/-/jose-5.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/jose/-/jose-5.8.0.tgz",
|
||||||
"integrity": "sha512-3P9qfTYDVnNn642LCAqIKbTGb9a1TBxZ9ti5zEVEr48aDdflgRjhspWFb6WM4PzAfFbGMJYC4+803v8riCRAKw==",
|
"integrity": "sha512-E7CqYpL/t7MMnfGnK/eg416OsFCVUrU/Y3Vwe7QjKhu/BkS1Ms455+2xsqZQVN57/U2MHMBvEb5SrmAZWAIntA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
@@ -5044,16 +5006,6 @@
|
|||||||
"integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==",
|
"integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/path-type": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/pathe": {
|
"node_modules/pathe": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
|
||||||
@@ -5839,16 +5791,6 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/slash": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/socket.io-client": {
|
"node_modules/socket.io-client": {
|
||||||
"version": "4.7.5",
|
"version": "4.7.5",
|
||||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz",
|
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz",
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "1.113.0",
|
"version": "1.113.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||||
"@playwright/test": "^1.44.1",
|
"@playwright/test": "^1.44.1",
|
||||||
"@types/luxon": "^3.4.2",
|
"@types/luxon": "^3.4.2",
|
||||||
"@types/node": "^20.16.1",
|
"@types/node": "^20.16.2",
|
||||||
"@types/oidc-provider": "^8.5.1",
|
"@types/oidc-provider": "^8.5.1",
|
||||||
"@types/pg": "^8.11.0",
|
"@types/pg": "^8.11.0",
|
||||||
"@types/pngjs": "^6.0.4",
|
"@types/pngjs": "^6.0.4",
|
||||||
|
|||||||
@@ -635,10 +635,11 @@ describe('/libraries', () => {
|
|||||||
it('should remove offline files', async () => {
|
it('should remove offline files', async () => {
|
||||||
const library = await utils.createLibrary(admin.accessToken, {
|
const library = await utils.createLibrary(admin.accessToken, {
|
||||||
ownerId: admin.userId,
|
ownerId: admin.userId,
|
||||||
importPaths: [`${testAssetDirInternal}/temp/offline2`],
|
importPaths: [`${testAssetDirInternal}/temp/offline`],
|
||||||
});
|
});
|
||||||
|
|
||||||
utils.createImageFile(`${testAssetDir}/temp/offline2/assetA.png`);
|
utils.createImageFile(`${testAssetDir}/temp/offline/online.png`);
|
||||||
|
utils.createImageFile(`${testAssetDir}/temp/offline/offline.png`);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
||||||
@@ -646,9 +647,9 @@ describe('/libraries', () => {
|
|||||||
const { assets: initialAssets } = await utils.metadataSearch(admin.accessToken, {
|
const { assets: initialAssets } = await utils.metadataSearch(admin.accessToken, {
|
||||||
libraryId: library.id,
|
libraryId: library.id,
|
||||||
});
|
});
|
||||||
expect(initialAssets.count).toBe(1);
|
expect(initialAssets.count).toBe(2);
|
||||||
|
|
||||||
utils.removeImageFile(`${testAssetDir}/temp/offline2/assetA.png`);
|
utils.removeImageFile(`${testAssetDir}/temp/offline/offline.png`);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
||||||
@@ -669,7 +670,54 @@ describe('/libraries', () => {
|
|||||||
|
|
||||||
const { assets } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id });
|
const { assets } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
expect(assets.count).toBe(0);
|
expect(assets.count).toBe(1);
|
||||||
|
|
||||||
|
utils.removeImageFile(`${testAssetDir}/temp/offline/online.png`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove offline files from trash', async () => {
|
||||||
|
const library = await utils.createLibrary(admin.accessToken, {
|
||||||
|
ownerId: admin.userId,
|
||||||
|
importPaths: [`${testAssetDirInternal}/temp/offline`],
|
||||||
|
});
|
||||||
|
|
||||||
|
utils.createImageFile(`${testAssetDir}/temp/offline/online.png`);
|
||||||
|
utils.createImageFile(`${testAssetDir}/temp/offline/offline.png`);
|
||||||
|
|
||||||
|
await scan(admin.accessToken, library.id);
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
||||||
|
|
||||||
|
const { assets: initialAssets } = await utils.metadataSearch(admin.accessToken, {
|
||||||
|
libraryId: library.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(initialAssets.count).toBe(2);
|
||||||
|
utils.removeImageFile(`${testAssetDir}/temp/offline/offline.png`);
|
||||||
|
|
||||||
|
await scan(admin.accessToken, library.id);
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
||||||
|
|
||||||
|
const { assets: offlineAssets } = await utils.metadataSearch(admin.accessToken, {
|
||||||
|
libraryId: library.id,
|
||||||
|
isOffline: true,
|
||||||
|
});
|
||||||
|
expect(offlineAssets.count).toBe(1);
|
||||||
|
|
||||||
|
const { status } = await request(app)
|
||||||
|
.post(`/libraries/${library.id}/removeOffline`)
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send();
|
||||||
|
expect(status).toBe(204);
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, 'backgroundTask');
|
||||||
|
|
||||||
|
const { assets } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
|
expect(assets.count).toBe(1);
|
||||||
|
expect(assets.items[0].isOffline).toBe(false);
|
||||||
|
expect(assets.items[0].originalPath).toEqual(`${testAssetDirInternal}/temp/offline/online.png`);
|
||||||
|
|
||||||
|
utils.removeImageFile(`${testAssetDir}/temp/offline/online.png`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not remove online files', async () => {
|
it('should not remove online files', async () => {
|
||||||
@@ -803,4 +851,26 @@ describe('/libraries', () => {
|
|||||||
expect(existsSync(`${testAssetDir}/temp/directoryB/assetB.png`)).toBe(true);
|
expect(existsSync(`${testAssetDir}/temp/directoryB/assetB.png`)).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('POST /search/metadata', () => {
|
||||||
|
it('should search by originalPath', async () => {
|
||||||
|
const directory = `some-61498-directory`;
|
||||||
|
const infix = 'me-61498-di';
|
||||||
|
|
||||||
|
utils.createImageFile(`${testAssetDir}/temp/${directory}/assetZ.jpg`);
|
||||||
|
await scan(admin.accessToken, library.id);
|
||||||
|
await utils.waitForWebsocketEvent({ event: 'assetUpload', total: 1 });
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/search/metadata')
|
||||||
|
.send({ originalPath: infix })
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.assets).toBeDefined();
|
||||||
|
expect(Array.isArray(body.assets.items)).toBe(true);
|
||||||
|
expect(body.assets.items).toHaveLength(1);
|
||||||
|
expect(body.assets.items[0]).toEqual(expect.objectContaining({ originalFileName: 'assetZ.jpg' }));
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -288,13 +288,6 @@ describe('/search', () => {
|
|||||||
should: 'should search by takenAfter (no results)',
|
should: 'should search by takenAfter (no results)',
|
||||||
deferred: () => ({ dto: { takenAfter: today.plus({ hour: 1 }).toJSDate() }, assets: [] }),
|
deferred: () => ({ dto: { takenAfter: today.plus({ hour: 1 }).toJSDate() }, assets: [] }),
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// should: 'should search by originalPath',
|
|
||||||
// deferred: () => ({
|
|
||||||
// dto: { originalPath: asset1.originalPath },
|
|
||||||
// assets: [asset1],
|
|
||||||
// }),
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
should: 'should search by originalFilename',
|
should: 'should search by originalFilename',
|
||||||
deferred: () => ({
|
deferred: () => ({
|
||||||
|
|||||||
@@ -42,6 +42,23 @@ describe('/trash', () => {
|
|||||||
const after = await getAssetStatistics({ isTrashed: true }, { headers: asBearerAuth(admin.accessToken) });
|
const after = await getAssetStatistics({ isTrashed: true }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
expect(after.total).toBe(0);
|
expect(after.total).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should empty the trash with archived assets', async () => {
|
||||||
|
const { id: assetId } = await utils.createAsset(admin.accessToken);
|
||||||
|
await utils.archiveAssets(admin.accessToken, [assetId]);
|
||||||
|
await utils.deleteAssets(admin.accessToken, [assetId]);
|
||||||
|
|
||||||
|
const before = await getAssetInfo({ id: assetId }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
expect(before).toStrictEqual(expect.objectContaining({ id: assetId, isTrashed: true, isArchived: true }));
|
||||||
|
|
||||||
|
const { status } = await request(app).post('/trash/empty').set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
expect(status).toBe(204);
|
||||||
|
|
||||||
|
await utils.waitForWebsocketEvent({ event: 'assetDelete', id: assetId });
|
||||||
|
|
||||||
|
const after = await getAssetStatistics({ isTrashed: true }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
expect(after.total).toBe(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /trash/restore', () => {
|
describe('POST /trash/restore', () => {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import {
|
|||||||
signUpAdmin,
|
signUpAdmin,
|
||||||
updateAdminOnboarding,
|
updateAdminOnboarding,
|
||||||
updateAlbumUser,
|
updateAlbumUser,
|
||||||
|
updateAssets,
|
||||||
updateConfig,
|
updateConfig,
|
||||||
validate,
|
validate,
|
||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
@@ -389,6 +390,9 @@ export const utils = {
|
|||||||
return searchMetadata({ metadataSearchDto: dto }, { headers: asBearerAuth(accessToken) });
|
return searchMetadata({ metadataSearchDto: dto }, { headers: asBearerAuth(accessToken) });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
archiveAssets: (accessToken: string, ids: string[]) =>
|
||||||
|
updateAssets({ assetBulkUpdateDto: { ids, isArchived: true } }, { headers: asBearerAuth(accessToken) }),
|
||||||
|
|
||||||
deleteAssets: (accessToken: string, ids: string[]) =>
|
deleteAssets: (accessToken: string, ids: string[]) =>
|
||||||
deleteAssets({ assetBulkDeleteDto: { ids } }, { headers: asBearerAuth(accessToken) }),
|
deleteAssets({ assetBulkDeleteDto: { ids } }, { headers: asBearerAuth(accessToken) }),
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { LoginResponseDto } from '@immich/sdk';
|
||||||
|
import { test } from '@playwright/test';
|
||||||
|
import { utils } from 'src/utils';
|
||||||
|
|
||||||
|
test.describe('Album', () => {
|
||||||
|
let admin: LoginResponseDto;
|
||||||
|
|
||||||
|
test.beforeAll(async () => {
|
||||||
|
utils.initSdk();
|
||||||
|
await utils.resetDatabase();
|
||||||
|
admin = await utils.adminSetup();
|
||||||
|
});
|
||||||
|
|
||||||
|
test(`doesn't delete album after canceling add assets`, async ({ context, page }) => {
|
||||||
|
await utils.setAuthCookies(context, admin.accessToken);
|
||||||
|
|
||||||
|
await page.goto('/albums');
|
||||||
|
await page.getByRole('button', { name: 'Create album' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Select photos' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Close' }).click();
|
||||||
|
|
||||||
|
await page.reload();
|
||||||
|
await page.getByRole('button', { name: 'Select photos' }).waitFor();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "machine-learning"
|
name = "machine-learning"
|
||||||
version = "1.113.0"
|
version = "1.113.1"
|
||||||
description = ""
|
description = ""
|
||||||
authors = ["Hau Tran <alex.tran1502@gmail.com>"]
|
authors = ["Hau Tran <alex.tran1502@gmail.com>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
<application android:label="Immich" android:name=".ImmichApp" android:usesCleartextTraffic="true"
|
<application android:label="Immich" android:name=".ImmichApp" android:usesCleartextTraffic="true"
|
||||||
android:icon="@mipmap/ic_launcher" android:requestLegacyExternalStorage="true"
|
android:icon="@mipmap/ic_launcher" android:requestLegacyExternalStorage="true"
|
||||||
android:largeHeap="true" android:enableOnBackInvokedCallback="true">
|
android:largeHeap="true" android:enableOnBackInvokedCallback="false">
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name="androidx.work.impl.foreground.SystemForegroundService"
|
android:name="androidx.work.impl.foreground.SystemForegroundService"
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
<data android:scheme="app.immich" android:pathPrefix="/oauth-callback"/>
|
<data android:scheme="app.immich" android:pathPrefix="/oauth-callback" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<!-- Don't delete the meta-data below.
|
<!-- Don't delete the meta-data below.
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ platform :android do
|
|||||||
task: 'bundle',
|
task: 'bundle',
|
||||||
build_type: 'Release',
|
build_type: 'Release',
|
||||||
properties: {
|
properties: {
|
||||||
"android.injected.version.code" => 155,
|
"android.injected.version.code" => 157,
|
||||||
"android.injected.version.name" => "1.113.0",
|
"android.injected.version.name" => "1.113.1",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
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')
|
||||||
|
|||||||
@@ -155,7 +155,7 @@
|
|||||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
D218A34AEE62BC1EF119F5B0 /* [CP] Embed Pods Frameworks */,
|
D218A34AEE62BC1EF119F5B0 /* [CP] Embed Pods Frameworks */,
|
||||||
C494C1A226E78FAB736DAB6C /* [CP] Copy Pods Resources */,
|
6724EEB7D74949FA08581154 /* [CP] Copy Pods Resources */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@@ -253,22 +253,7 @@
|
|||||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
6724EEB7D74949FA08581154 /* [CP] Copy Pods Resources */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
alwaysOutOfDate = 1;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
);
|
|
||||||
name = "Run Script";
|
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
|
|
||||||
};
|
|
||||||
C494C1A226E78FAB736DAB6C /* [CP] Copy Pods Resources */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
@@ -285,6 +270,21 @@
|
|||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
|
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
alwaysOutOfDate = 1;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Run Script";
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
|
||||||
|
};
|
||||||
D218A34AEE62BC1EF119F5B0 /* [CP] Embed Pods Frameworks */ = {
|
D218A34AEE62BC1EF119F5B0 /* [CP] Embed Pods Frameworks */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@@ -401,7 +401,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 = 169;
|
CURRENT_PROJECT_VERSION = 172;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@@ -543,7 +543,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 169;
|
CURRENT_PROJECT_VERSION = 172;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@@ -571,7 +571,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 169;
|
CURRENT_PROJECT_VERSION = 172;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||||
|
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="iOS"/>
|
<deployment identifier="iOS"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
<!--Flutter View Controller-->
|
<!--Flutter View Controller-->
|
||||||
@@ -14,13 +16,14 @@
|
|||||||
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||||
</layoutGuides>
|
</layoutGuides>
|
||||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</view>
|
</view>
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
|
<point key="canvasLocation" x="68" y="-2"/>
|
||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
</document>
|
</document>
|
||||||
|
|||||||
@@ -58,11 +58,11 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.112.1</string>
|
<string>1.113.1</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>169</string>
|
<string>172</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.113.0"
|
version_number: "1.113.1"
|
||||||
)
|
)
|
||||||
increment_build_number(
|
increment_build_number(
|
||||||
build_number: latest_testflight_build_number + 1,
|
build_number: latest_testflight_build_number + 1,
|
||||||
|
|||||||
@@ -349,6 +349,7 @@ class BackgroundService {
|
|||||||
Future<bool> _onAssetsChanged() async {
|
Future<bool> _onAssetsChanged() async {
|
||||||
final Isar db = await loadDb();
|
final Isar db = await loadDb();
|
||||||
|
|
||||||
|
HttpOverrides.global = HttpSSLCertOverride();
|
||||||
ApiService apiService = ApiService();
|
ApiService apiService = ApiService();
|
||||||
apiService.setAccessToken(Store.get(StoreKey.accessToken));
|
apiService.setAccessToken(Store.get(StoreKey.accessToken));
|
||||||
AppSettingsService settingService = AppSettingsService();
|
AppSettingsService settingService = AppSettingsService();
|
||||||
|
|||||||
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.113.0
|
- API version: 1.113.1
|
||||||
- Generator version: 7.5.0
|
- Generator version: 7.5.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.113.0+155
|
version: 1.113.1+157
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.3.0 <4.0.0'
|
sdk: '>=3.3.0 <4.0.0'
|
||||||
|
|||||||
@@ -7394,7 +7394,7 @@
|
|||||||
"info": {
|
"info": {
|
||||||
"title": "Immich",
|
"title": "Immich",
|
||||||
"description": "Immich API",
|
"description": "Immich API",
|
||||||
"version": "1.113.0",
|
"version": "1.113.1",
|
||||||
"contact": {}
|
"contact": {}
|
||||||
},
|
},
|
||||||
"tags": [],
|
"tags": [],
|
||||||
|
|||||||
+6
-6
@@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.113.0",
|
"version": "1.113.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.113.0",
|
"version": "1.113.1",
|
||||||
"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"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.16.1",
|
"@types/node": "^20.16.2",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -22,9 +22,9 @@
|
|||||||
"integrity": "sha512-8tKiYffhwTGHSHYGnZ3oneLGCjX0po/XAXQ5Ng9fqKkvIdl/xz8+Vh8i+6xjzZqvZ2pLVpUcuSfnvNI/x67L0g=="
|
"integrity": "sha512-8tKiYffhwTGHSHYGnZ3oneLGCjX0po/XAXQ5Ng9fqKkvIdl/xz8+Vh8i+6xjzZqvZ2pLVpUcuSfnvNI/x67L0g=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "20.16.1",
|
"version": "20.16.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.3.tgz",
|
||||||
"integrity": "sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ==",
|
"integrity": "sha512-/wdGiWRkMOm53gAsSyFMXFZHbVg7C6CbkrzHNpaHoYfsUWPg7m6ZRKtvQjgvQ9i8WT540a3ydRlRQbxjY30XxQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.113.0",
|
"version": "1.113.1",
|
||||||
"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",
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
"@oazapfts/runtime": "^1.0.2"
|
"@oazapfts/runtime": "^1.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.16.1",
|
"@types/node": "^20.16.2",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Immich
|
* Immich
|
||||||
* 1.113.0
|
* 1.113.1
|
||||||
* 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
|
||||||
*/
|
*/
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
# dev build
|
# dev build
|
||||||
FROM ghcr.io/immich-app/base-server-dev:20240827@sha256:c882c0a354faaac4f7256d30ecc2c45435eafa9b64d60793a171abb74ec5ca95 AS dev
|
FROM ghcr.io/immich-app/base-server-dev:20240903@sha256:ca18e2805ec8ddcf0ac7734a6eaf6d9a08bd3a14218bf0dbdbe865d83117190f AS dev
|
||||||
|
|
||||||
RUN apt-get install --no-install-recommends -yqq tini
|
RUN apt-get install --no-install-recommends -yqq tini
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
@@ -41,7 +41,7 @@ RUN npm run build
|
|||||||
|
|
||||||
|
|
||||||
# prod build
|
# prod build
|
||||||
FROM ghcr.io/immich-app/base-server-prod:20240827@sha256:72a419dd703b0f530c43f3e00f3aa56be9efb61b4eb9fe911bae8c6f98237967
|
FROM ghcr.io/immich-app/base-server-prod:20240903@sha256:d0d170ceeee7ef6c7b62b5d927820d74c14a9893f3e6285c1b9df45b33951b09
|
||||||
|
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
ENV NODE_ENV=production \
|
ENV NODE_ENV=production \
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ export default [
|
|||||||
'@typescript-eslint/require-await': 'error',
|
'@typescript-eslint/require-await': 'error',
|
||||||
curly: 2,
|
curly: 2,
|
||||||
'prettier/prettier': 0,
|
'prettier/prettier': 0,
|
||||||
|
'object-shorthand': ['error', 'always'],
|
||||||
|
|
||||||
'no-restricted-imports': [
|
'no-restricted-imports': [
|
||||||
'error',
|
'error',
|
||||||
|
|||||||
Generated
+1895
-538
File diff suppressed because it is too large
Load Diff
+5
-5
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich",
|
"name": "immich",
|
||||||
"version": "1.113.0",
|
"version": "1.113.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"author": "",
|
"author": "",
|
||||||
"private": true,
|
"private": true,
|
||||||
@@ -48,9 +48,9 @@
|
|||||||
"@nestjs/websockets": "^10.2.2",
|
"@nestjs/websockets": "^10.2.2",
|
||||||
"@opentelemetry/auto-instrumentations-node": "^0.49.0",
|
"@opentelemetry/auto-instrumentations-node": "^0.49.0",
|
||||||
"@opentelemetry/context-async-hooks": "^1.24.0",
|
"@opentelemetry/context-async-hooks": "^1.24.0",
|
||||||
"@opentelemetry/exporter-prometheus": "^0.52.0",
|
"@opentelemetry/exporter-prometheus": "^0.53.0",
|
||||||
"@opentelemetry/sdk-node": "^0.52.0",
|
"@opentelemetry/sdk-node": "^0.53.0",
|
||||||
"@react-email/components": "^0.0.23",
|
"@react-email/components": "^0.0.24",
|
||||||
"@socket.io/redis-adapter": "^8.3.0",
|
"@socket.io/redis-adapter": "^8.3.0",
|
||||||
"archiver": "^7.0.0",
|
"archiver": "^7.0.0",
|
||||||
"async-lock": "^1.4.0",
|
"async-lock": "^1.4.0",
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
"@types/lodash": "^4.14.197",
|
"@types/lodash": "^4.14.197",
|
||||||
"@types/mock-fs": "^4.13.1",
|
"@types/mock-fs": "^4.13.1",
|
||||||
"@types/multer": "^1.4.7",
|
"@types/multer": "^1.4.7",
|
||||||
"@types/node": "^20.16.1",
|
"@types/node": "^20.16.2",
|
||||||
"@types/nodemailer": "^6.4.14",
|
"@types/nodemailer": "^6.4.14",
|
||||||
"@types/picomatch": "^3.0.0",
|
"@types/picomatch": "^3.0.0",
|
||||||
"@types/react": "^18.3.4",
|
"@types/react": "^18.3.4",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export class CreateProfileImageResponseDto {
|
|||||||
|
|
||||||
export function mapCreateProfileImageResponse(userId: string, profileImagePath: string): CreateProfileImageResponseDto {
|
export function mapCreateProfileImageResponse(userId: string, profileImagePath: string): CreateProfileImageResponseDto {
|
||||||
return {
|
return {
|
||||||
userId: userId,
|
userId,
|
||||||
profileImagePath: profileImagePath,
|
profileImagePath,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ export const ASSET_CHECKSUM_CONSTRAINT = 'UQ_assets_owner_checksum';
|
|||||||
@Index('IDX_originalPath_libraryId', ['originalPath', 'libraryId'])
|
@Index('IDX_originalPath_libraryId', ['originalPath', 'libraryId'])
|
||||||
@Index('IDX_asset_id_stackId', ['id', 'stackId'])
|
@Index('IDX_asset_id_stackId', ['id', 'stackId'])
|
||||||
@Index('idx_originalFileName_trigram', { synchronize: false })
|
@Index('idx_originalFileName_trigram', { synchronize: false })
|
||||||
// For all assets, each originalpath must be unique per user and library
|
@Index('idx_originalPath_trigram', { synchronize: false })
|
||||||
|
// For all assets, each originalPath must be unique per user and library
|
||||||
export class AssetEntity {
|
export class AssetEntity {
|
||||||
@PrimaryGeneratedColumn('uuid')
|
@PrimaryGeneratedColumn('uuid')
|
||||||
id!: string;
|
id!: string;
|
||||||
|
|||||||
@@ -169,7 +169,12 @@ export interface IAssetRepository {
|
|||||||
order?: FindOptionsOrder<AssetEntity>,
|
order?: FindOptionsOrder<AssetEntity>,
|
||||||
): Promise<AssetEntity | null>;
|
): Promise<AssetEntity | null>;
|
||||||
getWithout(pagination: PaginationOptions, property: WithoutProperty): Paginated<AssetEntity>;
|
getWithout(pagination: PaginationOptions, property: WithoutProperty): Paginated<AssetEntity>;
|
||||||
getWith(pagination: PaginationOptions, property: WithProperty, libraryId?: string): Paginated<AssetEntity>;
|
getWith(
|
||||||
|
pagination: PaginationOptions,
|
||||||
|
property: WithProperty,
|
||||||
|
libraryId?: string,
|
||||||
|
withDeleted?: boolean,
|
||||||
|
): Paginated<AssetEntity>;
|
||||||
getRandom(userId: string, count: number): Promise<AssetEntity[]>;
|
getRandom(userId: string, count: number): Promise<AssetEntity[]>;
|
||||||
getFirstAssetForAlbumId(albumId: string): Promise<AssetEntity | null>;
|
getFirstAssetForAlbumId(albumId: string): Promise<AssetEntity | null>;
|
||||||
getLastUpdatedAssetForAlbumId(albumId: string): Promise<AssetEntity | null>;
|
getLastUpdatedAssetForAlbumId(albumId: string): Promise<AssetEntity | null>;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export class AddAssetChecksum1661881837496 implements MigrationInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_64c507300988dd1764f9a6530c"`);
|
await queryRunner.query(`DROP INDEX "IDX_64c507300988dd1764f9a6530c"`);
|
||||||
await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "checksum"`);
|
await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "checksum"`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ export class CreateTagsTable1670257571385 implements MigrationInterface {
|
|||||||
await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42"`);
|
await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42"`);
|
||||||
await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9"`);
|
await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9"`);
|
||||||
await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "FK_92e67dc508c705dd66c94615576"`);
|
await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "FK_92e67dc508c705dd66c94615576"`);
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_e99f31ea4cdf3a2c35c7287eb4"`);
|
await queryRunner.query(`DROP INDEX "IDX_e99f31ea4cdf3a2c35c7287eb4"`);
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_f8e8a9e893cb5c54907f1b798e"`);
|
await queryRunner.query(`DROP INDEX "IDX_f8e8a9e893cb5c54907f1b798e"`);
|
||||||
await queryRunner.query(`DROP TABLE "tag_asset"`);
|
await queryRunner.query(`DROP TABLE "tag_asset"`);
|
||||||
await queryRunner.query(`DROP TABLE "tags"`);
|
await queryRunner.query(`DROP TABLE "tags"`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,10 @@ export class AddSharedLinkTable1673150490490 implements MigrationInterface {
|
|||||||
await queryRunner.query(`ALTER TABLE "shared_link__asset" DROP CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab"`);
|
await queryRunner.query(`ALTER TABLE "shared_link__asset" DROP CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab"`);
|
||||||
await queryRunner.query(`ALTER TABLE "shared_link__asset" DROP CONSTRAINT "FK_5b7decce6c8d3db9593d6111a66"`);
|
await queryRunner.query(`ALTER TABLE "shared_link__asset" DROP CONSTRAINT "FK_5b7decce6c8d3db9593d6111a66"`);
|
||||||
await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66"`);
|
await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66"`);
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_c9fab4aa97ffd1b034f3d6581a"`);
|
await queryRunner.query(`DROP INDEX "IDX_c9fab4aa97ffd1b034f3d6581a"`);
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_5b7decce6c8d3db9593d6111a6"`);
|
await queryRunner.query(`DROP INDEX "IDX_5b7decce6c8d3db9593d6111a6"`);
|
||||||
await queryRunner.query(`DROP TABLE "shared_link__asset"`);
|
await queryRunner.query(`DROP TABLE "shared_link__asset"`);
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_sharedlink_key"`);
|
await queryRunner.query(`DROP INDEX "IDX_sharedlink_key"`);
|
||||||
await queryRunner.query(`DROP TABLE "shared_links"`);
|
await queryRunner.query(`DROP TABLE "shared_links"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,10 +44,10 @@ export class FixAlbumEntityTypeORM1675812532822 implements MigrationInterface {
|
|||||||
await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_4bd1303d199f4e72ccdf998c621"`);
|
await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_4bd1303d199f4e72ccdf998c621"`);
|
||||||
await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "FK_427c350ad49bd3935a50baab737"`);
|
await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "FK_427c350ad49bd3935a50baab737"`);
|
||||||
await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "FK_f48513bf9bccefd6ff3ad30bd06"`);
|
await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "FK_f48513bf9bccefd6ff3ad30bd06"`);
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_427c350ad49bd3935a50baab73"`);
|
await queryRunner.query(`DROP INDEX "IDX_427c350ad49bd3935a50baab73"`);
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_f48513bf9bccefd6ff3ad30bd0"`);
|
await queryRunner.query(`DROP INDEX "IDX_f48513bf9bccefd6ff3ad30bd0"`);
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_e590fa396c6898fcd4a50e4092"`);
|
await queryRunner.query(`DROP INDEX "IDX_e590fa396c6898fcd4a50e4092"`);
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_4bd1303d199f4e72ccdf998c62"`);
|
await queryRunner.query(`DROP INDEX "IDX_4bd1303d199f4e72ccdf998c62"`);
|
||||||
|
|
||||||
await queryRunner.query(`ALTER TABLE "albums" DROP CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4"`);
|
await queryRunner.query(`ALTER TABLE "albums" DROP CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4"`);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export class AppleContentIdentifier1676437878377 implements MigrationInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_live_photo_cid"`);
|
await queryRunner.query(`DROP INDEX "IDX_live_photo_cid"`);
|
||||||
await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "livePhotoCID"`);
|
await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "livePhotoCID"`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export class ExifEntityDefinitionFixes1676848629119 implements MigrationInterfac
|
|||||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "description" SET NOT NULL`);
|
await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "description" SET NOT NULL`);
|
||||||
|
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_c0117fdbc50b917ef9067740c4"`);
|
await queryRunner.query(`DROP INDEX "IDX_c0117fdbc50b917ef9067740c4"`);
|
||||||
await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "PK_28663352d85078ad0046dafafaa"`);
|
await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "PK_28663352d85078ad0046dafafaa"`);
|
||||||
await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "id"`);
|
await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "id"`);
|
||||||
await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "FK_c0117fdbc50b917ef9067740c44"`);
|
await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "FK_c0117fdbc50b917ef9067740c44"`);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export class SmartInfoEntityDefinitionFixes1676852143506 implements MigrationInt
|
|||||||
name = 'SmartInfoEntityDefinitionFixes1676852143506'
|
name = 'SmartInfoEntityDefinitionFixes1676852143506'
|
||||||
|
|
||||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_5e3753aadd956110bf3ec0244a"`);
|
await queryRunner.query(`DROP INDEX "IDX_5e3753aadd956110bf3ec0244a"`);
|
||||||
await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "PK_0beace66440e9713f5c40470e46"`);
|
await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "PK_0beace66440e9713f5c40470e46"`);
|
||||||
await queryRunner.query(`ALTER TABLE "smart_info" DROP COLUMN "id"`);
|
await queryRunner.query(`ALTER TABLE "smart_info" DROP COLUMN "id"`);
|
||||||
await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "FK_5e3753aadd956110bf3ec0244ac"`);
|
await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "FK_5e3753aadd956110bf3ec0244ac"`);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export class AddIndexForAlbumInSharedLinkTable1677535643119 implements Migration
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_sharedlink_albumId"`);
|
await queryRunner.query(`DROP INDEX "IDX_sharedlink_albumId"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ export class RequireChecksumNotNull1684328185099 implements MigrationInterface {
|
|||||||
name = 'removeNotNullFromChecksumIndex1684328185099';
|
name = 'removeNotNullFromChecksumIndex1684328185099';
|
||||||
|
|
||||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_64c507300988dd1764f9a6530c"`);
|
await queryRunner.query(`DROP INDEX "IDX_64c507300988dd1764f9a6530c"`);
|
||||||
await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "checksum" SET NOT NULL`);
|
await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "checksum" SET NOT NULL`);
|
||||||
await queryRunner.query(`CREATE INDEX "IDX_8d3efe36c0755849395e6ea866" ON "assets" ("checksum") `);
|
await queryRunner.query(`CREATE INDEX "IDX_8d3efe36c0755849395e6ea866" ON "assets" ("checksum") `);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_8d3efe36c0755849395e6ea866"`);
|
await queryRunner.query(`DROP INDEX "IDX_8d3efe36c0755849395e6ea866"`);
|
||||||
await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "checksum" DROP NOT NULL`);
|
await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "checksum" DROP NOT NULL`);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`CREATE INDEX "IDX_64c507300988dd1764f9a6530c" ON "assets" ("checksum") WHERE ('checksum' IS NOT NULL)`,
|
`CREATE INDEX "IDX_64c507300988dd1764f9a6530c" ON "assets" ("checksum") WHERE ('checksum' IS NOT NULL)`,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export class AddAuditTable1692804658140 implements MigrationInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_ownerId_createdAt"`);
|
await queryRunner.query(`DROP INDEX "IDX_ownerId_createdAt"`);
|
||||||
await queryRunner.query(`DROP TABLE "audit"`);
|
await queryRunner.query(`DROP TABLE "audit"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,6 @@ export class AddOriginalPathIndex1696888644031 implements MigrationInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_originalPath_libraryId"`);
|
await queryRunner.query(`DROP INDEX "IDX_originalPath_libraryId"`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export class AddActivity1698693294632 implements MigrationInterface {
|
|||||||
await queryRunner.query(`ALTER TABLE "activity" DROP CONSTRAINT "FK_1af8519996fbfb3684b58df280b"`);
|
await queryRunner.query(`ALTER TABLE "activity" DROP CONSTRAINT "FK_1af8519996fbfb3684b58df280b"`);
|
||||||
await queryRunner.query(`ALTER TABLE "activity" DROP CONSTRAINT "FK_3571467bcbe021f66e2bdce96ea"`);
|
await queryRunner.query(`ALTER TABLE "activity" DROP CONSTRAINT "FK_3571467bcbe021f66e2bdce96ea"`);
|
||||||
await queryRunner.query(`ALTER TABLE "activity" DROP CONSTRAINT "FK_8091ea76b12338cb4428d33d782"`);
|
await queryRunner.query(`ALTER TABLE "activity" DROP CONSTRAINT "FK_8091ea76b12338cb4428d33d782"`);
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_activity_like"`);
|
await queryRunner.query(`DROP INDEX "IDX_activity_like"`);
|
||||||
await queryRunner.query(`DROP TABLE "activity"`);
|
await queryRunner.query(`DROP TABLE "activity"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ export class AddAssetFaceIndicies1700752078178 implements MigrationInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_b463c8edb01364bf2beba08ef1"`);
|
await queryRunner.query(`DROP INDEX "IDX_b463c8edb01364bf2beba08ef1"`);
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_bf339a24070dac7e71304ec530"`);
|
await queryRunner.query(`DROP INDEX "IDX_bf339a24070dac7e71304ec530"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export class AddExifCityIndex1701665867595 implements MigrationInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
await queryRunner.query(`DROP INDEX "public"."exif_city"`);
|
await queryRunner.query(`DROP INDEX "exif_city"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export class AddAutoStackId1703035138085 implements MigrationInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_auto_stack_id"`);
|
await queryRunner.query(`DROP INDEX "IDX_auto_stack_id"`);
|
||||||
await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "autoStackId"`);
|
await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "autoStackId"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,6 @@ export class AddOriginalFileNameIndex1705306747072 implements MigrationInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_4d66e76dada1ca180f67a205dc"`);
|
await queryRunner.query(`DROP INDEX "IDX_4d66e76dada1ca180f67a205dc"`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export class CreateAssetStackTable1705197515600 implements MigrationInterface {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// update constraints
|
// update constraints
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_b463c8edb01364bf2beba08ef1"`);
|
await queryRunner.query(`DROP INDEX "IDX_b463c8edb01364bf2beba08ef1"`);
|
||||||
await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_b463c8edb01364bf2beba08ef19"`);
|
await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_b463c8edb01364bf2beba08ef19"`);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`ALTER TABLE "assets" ADD CONSTRAINT "FK_f15d48fa3ea5e4bda05ca8ab207" FOREIGN KEY ("stackId") REFERENCES "asset_stack"("id") ON DELETE SET NULL ON UPDATE CASCADE`,
|
`ALTER TABLE "assets" ADD CONSTRAINT "FK_f15d48fa3ea5e4bda05ca8ab207" FOREIGN KEY ("stackId") REFERENCES "asset_stack"("id") ON DELETE SET NULL ON UPDATE CASCADE`,
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ export class AddMemoryTable1711637874206 implements MigrationInterface {
|
|||||||
await queryRunner.query(`ALTER TABLE "memories_assets_assets" DROP CONSTRAINT "FK_6942ecf52d75d4273de19d2c16f"`);
|
await queryRunner.query(`ALTER TABLE "memories_assets_assets" DROP CONSTRAINT "FK_6942ecf52d75d4273de19d2c16f"`);
|
||||||
await queryRunner.query(`ALTER TABLE "memories_assets_assets" DROP CONSTRAINT "FK_984e5c9ab1f04d34538cd32334e"`);
|
await queryRunner.query(`ALTER TABLE "memories_assets_assets" DROP CONSTRAINT "FK_984e5c9ab1f04d34538cd32334e"`);
|
||||||
await queryRunner.query(`ALTER TABLE "memories" DROP CONSTRAINT "FK_575842846f0c28fa5da46c99b19"`);
|
await queryRunner.query(`ALTER TABLE "memories" DROP CONSTRAINT "FK_575842846f0c28fa5da46c99b19"`);
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_6942ecf52d75d4273de19d2c16"`);
|
await queryRunner.query(`DROP INDEX "IDX_6942ecf52d75d4273de19d2c16"`);
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_984e5c9ab1f04d34538cd32334"`);
|
await queryRunner.query(`DROP INDEX "IDX_984e5c9ab1f04d34538cd32334"`);
|
||||||
await queryRunner.query(`DROP TABLE "memories_assets_assets"`);
|
await queryRunner.query(`DROP TABLE "memories_assets_assets"`);
|
||||||
await queryRunner.query(`DROP TABLE "memories"`);
|
await queryRunner.query(`DROP TABLE "memories"`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ export class RemoveLibraryType1715804005643 implements MigrationInterface {
|
|||||||
|
|
||||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_9977c3c1de01c3d848039a6b90c"`);
|
await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_9977c3c1de01c3d848039a6b90c"`);
|
||||||
await queryRunner.query(`DROP INDEX "public"."UQ_assets_owner_library_checksum"`);
|
await queryRunner.query(`DROP INDEX "UQ_assets_owner_library_checksum"`);
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_originalPath_libraryId"`);
|
await queryRunner.query(`DROP INDEX "IDX_originalPath_libraryId"`);
|
||||||
await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "libraryId" DROP NOT NULL`);
|
await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "libraryId" DROP NOT NULL`);
|
||||||
await queryRunner.query(`
|
await queryRunner.query(`
|
||||||
UPDATE "assets"
|
UPDATE "assets"
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export class AddAssetFilesTable1724101822106 implements MigrationInterface {
|
|||||||
await queryRunner.query(`UPDATE "assets" SET "thumbnailPath" = "asset_files".path FROM "asset_files" WHERE "assets".id = "asset_files".assetId AND "asset_files".type = 'thumbnail'`);
|
await queryRunner.query(`UPDATE "assets" SET "thumbnailPath" = "asset_files".path FROM "asset_files" WHERE "assets".id = "asset_files".assetId AND "asset_files".type = 'thumbnail'`);
|
||||||
|
|
||||||
await queryRunner.query(`ALTER TABLE "asset_files" DROP CONSTRAINT "FK_e3e103a5f1d8bc8402999286040"`);
|
await queryRunner.query(`ALTER TABLE "asset_files" DROP CONSTRAINT "FK_e3e103a5f1d8bc8402999286040"`);
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_asset_files_assetId"`);
|
await queryRunner.query(`DROP INDEX "IDX_asset_files_assetId"`);
|
||||||
await queryRunner.query(`DROP TABLE "asset_files"`);
|
await queryRunner.query(`DROP TABLE "asset_files"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class AddAssetOriginalPathTrigramIndex1724231348454 implements MigrationInterface {
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`CREATE INDEX idx_originalPath_trigram ON assets USING gin (f_unaccent("originalPath") gin_trgm_ops)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`DROP INDEX "idx_originalPath_trigram"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -47,8 +47,8 @@ export class NestedTagTable1724790460210 implements MigrationInterface {
|
|||||||
await queryRunner.query(`ALTER TABLE "tags" ADD "name" character varying NOT NULL`);
|
await queryRunner.query(`ALTER TABLE "tags" ADD "name" character varying NOT NULL`);
|
||||||
await queryRunner.query(`ALTER TABLE "tags" ADD "type" character varying NOT NULL`);
|
await queryRunner.query(`ALTER TABLE "tags" ADD "type" character varying NOT NULL`);
|
||||||
await queryRunner.query(`ALTER TABLE "tags" ADD "renameTagId" uuid`);
|
await queryRunner.query(`ALTER TABLE "tags" ADD "renameTagId" uuid`);
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_b1a2a7ed45c29179b5ad51548a"`);
|
await queryRunner.query(`DROP INDEX "IDX_b1a2a7ed45c29179b5ad51548a"`);
|
||||||
await queryRunner.query(`DROP INDEX "public"."IDX_15fbcbc67663c6bfc07b354c22"`);
|
await queryRunner.query(`DROP INDEX "IDX_15fbcbc67663c6bfc07b354c22"`);
|
||||||
await queryRunner.query(`DROP TABLE "tags_closure"`);
|
await queryRunner.query(`DROP TABLE "tags_closure"`);
|
||||||
await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_tag_name_userId" UNIQUE ("name", "userId")`);
|
await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_tag_name_userId" UNIQUE ("name", "userId")`);
|
||||||
await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "FK_92e67dc508c705dd66c94615576" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "FK_92e67dc508c705dd66c94615576" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
export class UpsertMissingAssetJobStatus1725258039306 implements MigrationInterface {
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`INSERT INTO "asset_job_status" ("assetId", "facesRecognizedAt", "metadataExtractedAt", "duplicatesDetectedAt", "previewAt", "thumbnailAt") SELECT "assetId", NULL, NULL, NULL, NULL, NULL FROM "asset_files" f WHERE "f"."path" IS NOT NULL ON CONFLICT DO NOTHING`,
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.query(
|
||||||
|
`UPDATE "asset_job_status" SET "previewAt" = NOW() FROM "asset_files" f WHERE "previewAt" IS NULL AND "asset_job_status"."assetId" = "f"."assetId" AND "f"."type" = 'preview' AND "f"."path" IS NOT NULL`,
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.query(
|
||||||
|
`UPDATE "asset_job_status" SET "thumbnailAt" = NOW() FROM "asset_files" f WHERE "thumbnailAt" IS NULL AND "asset_job_status"."assetId" = "f"."assetId" AND "f"."type" = 'thumbnail' AND "f"."path" IS NOT NULL`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(): Promise<void> {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
export class RemoveThumbailAtForMissingThumbnails1725327902980 implements MigrationInterface {
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`UPDATE "asset_job_status" j SET "thumbnailAt" = NULL WHERE j."thumbnailAt" IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM asset_files f WHERE j."assetId" = f."assetId" AND f."type" = 'thumbnail' AND f."path" IS NOT NULL )`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(): Promise<void> {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -75,7 +75,6 @@ FROM
|
|||||||
"asset"."fileCreatedAt" >= $1
|
"asset"."fileCreatedAt" >= $1
|
||||||
AND "exifInfo"."lensModel" = $2
|
AND "exifInfo"."lensModel" = $2
|
||||||
AND 1 = 1
|
AND 1 = 1
|
||||||
AND 1 = 1
|
|
||||||
AND (
|
AND (
|
||||||
"asset"."isFavorite" = $3
|
"asset"."isFavorite" = $3
|
||||||
AND "asset"."isArchived" = $4
|
AND "asset"."isArchived" = $4
|
||||||
@@ -169,7 +168,6 @@ WHERE
|
|||||||
"asset"."fileCreatedAt" >= $1
|
"asset"."fileCreatedAt" >= $1
|
||||||
AND "exifInfo"."lensModel" = $2
|
AND "exifInfo"."lensModel" = $2
|
||||||
AND 1 = 1
|
AND 1 = 1
|
||||||
AND 1 = 1
|
|
||||||
AND (
|
AND (
|
||||||
"asset"."isFavorite" = $3
|
"asset"."isFavorite" = $3
|
||||||
AND "asset"."isArchived" = $4
|
AND "asset"."isArchived" = $4
|
||||||
|
|||||||
@@ -395,7 +395,7 @@ export class AssetRepository implements IAssetRepository {
|
|||||||
|
|
||||||
switch (property) {
|
switch (property) {
|
||||||
case WithoutProperty.THUMBNAIL: {
|
case WithoutProperty.THUMBNAIL: {
|
||||||
relations = { jobStatus: true };
|
relations = { jobStatus: true, files: true };
|
||||||
where = [
|
where = [
|
||||||
{ jobStatus: { previewAt: IsNull() }, isVisible: true },
|
{ jobStatus: { previewAt: IsNull() }, isVisible: true },
|
||||||
{ jobStatus: { thumbnailAt: IsNull() }, isVisible: true },
|
{ jobStatus: { thumbnailAt: IsNull() }, isVisible: true },
|
||||||
@@ -527,7 +527,12 @@ export class AssetRepository implements IAssetRepository {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getWith(pagination: PaginationOptions, property: WithProperty, libraryId?: string): Paginated<AssetEntity> {
|
getWith(
|
||||||
|
pagination: PaginationOptions,
|
||||||
|
property: WithProperty,
|
||||||
|
libraryId?: string,
|
||||||
|
withDeleted = false,
|
||||||
|
): Paginated<AssetEntity> {
|
||||||
let where: FindOptionsWhere<AssetEntity> | FindOptionsWhere<AssetEntity>[] = {};
|
let where: FindOptionsWhere<AssetEntity> | FindOptionsWhere<AssetEntity>[] = {};
|
||||||
|
|
||||||
switch (property) {
|
switch (property) {
|
||||||
@@ -557,6 +562,7 @@ export class AssetRepository implements IAssetRepository {
|
|||||||
|
|
||||||
return paginate(this.repository, pagination, {
|
return paginate(this.repository, pagination, {
|
||||||
where,
|
where,
|
||||||
|
withDeleted,
|
||||||
order: {
|
order: {
|
||||||
// Ensures correct order when paginating
|
// Ensures correct order when paginating
|
||||||
createdAt: 'ASC',
|
createdAt: 'ASC',
|
||||||
|
|||||||
@@ -317,7 +317,7 @@ export class MapRepository implements IMapRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const input = createReadStream(filePath);
|
const input = createReadStream(filePath);
|
||||||
const lineReader = readLine.createInterface({ input: input });
|
const lineReader = readLine.createInterface({ input });
|
||||||
|
|
||||||
const adminMap = new Map<string, string>();
|
const adminMap = new Map<string, string>();
|
||||||
for await (const line of lineReader) {
|
for await (const line of lineReader) {
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ export class AlbumService {
|
|||||||
throw new BadRequestException('User not found');
|
throw new BadRequestException('User not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.albumUserRepository.create({ userId: userId, albumId: id, role });
|
await this.albumUserRepository.create({ userId, albumId: id, role });
|
||||||
await this.eventRepository.emit('album.invite', { id, userId });
|
await this.eventRepository.emit('album.invite', { id, userId });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ const fixtures = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const oauthUserWithDefaultQuota = {
|
const oauthUserWithDefaultQuota = {
|
||||||
email: email,
|
email,
|
||||||
name: ' ',
|
name: ' ',
|
||||||
oauthId: sub,
|
oauthId: sub,
|
||||||
quotaSizeInBytes: 1_073_741_824,
|
quotaSizeInBytes: 1_073_741_824,
|
||||||
@@ -561,7 +561,7 @@ describe('AuthService', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(userMock.create).toHaveBeenCalledWith({
|
expect(userMock.create).toHaveBeenCalledWith({
|
||||||
email: email,
|
email,
|
||||||
name: ' ',
|
name: ' ',
|
||||||
oauthId: sub,
|
oauthId: sub,
|
||||||
quotaSizeInBytes: null,
|
quotaSizeInBytes: null,
|
||||||
@@ -581,7 +581,7 @@ describe('AuthService', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(userMock.create).toHaveBeenCalledWith({
|
expect(userMock.create).toHaveBeenCalledWith({
|
||||||
email: email,
|
email,
|
||||||
name: ' ',
|
name: ' ',
|
||||||
oauthId: sub,
|
oauthId: sub,
|
||||||
quotaSizeInBytes: 5_368_709_120,
|
quotaSizeInBytes: 5_368_709_120,
|
||||||
|
|||||||
@@ -421,7 +421,7 @@ export class AuthService {
|
|||||||
await this.sessionRepository.update({ id: session.id, updatedAt: new Date() });
|
await this.sessionRepository.update({ id: session.id, updatedAt: new Date() });
|
||||||
}
|
}
|
||||||
|
|
||||||
return { user: session.user, session: session };
|
return { user: session.user, session };
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new UnauthorizedException('Invalid user token');
|
throw new UnauthorizedException('Invalid user token');
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ export class LibraryService {
|
|||||||
const libraryId = job.id;
|
const libraryId = job.id;
|
||||||
|
|
||||||
const assetPagination = usePagination(JOBS_LIBRARY_PAGINATION_SIZE, (pagination) =>
|
const assetPagination = usePagination(JOBS_LIBRARY_PAGINATION_SIZE, (pagination) =>
|
||||||
this.assetRepository.getAll(pagination, { libraryId: libraryId, withDeleted: true }),
|
this.assetRepository.getAll(pagination, { libraryId, withDeleted: true }),
|
||||||
);
|
);
|
||||||
|
|
||||||
let assetsFound = false;
|
let assetsFound = false;
|
||||||
@@ -465,7 +465,7 @@ export class LibraryService {
|
|||||||
libraryId: job.id,
|
libraryId: job.id,
|
||||||
checksum: pathHash,
|
checksum: pathHash,
|
||||||
originalPath: assetPath,
|
originalPath: assetPath,
|
||||||
deviceAssetId: deviceAssetId,
|
deviceAssetId,
|
||||||
deviceId: 'Library Import',
|
deviceId: 'Library Import',
|
||||||
fileCreatedAt: stats.mtime,
|
fileCreatedAt: stats.mtime,
|
||||||
fileModifiedAt: stats.mtime,
|
fileModifiedAt: stats.mtime,
|
||||||
@@ -581,7 +581,7 @@ export class LibraryService {
|
|||||||
this.logger.debug(`Removing offline assets for library ${job.id}`);
|
this.logger.debug(`Removing offline assets for library ${job.id}`);
|
||||||
|
|
||||||
const assetPagination = usePagination(JOBS_LIBRARY_PAGINATION_SIZE, (pagination) =>
|
const assetPagination = usePagination(JOBS_LIBRARY_PAGINATION_SIZE, (pagination) =>
|
||||||
this.assetRepository.getWith(pagination, WithProperty.IS_OFFLINE, job.id),
|
this.assetRepository.getWith(pagination, WithProperty.IS_OFFLINE, job.id, true),
|
||||||
);
|
);
|
||||||
|
|
||||||
let offlineAssets = 0;
|
let offlineAssets = 0;
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ export class MediaService {
|
|||||||
async handleGeneratePreview({ id }: IEntityJob): Promise<JobStatus> {
|
async handleGeneratePreview({ id }: IEntityJob): Promise<JobStatus> {
|
||||||
const [{ image }, [asset]] = await Promise.all([
|
const [{ image }, [asset]] = await Promise.all([
|
||||||
this.configCore.getConfig({ withCache: true }),
|
this.configCore.getConfig({ withCache: true }),
|
||||||
this.assetRepository.getByIds([id], { exifInfo: true }),
|
this.assetRepository.getByIds([id], { exifInfo: true, files: true }),
|
||||||
]);
|
]);
|
||||||
if (!asset) {
|
if (!asset) {
|
||||||
return JobStatus.FAILED;
|
return JobStatus.FAILED;
|
||||||
|
|||||||
@@ -408,6 +408,17 @@ describe(MetadataService.name, () => {
|
|||||||
expect(tagMock.upsertValue).toHaveBeenCalledWith({ userId: 'user-id', value: 'Parent', parent: undefined });
|
expect(tagMock.upsertValue).toHaveBeenCalledWith({ userId: 'user-id', value: 'Parent', parent: undefined });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should extract tags from Keywords as a list with a number', async () => {
|
||||||
|
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||||
|
metadataMock.readTags.mockResolvedValue({ Keywords: ['Parent', 2024] as any[] });
|
||||||
|
tagMock.upsertValue.mockResolvedValue(tagStub.parent);
|
||||||
|
|
||||||
|
await sut.handleMetadataExtraction({ id: assetStub.image.id });
|
||||||
|
|
||||||
|
expect(tagMock.upsertValue).toHaveBeenCalledWith({ userId: 'user-id', value: 'Parent', parent: undefined });
|
||||||
|
expect(tagMock.upsertValue).toHaveBeenCalledWith({ userId: 'user-id', value: '2024', parent: undefined });
|
||||||
|
});
|
||||||
|
|
||||||
it('should extract hierarchal tags from Keywords', async () => {
|
it('should extract hierarchal tags from Keywords', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||||
metadataMock.readTags.mockResolvedValue({ Keywords: 'Parent/Child' });
|
metadataMock.readTags.mockResolvedValue({ Keywords: 'Parent/Child' });
|
||||||
@@ -423,6 +434,66 @@ describe(MetadataService.name, () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should ignore Keywords when TagsList is present', async () => {
|
||||||
|
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||||
|
metadataMock.readTags.mockResolvedValue({ Keywords: 'Child', TagsList: ['Parent/Child'] });
|
||||||
|
tagMock.upsertValue.mockResolvedValue(tagStub.parent);
|
||||||
|
|
||||||
|
await sut.handleMetadataExtraction({ id: assetStub.image.id });
|
||||||
|
|
||||||
|
expect(tagMock.upsertValue).toHaveBeenNthCalledWith(1, { userId: 'user-id', value: 'Parent', parent: undefined });
|
||||||
|
expect(tagMock.upsertValue).toHaveBeenNthCalledWith(2, {
|
||||||
|
userId: 'user-id',
|
||||||
|
value: 'Parent/Child',
|
||||||
|
parent: tagStub.parent,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should extract hierarchy from HierarchicalSubject', async () => {
|
||||||
|
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||||
|
metadataMock.readTags.mockResolvedValue({ HierarchicalSubject: ['Parent|Child'] });
|
||||||
|
tagMock.upsertValue.mockResolvedValueOnce(tagStub.parent);
|
||||||
|
tagMock.upsertValue.mockResolvedValueOnce(tagStub.child);
|
||||||
|
|
||||||
|
await sut.handleMetadataExtraction({ id: assetStub.image.id });
|
||||||
|
|
||||||
|
expect(tagMock.upsertValue).toHaveBeenNthCalledWith(1, { userId: 'user-id', value: 'Parent', parent: undefined });
|
||||||
|
expect(tagMock.upsertValue).toHaveBeenNthCalledWith(2, {
|
||||||
|
userId: 'user-id',
|
||||||
|
value: 'Parent/Child',
|
||||||
|
parent: tagStub.parent,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should extract ignore / characters in a HierarchicalSubject tag', async () => {
|
||||||
|
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||||
|
metadataMock.readTags.mockResolvedValue({ HierarchicalSubject: ['Mom/Dad'] });
|
||||||
|
tagMock.upsertValue.mockResolvedValueOnce(tagStub.parent);
|
||||||
|
|
||||||
|
await sut.handleMetadataExtraction({ id: assetStub.image.id });
|
||||||
|
|
||||||
|
expect(tagMock.upsertValue).toHaveBeenCalledWith({
|
||||||
|
userId: 'user-id',
|
||||||
|
value: 'Mom|Dad',
|
||||||
|
parent: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore HierarchicalSubject when TagsList is present', async () => {
|
||||||
|
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||||
|
metadataMock.readTags.mockResolvedValue({ HierarchicalSubject: ['Parent2|Child2'], TagsList: ['Parent/Child'] });
|
||||||
|
tagMock.upsertValue.mockResolvedValue(tagStub.parent);
|
||||||
|
|
||||||
|
await sut.handleMetadataExtraction({ id: assetStub.image.id });
|
||||||
|
|
||||||
|
expect(tagMock.upsertValue).toHaveBeenNthCalledWith(1, { userId: 'user-id', value: 'Parent', parent: undefined });
|
||||||
|
expect(tagMock.upsertValue).toHaveBeenNthCalledWith(2, {
|
||||||
|
userId: 'user-id',
|
||||||
|
value: 'Parent/Child',
|
||||||
|
parent: tagStub.parent,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should not apply motion photos if asset is video', async () => {
|
it('should not apply motion photos if asset is video', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([{ ...assetStub.livePhotoMotionAsset, isVisible: true }]);
|
assetMock.getByIds.mockResolvedValue([{ ...assetStub.livePhotoMotionAsset, isVisible: true }]);
|
||||||
mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer);
|
mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer);
|
||||||
|
|||||||
@@ -352,22 +352,29 @@ export class MetadataService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async applyTagList(asset: AssetEntity, exifTags: ImmichTags) {
|
private async applyTagList(asset: AssetEntity, exifTags: ImmichTags) {
|
||||||
const tags: string[] = [];
|
const tags: unknown[] = [];
|
||||||
|
|
||||||
if (exifTags.TagsList) {
|
if (exifTags.TagsList) {
|
||||||
tags.push(...exifTags.TagsList);
|
tags.push(...exifTags.TagsList);
|
||||||
}
|
} else if (exifTags.HierarchicalSubject) {
|
||||||
|
tags.push(
|
||||||
if (exifTags.Keywords) {
|
exifTags.HierarchicalSubject.map((tag) =>
|
||||||
|
tag
|
||||||
|
// convert | to /
|
||||||
|
.replaceAll('/', '<PLACEHOLDER>')
|
||||||
|
.replaceAll('|', '/')
|
||||||
|
.replaceAll('<PLACEHOLDER>', '|'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (exifTags.Keywords) {
|
||||||
let keywords = exifTags.Keywords;
|
let keywords = exifTags.Keywords;
|
||||||
if (typeof keywords === 'string') {
|
if (!Array.isArray(keywords)) {
|
||||||
keywords = [keywords];
|
keywords = [keywords];
|
||||||
}
|
}
|
||||||
tags.push(...keywords);
|
tags.push(...keywords);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tags.length > 0) {
|
if (tags.length > 0) {
|
||||||
const results = await upsertTags(this.tagRepository, { userId: asset.ownerId, tags });
|
const results = await upsertTags(this.tagRepository, { userId: asset.ownerId, tags: tags.map(String) });
|
||||||
const tagIds = results.map((tag) => tag.id);
|
const tagIds = results.map((tag) => tag.id);
|
||||||
await this.tagRepository.upsertAssetTags({ assetId: asset.id, tagIds });
|
await this.tagRepository.upsertAssetTags({ assetId: asset.id, tagIds });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -309,7 +309,7 @@ describe(StorageTemplateService.name, () => {
|
|||||||
entityId: assetStub.image.id,
|
entityId: assetStub.image.id,
|
||||||
pathType: AssetPathType.ORIGINAL,
|
pathType: AssetPathType.ORIGINAL,
|
||||||
oldPath: assetStub.image.originalPath,
|
oldPath: assetStub.image.originalPath,
|
||||||
newPath: newPath,
|
newPath,
|
||||||
});
|
});
|
||||||
expect(storageMock.rename).toHaveBeenCalledWith(assetStub.image.originalPath, newPath);
|
expect(storageMock.rename).toHaveBeenCalledWith(assetStub.image.originalPath, newPath);
|
||||||
expect(storageMock.copyFile).toHaveBeenCalledWith(assetStub.image.originalPath, newPath);
|
expect(storageMock.copyFile).toHaveBeenCalledWith(assetStub.image.originalPath, newPath);
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ export class StorageTemplateService {
|
|||||||
const storagePath = this.render(this.template.compiled, {
|
const storagePath = this.render(this.template.compiled, {
|
||||||
asset,
|
asset,
|
||||||
filename: sanitized,
|
filename: sanitized,
|
||||||
extension: extension,
|
extension,
|
||||||
albumName,
|
albumName,
|
||||||
});
|
});
|
||||||
const fullPath = path.normalize(path.join(rootPath, storagePath));
|
const fullPath = path.normalize(path.join(rootPath, storagePath));
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export class TrashService {
|
|||||||
const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) =>
|
const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) =>
|
||||||
this.assetRepository.getByUserId(pagination, auth.user.id, {
|
this.assetRepository.getByUserId(pagination, auth.user.id, {
|
||||||
trashedBefore: DateTime.now().toJSDate(),
|
trashedBefore: DateTime.now().toJSDate(),
|
||||||
|
withArchived: true,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -71,8 +71,15 @@ export function searchAssetBuilder(
|
|||||||
builder.andWhere(`${builder.alias}.ownerId IN (:...userIds)`, { userIds: options.userIds });
|
builder.andWhere(`${builder.alias}.ownerId IN (:...userIds)`, { userIds: options.userIds });
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = _.pick(options, ['encodedVideoPath', 'originalPath']);
|
if (options.encodedVideoPath) {
|
||||||
builder.andWhere(_.omitBy(path, _.isUndefined));
|
builder.andWhere({ encodedVideoPath: options.encodedVideoPath });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.originalPath) {
|
||||||
|
builder.andWhere(`f_unaccent(${builder.alias}.originalPath) ILIKE f_unaccent(:originalPath)`, {
|
||||||
|
originalPath: `%${options.originalPath}%`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (options.originalFileName) {
|
if (options.originalFileName) {
|
||||||
builder.andWhere(`f_unaccent(${builder.alias}.originalFileName) ILIKE f_unaccent(:originalFileName)`, {
|
builder.andWhere(`f_unaccent(${builder.alias}.originalFileName) ILIKE f_unaccent(:originalFileName)`, {
|
||||||
|
|||||||
Vendored
+1
-1
@@ -31,7 +31,7 @@ const files: AssetFileEntity[] = [previewFile, thumbnailFile];
|
|||||||
export const stackStub = (stackId: string, assets: AssetEntity[]): StackEntity => {
|
export const stackStub = (stackId: string, assets: AssetEntity[]): StackEntity => {
|
||||||
return {
|
return {
|
||||||
id: stackId,
|
id: stackId,
|
||||||
assets: assets,
|
assets,
|
||||||
owner: assets[0].owner,
|
owner: assets[0].owner,
|
||||||
ownerId: assets[0].ownerId,
|
ownerId: assets[0].ownerId,
|
||||||
primaryAsset: assets[0],
|
primaryAsset: assets[0],
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ export default [
|
|||||||
'@typescript-eslint/no-floating-promises': 'error',
|
'@typescript-eslint/no-floating-promises': 'error',
|
||||||
'@typescript-eslint/no-misused-promises': 'error',
|
'@typescript-eslint/no-misused-promises': 'error',
|
||||||
'@typescript-eslint/require-await': 'error',
|
'@typescript-eslint/require-await': 'error',
|
||||||
|
'object-shorthand': ['error', 'always'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Generated
+128
-665
File diff suppressed because it is too large
Load Diff
+8
-8
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-web",
|
"name": "immich-web",
|
||||||
"version": "1.113.0",
|
"version": "1.113.1",
|
||||||
"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",
|
||||||
@@ -29,8 +29,8 @@
|
|||||||
"@socket.io/component-emitter": "^3.1.0",
|
"@socket.io/component-emitter": "^3.1.0",
|
||||||
"@sveltejs/adapter-static": "^3.0.1",
|
"@sveltejs/adapter-static": "^3.0.1",
|
||||||
"@sveltejs/enhanced-img": "^0.3.0",
|
"@sveltejs/enhanced-img": "^0.3.0",
|
||||||
"@sveltejs/kit": "^2.5.2",
|
"@sveltejs/kit": "^2.5.18",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.0.2",
|
"@sveltejs/vite-plugin-svelte": "^3.1.2",
|
||||||
"@testing-library/jest-dom": "^6.4.2",
|
"@testing-library/jest-dom": "^6.4.2",
|
||||||
"@testing-library/svelte": "^5.2.0",
|
"@testing-library/svelte": "^5.2.0",
|
||||||
"@testing-library/user-event": "^14.5.2",
|
"@testing-library/user-event": "^14.5.2",
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"eslint": "^9.0.0",
|
"eslint": "^9.0.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-svelte": "^2.35.1",
|
"eslint-plugin-svelte": "^2.43.0",
|
||||||
"eslint-plugin-unicorn": "^55.0.0",
|
"eslint-plugin-unicorn": "^55.0.0",
|
||||||
"factory.ts": "^1.4.1",
|
"factory.ts": "^1.4.1",
|
||||||
"globals": "^15.9.0",
|
"globals": "^15.9.0",
|
||||||
@@ -53,13 +53,13 @@
|
|||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"prettier-plugin-organize-imports": "^4.0.0",
|
"prettier-plugin-organize-imports": "^4.0.0",
|
||||||
"prettier-plugin-sort-json": "^4.0.0",
|
"prettier-plugin-sort-json": "^4.0.0",
|
||||||
"prettier-plugin-svelte": "^3.2.1",
|
"prettier-plugin-svelte": "^3.2.6",
|
||||||
"rollup-plugin-visualizer": "^5.12.0",
|
"rollup-plugin-visualizer": "^5.12.0",
|
||||||
"svelte": "^4.2.12",
|
"svelte": "^4.2.19",
|
||||||
"svelte-check": "^3.6.5",
|
"svelte-check": "^4.0.0",
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
"tslib": "^2.6.2",
|
"tslib": "^2.6.2",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.5.0",
|
||||||
"vite": "^5.1.4",
|
"vite": "^5.1.4",
|
||||||
"vitest": "^2.0.5"
|
"vitest": "^2.0.5"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export type ShortcutOptions<T = HTMLElement> = {
|
|||||||
preventDefault?: boolean;
|
preventDefault?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const shouldIgnoreShortcut = (event: KeyboardEvent): boolean => {
|
export const shouldIgnoreEvent = (event: KeyboardEvent | ClipboardEvent): boolean => {
|
||||||
if (event.target === event.currentTarget) {
|
if (event.target === event.currentTarget) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ export const shortcuts = <T extends HTMLElement>(
|
|||||||
options: ShortcutOptions<T>[],
|
options: ShortcutOptions<T>[],
|
||||||
): ActionReturn<ShortcutOptions<T>[]> => {
|
): ActionReturn<ShortcutOptions<T>[]> => {
|
||||||
function onKeydown(event: KeyboardEvent) {
|
function onKeydown(event: KeyboardEvent) {
|
||||||
const ignoreShortcut = shouldIgnoreShortcut(event);
|
const ignoreShortcut = shouldIgnoreEvent(event);
|
||||||
for (const { shortcut, onShortcut, ignoreInputFields = true, preventDefault = true } of options) {
|
for (const { shortcut, onShortcut, ignoreInputFields = true, preventDefault = true } of options) {
|
||||||
if (ignoreInputFields && ignoreShortcut) {
|
if (ignoreInputFields && ignoreShortcut) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -476,7 +476,7 @@
|
|||||||
<section class="px-6 pt-6 dark:text-immich-dark-fg">
|
<section class="px-6 pt-6 dark:text-immich-dark-fg">
|
||||||
<p class="pb-4 text-sm">{$t('appears_in').toUpperCase()}</p>
|
<p class="pb-4 text-sm">{$t('appears_in').toUpperCase()}</p>
|
||||||
{#each albums as album}
|
{#each albums as album}
|
||||||
<a data-sveltekit-preload-data="hover" href={`/albums/${album.id}`}>
|
<a href="{AppRoute.ALBUMS}/{album.id}">
|
||||||
<div class="flex gap-4 pt-2 hover:cursor-pointer items-center">
|
<div class="flex gap-4 pt-2 hover:cursor-pointer items-center">
|
||||||
<div>
|
<div>
|
||||||
<img
|
<img
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getAssetOriginalUrl, getKey } from '$lib/utils';
|
import { getAssetOriginalUrl, getKey } from '$lib/utils';
|
||||||
|
import { isWebCompatibleImage } from '$lib/utils/asset-utils';
|
||||||
import { AssetMediaSize, AssetTypeEnum, viewAsset, type AssetResponseDto } from '@immich/sdk';
|
import { AssetMediaSize, AssetTypeEnum, viewAsset, type AssetResponseDto } from '@immich/sdk';
|
||||||
import type { AdapterConstructor, PluginConstructor } from '@photo-sphere-viewer/core';
|
import type { AdapterConstructor, PluginConstructor } from '@photo-sphere-viewer/core';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
export let asset: Pick<AssetResponseDto, 'id' | 'type'>;
|
export let asset: { id: string; type: AssetTypeEnum.Video } | AssetResponseDto;
|
||||||
|
|
||||||
const photoSphereConfigs =
|
const photoSphereConfigs =
|
||||||
asset.type === AssetTypeEnum.Video
|
asset.type === AssetTypeEnum.Video
|
||||||
@@ -27,6 +28,9 @@
|
|||||||
const url = URL.createObjectURL(data);
|
const url = URL.createObjectURL(data);
|
||||||
return url;
|
return url;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const originalImageUrl =
|
||||||
|
asset.type === AssetTypeEnum.Image && isWebCompatibleImage(asset) ? getAssetOriginalUrl(asset.id) : null;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div transition:fade={{ duration: 150 }} class="flex h-full select-none place-content-center place-items-center">
|
<div transition:fade={{ duration: 150 }} class="flex h-full select-none place-content-center place-items-center">
|
||||||
@@ -34,7 +38,14 @@
|
|||||||
{#await Promise.all([loadAssetData(), import('./photo-sphere-viewer-adapter.svelte'), ...photoSphereConfigs])}
|
{#await Promise.all([loadAssetData(), import('./photo-sphere-viewer-adapter.svelte'), ...photoSphereConfigs])}
|
||||||
<LoadingSpinner />
|
<LoadingSpinner />
|
||||||
{:then [data, module, adapter, plugins, navbar]}
|
{:then [data, module, adapter, plugins, navbar]}
|
||||||
<svelte:component this={module.default} panorama={data} plugins={plugins ?? undefined} {navbar} {adapter} />
|
<svelte:component
|
||||||
|
this={module.default}
|
||||||
|
panorama={data}
|
||||||
|
plugins={plugins ?? undefined}
|
||||||
|
{navbar}
|
||||||
|
{adapter}
|
||||||
|
{originalImageUrl}
|
||||||
|
/>
|
||||||
{:catch}
|
{:catch}
|
||||||
{$t('errors.failed_to_load_asset')}
|
{$t('errors.failed_to_load_asset')}
|
||||||
{/await}
|
{/await}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {
|
import {
|
||||||
Viewer,
|
Viewer,
|
||||||
|
events,
|
||||||
EquirectangularAdapter,
|
EquirectangularAdapter,
|
||||||
type PluginConstructor,
|
type PluginConstructor,
|
||||||
type AdapterConstructor,
|
type AdapterConstructor,
|
||||||
@@ -9,6 +10,7 @@
|
|||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
|
|
||||||
export let panorama: string | { source: string };
|
export let panorama: string | { source: string };
|
||||||
|
export let originalImageUrl: string | null;
|
||||||
export let adapter: AdapterConstructor | [AdapterConstructor, unknown] = EquirectangularAdapter;
|
export let adapter: AdapterConstructor | [AdapterConstructor, unknown] = EquirectangularAdapter;
|
||||||
export let plugins: (PluginConstructor | [PluginConstructor, unknown])[] = [];
|
export let plugins: (PluginConstructor | [PluginConstructor, unknown])[] = [];
|
||||||
export let navbar = false;
|
export let navbar = false;
|
||||||
@@ -28,6 +30,20 @@
|
|||||||
maxFov: 180,
|
maxFov: 180,
|
||||||
fisheye: true,
|
fisheye: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (originalImageUrl) {
|
||||||
|
const zoomHandler = ({ zoomLevel }: events.ZoomUpdatedEvent) => {
|
||||||
|
// zoomLevel range: [0, 100]
|
||||||
|
if (Math.round(zoomLevel) >= 75) {
|
||||||
|
// Replace the preview with the original
|
||||||
|
viewer.setPanorama(originalImageUrl, { showLoader: false, speed: 150 }).catch(() => {
|
||||||
|
viewer.setPanorama(panorama, { showLoader: false, speed: 0 }).catch(() => {});
|
||||||
|
});
|
||||||
|
viewer.removeEventListener(events.ZoomUpdatedEvent.type, zoomHandler);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
viewer.addEventListener(events.ZoomUpdatedEvent.type, zoomHandler);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
|
|||||||
@@ -169,7 +169,13 @@
|
|||||||
<LoadingSpinner />
|
<LoadingSpinner />
|
||||||
</div>
|
</div>
|
||||||
{:else if !imageError}
|
{:else if !imageError}
|
||||||
<div use:zoomImageAction class="h-full w-full" transition:fade={{ duration: haveFadeTransition ? 150 : 0 }}>
|
<div
|
||||||
|
use:zoomImageAction
|
||||||
|
use:swipe
|
||||||
|
on:swipe={onSwipe}
|
||||||
|
class="h-full w-full"
|
||||||
|
transition:fade={{ duration: haveFadeTransition ? 150 : 0 }}
|
||||||
|
>
|
||||||
{#if $slideshowState !== SlideshowState.None && $slideshowLook === SlideshowLook.BlurredBackground}
|
{#if $slideshowState !== SlideshowState.None && $slideshowLook === SlideshowLook.BlurredBackground}
|
||||||
<img
|
<img
|
||||||
src={assetFileUrl}
|
src={assetFileUrl}
|
||||||
@@ -181,8 +187,6 @@
|
|||||||
<img
|
<img
|
||||||
bind:this={$photoViewer}
|
bind:this={$photoViewer}
|
||||||
src={assetFileUrl}
|
src={assetFileUrl}
|
||||||
use:swipe
|
|
||||||
on:swipe={onSwipe}
|
|
||||||
alt={$getAltText(asset)}
|
alt={$getAltText(asset)}
|
||||||
class="h-full w-full {$slideshowState === SlideshowState.None
|
class="h-full w-full {$slideshowState === SlideshowState.None
|
||||||
? 'object-contain'
|
? 'object-contain'
|
||||||
|
|||||||
@@ -81,7 +81,7 @@
|
|||||||
const mergedPerson = await getPerson({ id: person.id });
|
const mergedPerson = await getPerson({ id: person.id });
|
||||||
const count = results.filter(({ success }) => success).length;
|
const count = results.filter(({ success }) => success).length;
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
message: $t('merged_people_count', { values: { count: count } }),
|
message: $t('merged_people_count', { values: { count } }),
|
||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
});
|
});
|
||||||
dispatch('merge', mergedPerson);
|
dispatch('merge', mergedPerson);
|
||||||
|
|||||||
@@ -309,7 +309,7 @@
|
|||||||
class:opacity-100={!galleryInView}
|
class:opacity-100={!galleryInView}
|
||||||
>
|
>
|
||||||
<CircleIconButton
|
<CircleIconButton
|
||||||
href="${AppRoute.PHOTOS}?at=${currentAsset.id}"
|
href="{AppRoute.PHOTOS}?at={currentAsset.id}"
|
||||||
icon={mdiImageSearch}
|
icon={mdiImageSearch}
|
||||||
title={$t('view_in_timeline')}
|
title={$t('view_in_timeline')}
|
||||||
color="light"
|
color="light"
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
const count = results.filter(({ success }) => success).length;
|
const count = results.filter(({ success }) => success).length;
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
message: $t('assets_removed_count', { values: { count: count } }),
|
message: $t('assets_removed_count', { values: { count } }),
|
||||||
});
|
});
|
||||||
|
|
||||||
clearSelect();
|
clearSelect();
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
|
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
message: $t('assets_removed_count', { values: { count: count } }),
|
message: $t('assets_removed_count', { values: { count } }),
|
||||||
});
|
});
|
||||||
|
|
||||||
clearSelect();
|
clearSelect();
|
||||||
|
|||||||
@@ -762,20 +762,21 @@
|
|||||||
{#if showShortcuts}
|
{#if showShortcuts}
|
||||||
<ShowShortcuts on:close={() => (showShortcuts = !showShortcuts)} />
|
<ShowShortcuts on:close={() => (showShortcuts = !showShortcuts)} />
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if assetStore.buckets.length > 0}
|
||||||
<Scrubber
|
<Scrubber
|
||||||
invisible={showSkeleton}
|
invisible={showSkeleton}
|
||||||
{assetStore}
|
{assetStore}
|
||||||
height={safeViewport.height}
|
height={safeViewport.height}
|
||||||
timelineTopOffset={topSectionHeight}
|
timelineTopOffset={topSectionHeight}
|
||||||
timelineBottomOffset={bottomSectionHeight}
|
timelineBottomOffset={bottomSectionHeight}
|
||||||
{leadout}
|
{leadout}
|
||||||
{scrubOverallPercent}
|
{scrubOverallPercent}
|
||||||
{scrubBucketPercent}
|
{scrubBucketPercent}
|
||||||
{scrubBucket}
|
{scrubBucket}
|
||||||
{onScrub}
|
{onScrub}
|
||||||
{stopScrub}
|
{stopScrub}
|
||||||
/>
|
/>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<!-- Right margin MUST be equal to the width of immich-scrubbable-scrollbar -->
|
<!-- Right margin MUST be equal to the width of immich-scrubbable-scrollbar -->
|
||||||
<section
|
<section
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user