merge main
@ -22,6 +22,7 @@ open-api/typescript-sdk/node_modules/
|
||||
server/coverage/
|
||||
server/node_modules/
|
||||
server/upload/
|
||||
server/src/queries
|
||||
server/dist/
|
||||
server/www/
|
||||
|
||||
|
1
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
custom: ['https://buy.immich.app']
|
1
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@ -83,7 +83,6 @@ body:
|
||||
2.
|
||||
3.
|
||||
...
|
||||
render: bash
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
9
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,11 +1,14 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: I have a question or need support
|
||||
- name: ✋ I have a question or need support
|
||||
url: https://discord.immich.app
|
||||
about: We use GitHub for tracking bugs, please check out our Discord channel for freaky fast support.
|
||||
- name: Feature Request
|
||||
- name: 📷 My photo or video has a date, time, or timezone problem
|
||||
url: https://github.com/immich-app/immich/discussions/12650
|
||||
about: Upload a sample file to this discussion and we will take a look
|
||||
- name: 🌟 Feature request
|
||||
url: https://github.com/immich-app/immich/discussions/new?category=feature-request
|
||||
about: Please use our GitHub Discussion for making feature requests.
|
||||
- name: I'm unsure where to go
|
||||
- name: 🫣 I'm unsure where to go
|
||||
url: https://discord.immich.app
|
||||
about: If you are unsure where to go, then joining our Discord is recommended; Just ask!
|
||||
|
7
.github/dependabot.yml
vendored
@ -1,7 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
# Maintain dependencies for GitHub Actions
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
3
.github/labeler.yml
vendored
@ -33,3 +33,6 @@ documentation:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- machine-learning/app/**
|
||||
|
||||
changelog:translation:
|
||||
- head-branch: ['^chore/translations$']
|
||||
|
40
.github/release.yml
vendored
@ -1,41 +1,33 @@
|
||||
changelog:
|
||||
categories:
|
||||
- title: ⚠️ Breaking Changes
|
||||
- title: 🚨 Breaking Changes
|
||||
labels:
|
||||
- breaking-change
|
||||
- changelog:breaking-change
|
||||
|
||||
- title: 🗄️ Server
|
||||
- title: 🫥 Deprecated Changes
|
||||
labels:
|
||||
- 🗄️server
|
||||
- changelog:deprecated
|
||||
|
||||
- title: 📱 Mobile
|
||||
- title: 🔒 Security
|
||||
labels:
|
||||
- 📱mobile
|
||||
- changelog:security
|
||||
|
||||
- title: 🖥️ Web
|
||||
- title: 🚀 Features
|
||||
labels:
|
||||
- 🖥️web
|
||||
- changelog:feature
|
||||
|
||||
- title: 🧠 Machine Learning
|
||||
- title: 🌟 Enhancements
|
||||
labels:
|
||||
- 🧠machine-learning
|
||||
- changelog:enhancement
|
||||
|
||||
- title: ⚡ CLI
|
||||
- title: 🐛 Bug fixes
|
||||
labels:
|
||||
- cli
|
||||
- changelog:bugfix
|
||||
|
||||
- title: 📓 Documentation
|
||||
- title: 📚 Documentation
|
||||
labels:
|
||||
- documentation
|
||||
- changelog:documentation
|
||||
|
||||
- title: 🔨 Maintenance
|
||||
- title: 🌐 Translations
|
||||
labels:
|
||||
- deployment
|
||||
- dependencies
|
||||
- renovate
|
||||
- maintenance
|
||||
- tech-debt
|
||||
|
||||
- title: Other changes
|
||||
labels:
|
||||
- "*"
|
||||
- changelog:translation
|
||||
|
20
.github/workflows/build-mobile.yml
vendored
@ -16,10 +16,28 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
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:
|
||||
name: Build and sign Android
|
||||
needs: pre-job
|
||||
# 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
|
||||
|
||||
steps:
|
||||
|
6
.github/workflows/cli.yml
vendored
@ -22,7 +22,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Publish
|
||||
name: CLI Publish
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
@ -59,7 +59,7 @@ jobs:
|
||||
uses: docker/setup-qemu-action@v3.2.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.6.1
|
||||
uses: docker/setup-buildx-action@v3.7.1
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
@ -88,7 +88,7 @@ jobs:
|
||||
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
|
||||
|
||||
- name: Build and push image
|
||||
uses: docker/build-push-action@v6.5.0
|
||||
uses: docker/build-push-action@v6.9.0
|
||||
with:
|
||||
file: cli/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
8
.github/workflows/docker-cleanup.yml
vendored
@ -22,7 +22,7 @@ concurrency:
|
||||
jobs:
|
||||
cleanup-images:
|
||||
name: Cleanup Stale Images Tags for ${{ matrix.primary-name }}
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@ -35,7 +35,7 @@ jobs:
|
||||
steps:
|
||||
- name: Clean temporary images
|
||||
if: "${{ env.TOKEN != '' }}"
|
||||
uses: stumpylog/image-cleaner-action/ephemeral@v0.8.0
|
||||
uses: stumpylog/image-cleaner-action/ephemeral@v0.9.0
|
||||
with:
|
||||
token: "${{ env.TOKEN }}"
|
||||
owner: "immich-app"
|
||||
@ -48,7 +48,7 @@ jobs:
|
||||
|
||||
cleanup-untagged-images:
|
||||
name: Cleanup Untagged Images Tags for ${{ matrix.primary-name }}
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- cleanup-images
|
||||
strategy:
|
||||
@ -64,7 +64,7 @@ jobs:
|
||||
steps:
|
||||
- name: Clean untagged images
|
||||
if: "${{ env.TOKEN != '' }}"
|
||||
uses: stumpylog/image-cleaner-action/untagged@v0.8.0
|
||||
uses: stumpylog/image-cleaner-action/untagged@v0.9.0
|
||||
with:
|
||||
token: "${{ env.TOKEN }}"
|
||||
owner: "immich-app"
|
||||
|
240
.github/workflows/docker.yml
vendored
@ -17,47 +17,106 @@ permissions:
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
build_and_push:
|
||||
name: Build and Push
|
||||
pre-job:
|
||||
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/**'
|
||||
- 'i18n/**'
|
||||
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"
|
||||
|
||||
retag_ml:
|
||||
name: Re-Tag ML
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_ml == 'false' && !github.event.pull_request.head.repo.fork }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
suffix: ["", "-cuda", "-openvino", "-armnn"]
|
||||
steps:
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Re-tag image
|
||||
run: |
|
||||
REGISTRY_NAME="ghcr.io"
|
||||
REPOSITORY=${{ github.repository_owner }}/immich-machine-learning
|
||||
TAG_OLD=main${{ matrix.suffix }}
|
||||
TAG_NEW=${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }}
|
||||
docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_NEW $REGISTRY_NAME/$REPOSITORY:$TAG_OLD
|
||||
|
||||
retag_server:
|
||||
name: Re-Tag Server
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_server == 'false' && !github.event.pull_request.head.repo.fork }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
suffix: [""]
|
||||
steps:
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Re-tag image
|
||||
run: |
|
||||
REGISTRY_NAME="ghcr.io"
|
||||
REPOSITORY=${{ github.repository_owner }}/immich-server
|
||||
TAG_OLD=main${{ matrix.suffix }}
|
||||
TAG_NEW=${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }}
|
||||
docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_NEW $REGISTRY_NAME/$REPOSITORY:$TAG_OLD
|
||||
|
||||
|
||||
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:
|
||||
# Prevent a failure in one image from stopping the other builds
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- image: immich-machine-learning
|
||||
context: machine-learning
|
||||
file: machine-learning/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
- platforms: linux/amd64,linux/arm64
|
||||
device: cpu
|
||||
|
||||
- image: immich-machine-learning
|
||||
context: machine-learning
|
||||
file: machine-learning/Dockerfile
|
||||
platforms: linux/amd64
|
||||
- platforms: linux/amd64
|
||||
device: cuda
|
||||
suffix: -cuda
|
||||
|
||||
- image: immich-machine-learning
|
||||
context: machine-learning
|
||||
file: machine-learning/Dockerfile
|
||||
platforms: linux/amd64
|
||||
- platforms: linux/amd64
|
||||
device: openvino
|
||||
suffix: -openvino
|
||||
|
||||
- image: immich-machine-learning
|
||||
context: machine-learning
|
||||
file: machine-learning/Dockerfile
|
||||
platforms: linux/arm64
|
||||
- platforms: linux/arm64
|
||||
device: armnn
|
||||
suffix: -armnn
|
||||
|
||||
- image: immich-server
|
||||
context: .
|
||||
file: server/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
device: cpu
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@ -66,7 +125,7 @@ jobs:
|
||||
uses: docker/setup-qemu-action@v3.2.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.6.1
|
||||
uses: docker/setup-buildx-action@v3.7.1
|
||||
|
||||
- name: Login to Docker Hub
|
||||
# Only push to Docker Hub when making a release
|
||||
@ -93,8 +152,8 @@ jobs:
|
||||
# Disable latest tag
|
||||
latest=false
|
||||
images: |
|
||||
name=ghcr.io/${{ github.repository_owner }}/${{matrix.image}}
|
||||
name=altran1502/${{matrix.image}},enable=${{ github.event_name == 'release' }}
|
||||
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 }}
|
||||
@ -111,18 +170,18 @@ jobs:
|
||||
# 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:${{ 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
|
||||
|
||||
- name: Build and push image
|
||||
uses: docker/build-push-action@v6.5.0
|
||||
uses: docker/build-push-action@v6.9.0
|
||||
with:
|
||||
context: ${{ matrix.context }}
|
||||
file: ${{ matrix.file }}
|
||||
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:${{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 }}
|
||||
@ -132,3 +191,120 @@ jobs:
|
||||
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.7.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.9.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 }}
|
||||
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 }}
|
||||
|
||||
success-check-server:
|
||||
name: Docker Build & Push Server Success
|
||||
needs: [build_and_push_server, retag_server]
|
||||
runs-on: ubuntu-latest
|
||||
if: always()
|
||||
steps:
|
||||
- name: Any jobs failed?
|
||||
if: ${{ contains(needs.*.result, 'failure') }}
|
||||
run: exit 1
|
||||
- name: All jobs passed or skipped
|
||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
||||
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
||||
|
||||
success-check-ml:
|
||||
name: Docker Build & Push ML Success
|
||||
needs: [build_and_push_ml, retag_ml]
|
||||
runs-on: ubuntu-latest
|
||||
if: always()
|
||||
steps:
|
||||
- name: Any jobs failed?
|
||||
if: ${{ contains(needs.*.result, 'failure') }}
|
||||
run: exit 1
|
||||
- name: All jobs passed or skipped
|
||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
||||
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
||||
|
24
.github/workflows/docs-build.yml
vendored
@ -2,12 +2,8 @@ name: Docs build
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "docs/**"
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "docs/**"
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
@ -16,7 +12,27 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
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.ref_name == 'main' }}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
build:
|
||||
name: Docs Build
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
|
39
.github/workflows/docs-deploy.yml
vendored
@ -7,13 +7,32 @@ on:
|
||||
|
||||
jobs:
|
||||
checks:
|
||||
name: Docs Deploy Checks
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
parameters: ${{ steps.parameters.outputs.result }}
|
||||
artifact: ${{ steps.get-artifact.outputs.result }}
|
||||
steps:
|
||||
- if: ${{ github.event.workflow_run.conclusion == 'failure' }}
|
||||
run: echo 'The triggering workflow failed' && exit 1
|
||||
|
||||
- if: ${{ github.event.workflow_run.conclusion != 'success' }}
|
||||
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
|
||||
id: parameters
|
||||
uses: actions/github-script@v7
|
||||
@ -73,9 +92,10 @@ jobs:
|
||||
return parameters;
|
||||
|
||||
deploy:
|
||||
name: Docs Deploy
|
||||
runs-on: ubuntu-latest
|
||||
needs: checks
|
||||
if: ${{ fromJson(needs.checks.outputs.parameters).shouldDeploy }}
|
||||
if: ${{ fromJson(needs.checks.outputs.artifact).found && fromJson(needs.checks.outputs.parameters).shouldDeploy }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
@ -98,18 +118,11 @@ jobs:
|
||||
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];
|
||||
let artifact = ${{ needs.checks.outputs.artifact }};
|
||||
let download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: matchArtifact.id,
|
||||
artifact_id: artifact.id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
let fs = require('fs');
|
||||
|
3
.github/workflows/docs-destroy.yml
vendored
@ -5,6 +5,7 @@ on:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: Docs Destroy
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@ -22,7 +23,7 @@ jobs:
|
||||
tg_version: "0.58.12"
|
||||
tofu_version: "1.7.1"
|
||||
tg_dir: "deployment/modules/cloudflare/docs"
|
||||
tg_command: "destroy"
|
||||
tg_command: "destroy -refresh=false"
|
||||
|
||||
- name: Comment
|
||||
uses: actions-cool/maintain-one-comment@v3
|
||||
|
52
.github/workflows/fix-format.yml
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
name: Fix formatting
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
|
||||
jobs:
|
||||
fix-formatting:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.label.name == 'fix:formatting' }}
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Generate a token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: './server/.nvmrc'
|
||||
|
||||
- name: Fix formatting
|
||||
run: make install-all && make format-all
|
||||
|
||||
- name: Commit and push
|
||||
uses: EndBug/add-and-commit@v9
|
||||
with:
|
||||
default_author: github_actions
|
||||
message: 'chore: fix formatting'
|
||||
|
||||
- name: Remove label
|
||||
uses: actions/github-script@v7
|
||||
if: always()
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.removeLabel({
|
||||
issue_number: context.payload.pull_request.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name: 'fix:formatting'
|
||||
})
|
||||
|
21
.github/workflows/pr-label-validation.yml
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
name: PR Label Validation
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, labeled, unlabeled, synchronize]
|
||||
|
||||
jobs:
|
||||
validate-release-label:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Require PR to have a changelog label
|
||||
uses: mheap/github-action-required-labels@v5
|
||||
with:
|
||||
mode: exactly
|
||||
count: 1
|
||||
use_regex: true
|
||||
labels: "changelog:.*"
|
||||
add_comment: true
|
15
.github/workflows/prepare-release.yml
vendored
@ -29,10 +29,17 @@ jobs:
|
||||
ref: ${{ steps.push-tag.outputs.commit_long_sha }}
|
||||
|
||||
steps:
|
||||
- name: Generate a token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.ORG_RELEASE_TOKEN }}
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
|
||||
- name: Install Poetry
|
||||
run: pipx install poetry
|
||||
@ -44,10 +51,8 @@ jobs:
|
||||
id: push-tag
|
||||
uses: EndBug/add-and-commit@v9
|
||||
with:
|
||||
author_name: Alex The Bot
|
||||
author_email: alex.tran1502@gmail.com
|
||||
default_author: user_info
|
||||
message: 'Version ${{ env.IMMICH_VERSION }}'
|
||||
default_author: github_actions
|
||||
message: 'chore: version ${{ env.IMMICH_VERSION }}'
|
||||
tag: ${{ env.IMMICH_VERSION }}
|
||||
push: true
|
||||
|
||||
|
23
.github/workflows/static_analysis.yml
vendored
@ -10,8 +10,27 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
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:
|
||||
name: Run Dart Code Analysis
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run == 'true' }}
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@ -37,6 +56,10 @@ jobs:
|
||||
run: dart format lib/ --set-exit-if-changed
|
||||
working-directory: ./mobile
|
||||
|
||||
- name: Run dart custom_lint
|
||||
run: dart run custom_lint
|
||||
working-directory: ./mobile
|
||||
|
||||
# Enable after riverpod generator migration is completed
|
||||
# - name: Run dart custom lint
|
||||
# run: dart run custom_lint
|
||||
|
170
.github/workflows/test.yml
vendored
@ -10,8 +10,48 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
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/**'
|
||||
- 'i18n/**'
|
||||
- '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:
|
||||
name: Server
|
||||
name: Test & Lint Server
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
@ -41,12 +81,14 @@ jobs:
|
||||
run: npm run check
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run unit tests & coverage
|
||||
- name: Run small tests & coverage
|
||||
run: npm run test:cov
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
cli-unit-tests:
|
||||
name: CLI
|
||||
name: Unit Test CLI
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_cli == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
@ -85,7 +127,9 @@ jobs:
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
cli-unit-tests-win:
|
||||
name: CLI (Windows)
|
||||
name: Unit Test CLI (Windows)
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_cli == 'true' }}
|
||||
runs-on: windows-latest
|
||||
defaults:
|
||||
run:
|
||||
@ -117,7 +161,9 @@ jobs:
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
web-unit-tests:
|
||||
name: Web
|
||||
name: Test & Lint Web
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_web == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
@ -159,13 +205,74 @@ jobs:
|
||||
run: npm run test:cov
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
e2e-tests:
|
||||
name: End-to-End Tests
|
||||
e2e-tests-lint:
|
||||
name: End-to-End Lint
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_e2e == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
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() }}
|
||||
|
||||
medium-tests-server:
|
||||
name: Medium Tests (Server)
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
||||
runs-on: mich
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Production build
|
||||
if: ${{ !cancelled() }}
|
||||
run: docker compose -f e2e/docker-compose.yml build
|
||||
|
||||
- name: Run medium tests
|
||||
if: ${{ !cancelled() }}
|
||||
run: make test-medium
|
||||
|
||||
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:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
@ -191,16 +298,41 @@ jobs:
|
||||
run: npm ci
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run linter
|
||||
run: npm run lint
|
||||
- name: Docker build
|
||||
run: docker compose build
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run formatter
|
||||
run: npm run format
|
||||
- name: Run e2e tests (api & cli)
|
||||
run: npm run test
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run tsc
|
||||
run: npm run check
|
||||
e2e-tests-web:
|
||||
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() }}
|
||||
|
||||
- name: Install Playwright Browsers
|
||||
@ -211,16 +343,14 @@ jobs:
|
||||
run: docker compose build
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run e2e tests (api & cli)
|
||||
run: npm run test
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run e2e tests (web)
|
||||
run: npx playwright test
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
mobile-unit-tests:
|
||||
name: Mobile
|
||||
name: Unit Test Mobile
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_mobile == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@ -234,7 +364,9 @@ jobs:
|
||||
run: flutter test -j 1
|
||||
|
||||
ml-unit-tests:
|
||||
name: Machine Learning
|
||||
name: Unit Test ML
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_ml == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
|
2
.gitignore
vendored
@ -21,3 +21,5 @@ mobile/openapi/.openapi-generator/FILES
|
||||
open-api/typescript-sdk/build
|
||||
mobile/android/fastlane/report.xml
|
||||
mobile/ios/fastlane/report.xml
|
||||
|
||||
vite.config.js.timestamp-*
|
||||
|
2
.gitmodules
vendored
@ -1,6 +1,6 @@
|
||||
[submodule "mobile/.isar"]
|
||||
path = mobile/.isar
|
||||
url = https://github.com/isar/isar
|
||||
[submodule "server/test/assets"]
|
||||
[submodule "e2e/test-assets"]
|
||||
path = e2e/test-assets
|
||||
url = https://github.com/immich-app/test-assets
|
||||
|
8
.vscode/launch.json
vendored
@ -5,8 +5,8 @@
|
||||
"type": "node",
|
||||
"request": "attach",
|
||||
"restart": true,
|
||||
"port": 9230,
|
||||
"name": "Immich Server",
|
||||
"port": 9231,
|
||||
"name": "Immich API Server",
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"localRoot": "${workspaceFolder}/server"
|
||||
},
|
||||
@ -14,8 +14,8 @@
|
||||
"type": "node",
|
||||
"request": "attach",
|
||||
"restart": true,
|
||||
"port": 9231,
|
||||
"name": "Immich Microservices",
|
||||
"port": 9230,
|
||||
"name": "Immich Workers",
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"localRoot": "${workspaceFolder}/server"
|
||||
}
|
||||
|
12
Makefile
@ -66,6 +66,18 @@ test-e2e:
|
||||
docker compose -f ./e2e/docker-compose.yml build
|
||||
npm --prefix e2e run test
|
||||
npm --prefix e2e run test:web
|
||||
test-medium:
|
||||
docker run \
|
||||
--rm \
|
||||
-v ./server/src:/usr/src/app/src \
|
||||
-v ./server/test:/usr/src/app/test \
|
||||
-v ./server/vitest.config.medium.mjs:/usr/src/app/vitest.config.medium.mjs \
|
||||
-v ./server/tsconfig.json:/usr/src/app/tsconfig.json \
|
||||
-e NODE_ENV=development \
|
||||
immich-server:latest \
|
||||
-c "npm ci && npm run test:medium -- --run"
|
||||
test-medium-dev:
|
||||
docker exec -it immich_server /bin/sh -c "npm run test:medium"
|
||||
|
||||
build-all: $(foreach M,$(MODULES),build-$M) ;
|
||||
install-all: $(foreach M,$(MODULES),install-$M) ;
|
||||
|
37
README.md
@ -17,23 +17,24 @@
|
||||
<img src="design/immich-screenshots.png" title="Main Screenshot">
|
||||
</a>
|
||||
<br/>
|
||||
|
||||
<p align="center">
|
||||
|
||||
<a href="readme_i18n/README_ca_ES.md">Català</a>
|
||||
<a href="readme_i18n/README_es_ES.md">Español</a>
|
||||
<a href="readme_i18n/README_fr_FR.md">Français</a>
|
||||
<a href="readme_i18n/README_it_IT.md">Italiano</a>
|
||||
<a href="readme_i18n/README_ja_JP.md">日本語</a>
|
||||
<a href="readme_i18n/README_ko_KR.md">한국어</a>
|
||||
<a href="readme_i18n/README_de_DE.md">Deutsch</a>
|
||||
<a href="readme_i18n/README_nl_NL.md">Nederlands</a>
|
||||
<a href="readme_i18n/README_tr_TR.md">Türkçe</a>
|
||||
<a href="readme_i18n/README_zh_CN.md">中文</a>
|
||||
<a href="readme_i18n/README_ru_RU.md">Русский</a>
|
||||
<a href="readme_i18n/README_pt_BR.md">Português Brasileiro</a>
|
||||
<a href="readme_i18n/README_sv_SE.md">Svenska</a>
|
||||
<a href="readme_i18n/README_ar_JO.md">العربية</a>
|
||||
|
||||
<a href="readme_i18n/README_ca_ES.md">Català</a>
|
||||
<a href="readme_i18n/README_es_ES.md">Español</a>
|
||||
<a href="readme_i18n/README_fr_FR.md">Français</a>
|
||||
<a href="readme_i18n/README_it_IT.md">Italiano</a>
|
||||
<a href="readme_i18n/README_ja_JP.md">日本語</a>
|
||||
<a href="readme_i18n/README_ko_KR.md">한국어</a>
|
||||
<a href="readme_i18n/README_de_DE.md">Deutsch</a>
|
||||
<a href="readme_i18n/README_nl_NL.md">Nederlands</a>
|
||||
<a href="readme_i18n/README_tr_TR.md">Türkçe</a>
|
||||
<a href="readme_i18n/README_zh_CN.md">中文</a>
|
||||
<a href="readme_i18n/README_ru_RU.md">Русский</a>
|
||||
<a href="readme_i18n/README_pt_BR.md">Português Brasileiro</a>
|
||||
<a href="readme_i18n/README_sv_SE.md">Svenska</a>
|
||||
<a href="readme_i18n/README_ar_JO.md">العربية</a>
|
||||
<a href="readme_i18n/README_vi_VN.md">Tiếng Việt</a>
|
||||
<a href="readme_i18n/README_th_TH.md">ภาษาไทย</a>
|
||||
</p>
|
||||
|
||||
## Disclaimer
|
||||
@ -92,7 +93,7 @@ For the mobile app, you can use `https://demo.immich.app/api` for the `Server En
|
||||
| LivePhoto/MotionPhoto backup and playback | Yes | Yes |
|
||||
| Support 360 degree image display | No | Yes |
|
||||
| User-defined storage structure | Yes | Yes |
|
||||
| Public Sharing | No | Yes |
|
||||
| Public Sharing | Yes | Yes |
|
||||
| Archive and Favorites | Yes | Yes |
|
||||
| Global Map | Yes | Yes |
|
||||
| Partner Sharing | Yes | Yes |
|
||||
@ -101,6 +102,8 @@ For the mobile app, you can use `https://demo.immich.app/api` for the `Server En
|
||||
| Offline support | Yes | No |
|
||||
| Read-only gallery | Yes | Yes |
|
||||
| Stacked Photos | Yes | Yes |
|
||||
| Tags | No | Yes |
|
||||
| Folder View | No | Yes |
|
||||
|
||||
## Translations
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
/dist
|
@ -1,28 +0,0 @@
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
sourceType: 'module',
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended', 'plugin:unicorn/recommended'],
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
},
|
||||
ignorePatterns: ['.eslintrc.js'],
|
||||
rules: {
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-floating-promises': 'error',
|
||||
'unicorn/prefer-module': 'off',
|
||||
'unicorn/prevent-abbreviations': 'off',
|
||||
'unicorn/no-process-exit': 'off',
|
||||
'unicorn/import-style': 'off',
|
||||
curly: 2,
|
||||
'prettier/prettier': 0,
|
||||
},
|
||||
};
|
@ -1 +1 @@
|
||||
20.16.0
|
||||
22.11.0
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM node:20.16.0-alpine3.20@sha256:eb8101caae9ac02229bd64c024919fe3d4504ff7f329da79ca60a04db08cef52 as core
|
||||
FROM node:22.10.0-alpine3.20@sha256:fc95a044b87e95507c60c1f8c829e5d98ddf46401034932499db370c494ef0ff AS core
|
||||
|
||||
WORKDIR /usr/src/open-api/typescript-sdk
|
||||
COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./
|
||||
|
@ -4,8 +4,18 @@ Please see the [Immich CLI documentation](https://immich.app/docs/features/comma
|
||||
|
||||
# For developers
|
||||
|
||||
Before building the CLI, you must build the immich server and the open-api client. To build the server run the following in the server folder:
|
||||
|
||||
$ npm install
|
||||
$ npm run build
|
||||
|
||||
Then, to build the open-api client run the following in the open-api folder:
|
||||
|
||||
$ ./bin/generate-open-api.sh
|
||||
|
||||
To run the Immich CLI from source, run the following in the cli folder:
|
||||
|
||||
$ npm install
|
||||
$ npm run build
|
||||
$ ts-node .
|
||||
|
||||
@ -17,3 +27,4 @@ You can also build and install the CLI using
|
||||
|
||||
$ npm run build
|
||||
$ npm install -g .
|
||||
****
|
||||
|
61
cli/eslint.config.mjs
Normal file
@ -0,0 +1,61 @@
|
||||
import { FlatCompat } from '@eslint/eslintrc';
|
||||
import js from '@eslint/js';
|
||||
import typescriptEslint from '@typescript-eslint/eslint-plugin';
|
||||
import tsParser from '@typescript-eslint/parser';
|
||||
import globals from 'globals';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all,
|
||||
});
|
||||
|
||||
export default [
|
||||
{
|
||||
ignores: ['eslint.config.mjs', 'dist'],
|
||||
},
|
||||
...compat.extends(
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
'plugin:unicorn/recommended',
|
||||
),
|
||||
{
|
||||
plugins: {
|
||||
'@typescript-eslint': typescriptEslint,
|
||||
},
|
||||
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.node,
|
||||
},
|
||||
|
||||
parser: tsParser,
|
||||
ecmaVersion: 5,
|
||||
sourceType: 'module',
|
||||
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
},
|
||||
|
||||
rules: {
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-floating-promises': 'error',
|
||||
'unicorn/prefer-module': 'off',
|
||||
'unicorn/prevent-abbreviations': 'off',
|
||||
'unicorn/no-process-exit': 'off',
|
||||
'unicorn/import-style': 'off',
|
||||
curly: 2,
|
||||
'prettier/prettier': 0,
|
||||
'object-shorthand': ['error', 'always'],
|
||||
},
|
||||
},
|
||||
];
|
1337
cli/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@immich/cli",
|
||||
"version": "2.2.13",
|
||||
"version": "2.2.28",
|
||||
"description": "Command Line Interface (CLI) for Immich",
|
||||
"type": "module",
|
||||
"exports": "./dist/index.js",
|
||||
@ -13,30 +13,33 @@
|
||||
"cli"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.1.0",
|
||||
"@eslint/js": "^9.8.0",
|
||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||
"@types/byte-size": "^8.1.0",
|
||||
"@types/cli-progress": "^3.11.0",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/mock-fs": "^4.13.1",
|
||||
"@types/node": "^20.14.12",
|
||||
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
||||
"@typescript-eslint/parser": "^7.0.0",
|
||||
"@types/node": "^22.8.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||
"@typescript-eslint/parser": "^8.0.0",
|
||||
"@vitest/coverage-v8": "^2.0.5",
|
||||
"byte-size": "^9.0.0",
|
||||
"cli-progress": "^3.12.0",
|
||||
"commander": "^12.0.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint": "^9.0.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-unicorn": "^55.0.0",
|
||||
"globals": "^15.9.0",
|
||||
"mock-fs": "^5.2.0",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier-plugin-organize-imports": "^4.0.0",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^5.0.12",
|
||||
"vite-tsconfig-paths": "^4.3.2",
|
||||
"vite-tsconfig-paths": "^5.0.0",
|
||||
"vitest": "^2.0.5",
|
||||
"vitest-fetch-mock": "^0.3.0",
|
||||
"vitest-fetch-mock": "^0.4.0",
|
||||
"yaml": "^2.3.1"
|
||||
},
|
||||
"scripts": {
|
||||
@ -64,6 +67,6 @@
|
||||
"lodash-es": "^4.17.21"
|
||||
},
|
||||
"volta": {
|
||||
"node": "20.16.0"
|
||||
"node": "22.11.0"
|
||||
}
|
||||
}
|
||||
|
@ -115,17 +115,7 @@ const tests: Test[] = [
|
||||
'/albums/image3.jpg': true,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: 'should support globbing paths',
|
||||
options: {
|
||||
pathsToCrawl: ['/photos*'],
|
||||
},
|
||||
files: {
|
||||
'/photos1/image1.jpg': true,
|
||||
'/photos2/image2.jpg': true,
|
||||
'/images/image3.jpg': false,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
test: 'should crawl a single path without trailing slash',
|
||||
options: {
|
||||
|
@ -141,25 +141,21 @@ export const crawl = async (options: CrawlOptions): Promise<string[]> => {
|
||||
}
|
||||
}
|
||||
|
||||
let searchPattern: string;
|
||||
if (patterns.length === 1) {
|
||||
searchPattern = patterns[0];
|
||||
} else if (patterns.length === 0) {
|
||||
if (patterns.length === 0) {
|
||||
return crawledFiles;
|
||||
} else {
|
||||
searchPattern = '{' + patterns.join(',') + '}';
|
||||
}
|
||||
|
||||
if (recursive) {
|
||||
searchPattern = searchPattern + '/**/';
|
||||
}
|
||||
const searchPatterns = patterns.map((pattern) => {
|
||||
let escapedPattern = pattern;
|
||||
if (recursive) {
|
||||
escapedPattern = escapedPattern + '/**';
|
||||
}
|
||||
return `${escapedPattern}/*.{${extensions.join(',')}}`;
|
||||
});
|
||||
|
||||
searchPattern = `${searchPattern}/*.{${extensions.join(',')}}`;
|
||||
|
||||
const globbedFiles = await glob(searchPattern, {
|
||||
const globbedFiles = await glob(searchPatterns, {
|
||||
absolute: true,
|
||||
caseSensitiveMatch: false,
|
||||
onlyFiles: true,
|
||||
dot: includeHidden,
|
||||
ignore: [`**/${exclusionPattern}`],
|
||||
});
|
||||
|
@ -2,37 +2,37 @@
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.opentofu.org/cloudflare/cloudflare" {
|
||||
version = "4.38.0"
|
||||
constraints = "4.38.0"
|
||||
version = "4.45.0"
|
||||
constraints = "4.45.0"
|
||||
hashes = [
|
||||
"h1:+27KAHKHBDvv3dqyJv5vhtdKQZJzoZXoMqIyronlHNw=",
|
||||
"h1:/uV9RgOUhkxElkHhWs8fs5ZbX9vj6RCBfP0oJO0JF30=",
|
||||
"h1:1DNAdMugJJOAWD/XYiZenYYZLy7fw2ctjT4YZmkRCVQ=",
|
||||
"h1:1wn4PmCLdT7mvd74JkCGmJDJxTQDkcxc+1jNbmwnMHA=",
|
||||
"h1:BIHB4fBxHg2bA9KbL92njhyctxKC8b6hNDp60y5QBss=",
|
||||
"h1:HCQpvKPsMsR4HO5eDqt+Kao7T7CYeEH7KZIO7xMcC6M=",
|
||||
"h1:HTomuzocukpNLwtWzeSF3yteCVsyVKbwKmN66u9iPac=",
|
||||
"h1:YDxsUBhBAwHSXLzVwrSlSBOwv1NvLyry7s5SfCV7VqQ=",
|
||||
"h1:dchVhxo+Acd1l2RuZ88tW9lWj4422QMfgtxKvKCjYrw=",
|
||||
"h1:eypa+P4ZpsEGMPFuCE+6VkRefu0TZRFmVBOpK+PDOPY=",
|
||||
"h1:f3yjse2OsRZj7ZhR7BLintJMlI4fpyt8HyDP/zcEavw=",
|
||||
"h1:mSJ7xj8K+xcnEmGg7lH0jjzyQb157wH94ULTAlIV+HQ=",
|
||||
"h1:tt+2J2Ze8VIdDq2Hr6uHlTJzAMBRpErBwTYx0uD5ilE=",
|
||||
"h1:uQW8SKxmulqrAisO+365mIf2FueINAp5PY28bqCPCug=",
|
||||
"zh:171ab67cccceead4514fafb2d39e4e708a90cce79000aaf3c29aab7ed4457071",
|
||||
"zh:18aa7228447baaaefc49a43e8eff970817a7491a63d8937e796357a3829dd979",
|
||||
"zh:2cbaab6092e81ba6f41fa60a50f14e980c8ec327ee11d0b21f16a478be4b7567",
|
||||
"zh:53b8e49c06f5b31a8c681f8c0669cf43e78abe71657b8182a221d096bb514965",
|
||||
"zh:6037cfc60b4b647aabae155fcb46d649ed7c650e0287f05db52b2068f1e27c8a",
|
||||
"zh:62460982ce1a869eebfca675603fbbd50416cf6b69459fb855bfbe5ae2b97607",
|
||||
"zh:65f6f3a8470917b6398baa5eb4f74b3932b213eac7c0202798bfad6fd1ee17df",
|
||||
"h1:/CGpnYMkLRDmqn4iAsh/jg7ELZ6QExUw03VdjKZyK5M=",
|
||||
"h1:82C/ryqwQvxhBINYOOyF5ZzPW/k4zJ/RYT13eCdPgEc=",
|
||||
"h1:8Wu1D7ZwbLGdHakLRAzoAJ5VqZ8I14qzkPv1OGNfIlg=",
|
||||
"h1:CVq0CAibeueOuiNk0UQtwZvMLMof33n1BgskFPOymrk=",
|
||||
"h1:FSS5Kq+L+CX1zARy8PhaF8edBFNgsLtds4Uo8MwJiK8=",
|
||||
"h1:L4qsorLII7f8xSFmv6JOoWfLWDunWQEpK964Bxk7mtM=",
|
||||
"h1:StO3PV5PDskSCnhoHhWHOPxu6hbzJUQggfLgOSkvhwg=",
|
||||
"h1:Tjo+Er9ets5YrTRIdP9LBmi4p89nL/W+A7r8a1MM9nI=",
|
||||
"h1:XIwT+AWvks1LTytePM9zls+O8ItxoqCfPOgHwuH9ivQ=",
|
||||
"h1:aOXn/zuM1+5GGy/SSRx8q4EYCSTFE9Tr0twHPIf5/KE=",
|
||||
"h1:lb+YcuZ4guYd8zE51vgSnDsRAD9IV00Z15l1i1X52s8=",
|
||||
"h1:pYwNXGjfXA2rUEmotGMLWgmavT9D2rdHnV3TpuIK3ko=",
|
||||
"h1:q1qrnPq6KkljwBrugCwzb7f0SVP4Lzkfh+EOLARY9V8=",
|
||||
"h1:v9sL4cZLTV5Gu2004DDyy7209gT0JmudBCAD0WCr/JE=",
|
||||
"zh:00be2a6adc76615a368491c7a026098103b6286deb31e3cfb037365dd39f095f",
|
||||
"zh:05bd072e6119f7a5abff05c6064001f745473119a956586cf77ae843cf55d666",
|
||||
"zh:228bbe61345c4e8e0bc6b698b4b9652abff65662ee72ede2aecb4c3efb91b243",
|
||||
"zh:2948aeefe71ba041c94082cf931ecc95510d93af0a61d0a287880f5b9d24b11a",
|
||||
"zh:5dfc2c5e95843ca54957212ee3ecb7ff06f2cf60bfd6ca278b5249fd70ac18f5",
|
||||
"zh:69922cb45559b0b0544b9c2d31ed2d0fac9121faa75bc2f523484785b45d8e2b",
|
||||
"zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f",
|
||||
"zh:8b5cebe64bf04105a49178a165b6a8800a9a33bae6767143a47fe4977755f805",
|
||||
"zh:a5596635db0993ee3c3060fbc2227d91b239466e96d2d82642625a5aa2486988",
|
||||
"zh:b3a9c63038441f13c311fd4b2c7e69e571445e5a7365a20c7cc9046b7e6c8aba",
|
||||
"zh:b585e7e4d7648a540b14b9182819214896ca9337729eeb1f2034833b17db754d",
|
||||
"zh:d2c3c545318ac8542369e9fc8228e29ee585febdf203a450fad3e0eded71ce02",
|
||||
"zh:e95dd2d6c3525073af47d47b763cb81b6a51b20cabf76f789c69328922da9ecf",
|
||||
"zh:eee6e590b36d6c6168a7daae8afa74a8721fd7aa9f62a710f04a311975100722",
|
||||
"zh:9d83a0cbf72327286f7dbd63cd4af89059c648163fe6ed21b1df768e0518d445",
|
||||
"zh:a8e1982945822c7d7aaa6ba8602c7247d1a3fad15d612f30eb323491a637bf8d",
|
||||
"zh:c6d41ebd69ddb23e3dad49a0ebf1da5a9c7d8706a4f55d953115d371f407928b",
|
||||
"zh:d03e5442b12846c2737f099d30cd23d9f85a0c6d65437ccb44819f9a6c4e1d7f",
|
||||
"zh:d446f2e1186b35037aea03b0e27d8b032d2f069f194f84b3f0e2907b3a79a955",
|
||||
"zh:e4d7549a4c856524e01f3dd4d69f57119ea205f7a0fa38dcfe154475b4ae9258",
|
||||
"zh:e64b8915cb9686f85e77115bd674f2faf4f29880688067d7d0f1376566fdb3b0",
|
||||
"zh:f046efdc55e6385cdd69baaa06a929bef9fe6809d373b0d2d6c7df8f8c23eddc",
|
||||
]
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ terraform {
|
||||
required_providers {
|
||||
cloudflare = {
|
||||
source = "cloudflare/cloudflare"
|
||||
version = "4.38.0"
|
||||
version = "4.45.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,6 @@ resource "cloudflare_record" "immich_app_release_domain" {
|
||||
proxied = true
|
||||
ttl = 1
|
||||
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
|
||||
}
|
||||
|
@ -2,37 +2,37 @@
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.opentofu.org/cloudflare/cloudflare" {
|
||||
version = "4.38.0"
|
||||
constraints = "4.38.0"
|
||||
version = "4.45.0"
|
||||
constraints = "4.45.0"
|
||||
hashes = [
|
||||
"h1:+27KAHKHBDvv3dqyJv5vhtdKQZJzoZXoMqIyronlHNw=",
|
||||
"h1:/uV9RgOUhkxElkHhWs8fs5ZbX9vj6RCBfP0oJO0JF30=",
|
||||
"h1:1DNAdMugJJOAWD/XYiZenYYZLy7fw2ctjT4YZmkRCVQ=",
|
||||
"h1:1wn4PmCLdT7mvd74JkCGmJDJxTQDkcxc+1jNbmwnMHA=",
|
||||
"h1:BIHB4fBxHg2bA9KbL92njhyctxKC8b6hNDp60y5QBss=",
|
||||
"h1:HCQpvKPsMsR4HO5eDqt+Kao7T7CYeEH7KZIO7xMcC6M=",
|
||||
"h1:HTomuzocukpNLwtWzeSF3yteCVsyVKbwKmN66u9iPac=",
|
||||
"h1:YDxsUBhBAwHSXLzVwrSlSBOwv1NvLyry7s5SfCV7VqQ=",
|
||||
"h1:dchVhxo+Acd1l2RuZ88tW9lWj4422QMfgtxKvKCjYrw=",
|
||||
"h1:eypa+P4ZpsEGMPFuCE+6VkRefu0TZRFmVBOpK+PDOPY=",
|
||||
"h1:f3yjse2OsRZj7ZhR7BLintJMlI4fpyt8HyDP/zcEavw=",
|
||||
"h1:mSJ7xj8K+xcnEmGg7lH0jjzyQb157wH94ULTAlIV+HQ=",
|
||||
"h1:tt+2J2Ze8VIdDq2Hr6uHlTJzAMBRpErBwTYx0uD5ilE=",
|
||||
"h1:uQW8SKxmulqrAisO+365mIf2FueINAp5PY28bqCPCug=",
|
||||
"zh:171ab67cccceead4514fafb2d39e4e708a90cce79000aaf3c29aab7ed4457071",
|
||||
"zh:18aa7228447baaaefc49a43e8eff970817a7491a63d8937e796357a3829dd979",
|
||||
"zh:2cbaab6092e81ba6f41fa60a50f14e980c8ec327ee11d0b21f16a478be4b7567",
|
||||
"zh:53b8e49c06f5b31a8c681f8c0669cf43e78abe71657b8182a221d096bb514965",
|
||||
"zh:6037cfc60b4b647aabae155fcb46d649ed7c650e0287f05db52b2068f1e27c8a",
|
||||
"zh:62460982ce1a869eebfca675603fbbd50416cf6b69459fb855bfbe5ae2b97607",
|
||||
"zh:65f6f3a8470917b6398baa5eb4f74b3932b213eac7c0202798bfad6fd1ee17df",
|
||||
"h1:/CGpnYMkLRDmqn4iAsh/jg7ELZ6QExUw03VdjKZyK5M=",
|
||||
"h1:82C/ryqwQvxhBINYOOyF5ZzPW/k4zJ/RYT13eCdPgEc=",
|
||||
"h1:8Wu1D7ZwbLGdHakLRAzoAJ5VqZ8I14qzkPv1OGNfIlg=",
|
||||
"h1:CVq0CAibeueOuiNk0UQtwZvMLMof33n1BgskFPOymrk=",
|
||||
"h1:FSS5Kq+L+CX1zARy8PhaF8edBFNgsLtds4Uo8MwJiK8=",
|
||||
"h1:L4qsorLII7f8xSFmv6JOoWfLWDunWQEpK964Bxk7mtM=",
|
||||
"h1:StO3PV5PDskSCnhoHhWHOPxu6hbzJUQggfLgOSkvhwg=",
|
||||
"h1:Tjo+Er9ets5YrTRIdP9LBmi4p89nL/W+A7r8a1MM9nI=",
|
||||
"h1:XIwT+AWvks1LTytePM9zls+O8ItxoqCfPOgHwuH9ivQ=",
|
||||
"h1:aOXn/zuM1+5GGy/SSRx8q4EYCSTFE9Tr0twHPIf5/KE=",
|
||||
"h1:lb+YcuZ4guYd8zE51vgSnDsRAD9IV00Z15l1i1X52s8=",
|
||||
"h1:pYwNXGjfXA2rUEmotGMLWgmavT9D2rdHnV3TpuIK3ko=",
|
||||
"h1:q1qrnPq6KkljwBrugCwzb7f0SVP4Lzkfh+EOLARY9V8=",
|
||||
"h1:v9sL4cZLTV5Gu2004DDyy7209gT0JmudBCAD0WCr/JE=",
|
||||
"zh:00be2a6adc76615a368491c7a026098103b6286deb31e3cfb037365dd39f095f",
|
||||
"zh:05bd072e6119f7a5abff05c6064001f745473119a956586cf77ae843cf55d666",
|
||||
"zh:228bbe61345c4e8e0bc6b698b4b9652abff65662ee72ede2aecb4c3efb91b243",
|
||||
"zh:2948aeefe71ba041c94082cf931ecc95510d93af0a61d0a287880f5b9d24b11a",
|
||||
"zh:5dfc2c5e95843ca54957212ee3ecb7ff06f2cf60bfd6ca278b5249fd70ac18f5",
|
||||
"zh:69922cb45559b0b0544b9c2d31ed2d0fac9121faa75bc2f523484785b45d8e2b",
|
||||
"zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f",
|
||||
"zh:8b5cebe64bf04105a49178a165b6a8800a9a33bae6767143a47fe4977755f805",
|
||||
"zh:a5596635db0993ee3c3060fbc2227d91b239466e96d2d82642625a5aa2486988",
|
||||
"zh:b3a9c63038441f13c311fd4b2c7e69e571445e5a7365a20c7cc9046b7e6c8aba",
|
||||
"zh:b585e7e4d7648a540b14b9182819214896ca9337729eeb1f2034833b17db754d",
|
||||
"zh:d2c3c545318ac8542369e9fc8228e29ee585febdf203a450fad3e0eded71ce02",
|
||||
"zh:e95dd2d6c3525073af47d47b763cb81b6a51b20cabf76f789c69328922da9ecf",
|
||||
"zh:eee6e590b36d6c6168a7daae8afa74a8721fd7aa9f62a710f04a311975100722",
|
||||
"zh:9d83a0cbf72327286f7dbd63cd4af89059c648163fe6ed21b1df768e0518d445",
|
||||
"zh:a8e1982945822c7d7aaa6ba8602c7247d1a3fad15d612f30eb323491a637bf8d",
|
||||
"zh:c6d41ebd69ddb23e3dad49a0ebf1da5a9c7d8706a4f55d953115d371f407928b",
|
||||
"zh:d03e5442b12846c2737f099d30cd23d9f85a0c6d65437ccb44819f9a6c4e1d7f",
|
||||
"zh:d446f2e1186b35037aea03b0e27d8b032d2f069f194f84b3f0e2907b3a79a955",
|
||||
"zh:e4d7549a4c856524e01f3dd4d69f57119ea205f7a0fa38dcfe154475b4ae9258",
|
||||
"zh:e64b8915cb9686f85e77115bd674f2faf4f29880688067d7d0f1376566fdb3b0",
|
||||
"zh:f046efdc55e6385cdd69baaa06a929bef9fe6809d373b0d2d6c7df8f8c23eddc",
|
||||
]
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ terraform {
|
||||
required_providers {
|
||||
cloudflare = {
|
||||
source = "cloudflare/cloudflare"
|
||||
version = "4.38.0"
|
||||
version = "4.45.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ resource "cloudflare_record" "immich_app_branch_subdomain" {
|
||||
proxied = true
|
||||
ttl = 1
|
||||
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
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ output "immich_app_branch_subdomain" {
|
||||
}
|
||||
|
||||
output "immich_app_branch_pages_hostname" {
|
||||
value = cloudflare_record.immich_app_branch_subdomain.value
|
||||
value = cloudflare_record.immich_app_branch_subdomain.content
|
||||
}
|
||||
|
||||
output "pages_project_name" {
|
||||
|
@ -36,16 +36,22 @@ services:
|
||||
IMMICH_BUILD_URL: https://github.com/immich-app/immich/actions/runs/9654404849
|
||||
IMMICH_BUILD_IMAGE: development
|
||||
IMMICH_BUILD_IMAGE_URL: https://github.com/immich-app/immich/pkgs/container/immich-server
|
||||
IMMICH_THIRD_PARTY_SOURCE_URL: https://github.com/immich-app/immich/
|
||||
IMMICH_THIRD_PARTY_BUG_FEATURE_URL: https://github.com/immich-app/immich/issues
|
||||
IMMICH_THIRD_PARTY_DOCUMENTATION_URL: https://immich.app/docs
|
||||
IMMICH_THIRD_PARTY_SUPPORT_URL: https://immich.app/docs/third-party
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 1048576
|
||||
hard: 1048576
|
||||
ports:
|
||||
- 3001:3001
|
||||
- 9230:9230
|
||||
- 9231:9231
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
healthcheck:
|
||||
disable: false
|
||||
|
||||
immich-web:
|
||||
container_name: immich_web
|
||||
@ -60,6 +66,7 @@ services:
|
||||
- 24678:24678
|
||||
volumes:
|
||||
- ../web:/usr/src/app
|
||||
- ../i18n:/usr/src/i18n
|
||||
- ../open-api/:/usr/src/open-api/
|
||||
- /usr/src/app/node_modules
|
||||
ulimits:
|
||||
@ -91,10 +98,12 @@ services:
|
||||
depends_on:
|
||||
- database
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
disable: false
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: redis:6.2-alpine@sha256:e3b17ba9479deec4b7d1eeec1548a253acc5374d68d3b27937fcfe4df8d18c7e
|
||||
image: redis:6.2-alpine@sha256:2ba50e1ac3a0ea17b736ce9db2b0a9f6f8b85d4c27d5f5accc6a416d8f42c6d5
|
||||
healthcheck:
|
||||
test: redis-cli ping || exit 1
|
||||
|
||||
@ -134,7 +143,7 @@ services:
|
||||
'wal_compression=on',
|
||||
]
|
||||
|
||||
# set IMMICH_METRICS=true in .env to enable metrics
|
||||
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
|
||||
# immich-prometheus:
|
||||
# container_name: immich_prometheus
|
||||
# ports:
|
||||
|
@ -16,11 +16,13 @@ services:
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- 2283:3001
|
||||
- 2283:2283
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
restart: always
|
||||
healthcheck:
|
||||
disable: false
|
||||
|
||||
immich-machine-learning:
|
||||
container_name: immich_machine_learning
|
||||
@ -40,10 +42,12 @@ services:
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
healthcheck:
|
||||
disable: false
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: redis:6.2-alpine@sha256:e3b17ba9479deec4b7d1eeec1548a253acc5374d68d3b27937fcfe4df8d18c7e
|
||||
image: redis:6.2-alpine@sha256:2ba50e1ac3a0ea17b736ce9db2b0a9f6f8b85d4c27d5f5accc6a416d8f42c6d5
|
||||
healthcheck:
|
||||
test: redis-cli ping || exit 1
|
||||
restart: always
|
||||
@ -67,15 +71,30 @@ services:
|
||||
interval: 5m
|
||||
start_interval: 30s
|
||||
start_period: 5m
|
||||
command: ["postgres", "-c" ,"shared_preload_libraries=vectors.so", "-c", 'search_path="$$user", public, vectors', "-c", "logging_collector=on", "-c", "max_wal_size=2GB", "-c", "shared_buffers=512MB", "-c", "wal_compression=on"]
|
||||
command:
|
||||
[
|
||||
'postgres',
|
||||
'-c',
|
||||
'shared_preload_libraries=vectors.so',
|
||||
'-c',
|
||||
'search_path="$$user", public, vectors',
|
||||
'-c',
|
||||
'logging_collector=on',
|
||||
'-c',
|
||||
'max_wal_size=2GB',
|
||||
'-c',
|
||||
'shared_buffers=512MB',
|
||||
'-c',
|
||||
'wal_compression=on',
|
||||
]
|
||||
restart: always
|
||||
|
||||
# set IMMICH_METRICS=true in .env to enable metrics
|
||||
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
|
||||
immich-prometheus:
|
||||
container_name: immich_prometheus
|
||||
ports:
|
||||
- 9090:9090
|
||||
image: prom/prometheus@sha256:f20d3127bf2876f4a1df76246fca576b41ddf1125ed1c546fbd8b16ea55117e6
|
||||
image: prom/prometheus@sha256:378f4e03703557d1c6419e6caccf922f96e6d88a530f7431d66a4c4f4b1000fe
|
||||
volumes:
|
||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
- prometheus-data:/prometheus
|
||||
@ -87,7 +106,7 @@ services:
|
||||
command: ['./run.sh', '-disable-reporting']
|
||||
ports:
|
||||
- 3000:3000
|
||||
image: grafana/grafana:11.1.3-ubuntu@sha256:e10453733015f31103cb530425f32c994816b50102886fa885dafea2c50a711c
|
||||
image: grafana/grafana:11.3.0-ubuntu@sha256:51587e148ac0214d7938e7f3fe8512182e4eb6141892a3ffb88bba1901b49285
|
||||
volumes:
|
||||
- grafana-data:/var/lib/grafana
|
||||
|
||||
|
@ -22,11 +22,13 @@ services:
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- 2283:3001
|
||||
- '2283:2283'
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
restart: always
|
||||
healthcheck:
|
||||
disable: false
|
||||
|
||||
immich-machine-learning:
|
||||
container_name: immich_machine_learning
|
||||
@ -41,10 +43,12 @@ services:
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
healthcheck:
|
||||
disable: false
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: docker.io/redis:6.2-alpine@sha256:e3b17ba9479deec4b7d1eeec1548a253acc5374d68d3b27937fcfe4df8d18c7e
|
||||
image: docker.io/redis:6.2-alpine@sha256:2ba50e1ac3a0ea17b736ce9db2b0a9f6f8b85d4c27d5f5accc6a416d8f42c6d5
|
||||
healthcheck:
|
||||
test: redis-cli ping || exit 1
|
||||
restart: always
|
||||
@ -65,7 +69,22 @@ services:
|
||||
interval: 5m
|
||||
start_interval: 30s
|
||||
start_period: 5m
|
||||
command: ["postgres", "-c" ,"shared_preload_libraries=vectors.so", "-c", 'search_path="$$user", public, vectors', "-c", "logging_collector=on", "-c", "max_wal_size=2GB", "-c", "shared_buffers=512MB", "-c", "wal_compression=on"]
|
||||
command:
|
||||
[
|
||||
'postgres',
|
||||
'-c',
|
||||
'shared_preload_libraries=vectors.so',
|
||||
'-c',
|
||||
'search_path="$$user", public, vectors',
|
||||
'-c',
|
||||
'logging_collector=on',
|
||||
'-c',
|
||||
'max_wal_size=2GB',
|
||||
'-c',
|
||||
'shared_buffers=512MB',
|
||||
'-c',
|
||||
'wal_compression=on',
|
||||
]
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
|
@ -12,6 +12,7 @@ DB_DATA_LOCATION=./postgres
|
||||
IMMICH_VERSION=release
|
||||
|
||||
# Connection secret for postgres. You should change it to a random password
|
||||
# Please use only the characters `A-Za-z0-9`, without special characters or spaces
|
||||
DB_PASSWORD=postgres
|
||||
|
||||
# The values below this line do not need to be changed
|
||||
|
@ -51,5 +51,4 @@ services:
|
||||
volumes:
|
||||
- /usr/lib/wsl:/usr/lib/wsl
|
||||
environment:
|
||||
- LD_LIBRARY_PATH=/usr/lib/wsl/lib
|
||||
- LIBVA_DRIVER_NAME=d3d12
|
||||
|
@ -1 +1 @@
|
||||
20.16.0
|
||||
22.11.0
|
||||
|
@ -52,14 +52,25 @@ On iOS (iPhone and iPad), the operating system determines if a particular app ca
|
||||
- Disable Background App Refresh for apps that don't need background tasks to run. This will reduce the competition for background task invocation for Immich.
|
||||
- Use the Immich app more often.
|
||||
|
||||
### Why are features not working with a self-signed cert or mTLS?
|
||||
|
||||
Due to limitations in the upstream app/video library, using a self-signed TLS certificate or mutual TLS may break video playback or asset upload (both foreground and/or background).
|
||||
We recommend using a real SSL certificate from a free provider, for example [Let's Encrypt](https://letsencrypt.org/).
|
||||
|
||||
---
|
||||
|
||||
## Assets
|
||||
|
||||
### Does Immich change the file?
|
||||
|
||||
No, Immich does not touch the original file under any circumstances,
|
||||
all edited metadata are saved in the companion sidecar file and the database.
|
||||
No, Immich does not modify the original files.
|
||||
All edited metadata is saved in companion `.xmp` sidecar files and the database.
|
||||
However, Immich will delete original files that have been trashed when the trash is emptied in the Immich UI.
|
||||
|
||||
### Why do my file names appear as a random string in the file manager?
|
||||
|
||||
When Storage Template is off (default) Immich saves the file names in a random string (also known as random UUIDs) to prevent duplicate file names. To retrieve the original file names, you must enable the Storage Template and then run the STORAGE TEMPLATE MIGRATION job.
|
||||
It is recommended to read about [Storage Template](https://immich.app/docs/administration/storage-template) before activation.
|
||||
|
||||
### Can I add my existing photo library?
|
||||
|
||||
@ -157,13 +168,26 @@ We haven't implemented an official mechanism for creating albums from external l
|
||||
|
||||
Duplicate checking only exists for upload libraries, using the file hash. Furthermore, duplicate checking is not global, but _per library_. Therefore, a situation where the same file appears twice in the timeline is possible, especially for external libraries.
|
||||
|
||||
### Why are my edits to files not being saved in read-only external libraries?
|
||||
|
||||
Images in read-write external libraries (the default) can be edited as normal.
|
||||
In read-only libraries (`:ro` in the `docker-compose.yml`), Immich is unable to create the `.xmp` sidecar files to store edited file metadata.
|
||||
For this reason, the metadata (timestamp, location, description, star rating, etc.) cannot be edited for files in read-only external libraries.
|
||||
|
||||
### How are deletions of files handled in external libraries?
|
||||
|
||||
Immich will attempt to delete original files that have been trashed when the trash is emptied.
|
||||
In read-write external libraries (the default), Immich will delete the original file.
|
||||
In read-only libraries (`:ro` in the `docker-compose.yml`), files can still be trashed in the UI.
|
||||
However, when the trash is emptied, the files will re-appear in the main timeline since Immich is unable to delete the original file.
|
||||
|
||||
---
|
||||
|
||||
## Machine Learning
|
||||
|
||||
### How does smart search work?
|
||||
|
||||
Immich uses CLIP models. For more information about CLIP and its capabilities, read about it [here](https://openai.com/research/clip).
|
||||
Immich uses CLIP models. An ML model converts each image to an "embedding", which is essentially a string of numbers that semantically encodes what is in the image. The same is done for the text that you enter when you do a search, and that text embedding is then compared with those of the images to find similar ones. As such, there are no "tags", "labels", or "descriptions" generated that you can look at. For more information about CLIP and its capabilities, read about it [here](https://openai.com/research/clip).
|
||||
|
||||
### How does facial recognition work?
|
||||
|
||||
@ -294,6 +318,12 @@ You need to enable WebSockets on your reverse proxy.
|
||||
|
||||
Immich components are typically deployed using docker. To see logs for deployed docker containers, you can use the [Docker CLI](https://docs.docker.com/engine/reference/commandline/cli/), specifically the `docker logs` command. For examples, see [Docker Help](/docs/guides/docker-help.md).
|
||||
|
||||
### How can I reduce the log verbosity of Redis?
|
||||
|
||||
To decrease Redis logs, you can add the following line to the `redis:` section of the `docker-compose.yml`:
|
||||
|
||||
` command: redis-server --loglevel warning`
|
||||
|
||||
### How can I run Immich as a non-root user?
|
||||
|
||||
You can change the user in the container by setting the `user` argument in `docker-compose.yml` for each service.
|
||||
@ -303,7 +333,11 @@ You may need to add mount points or docker volumes for the following internal co
|
||||
- `immich-machine-learning:/.cache`
|
||||
- `redis:/data`
|
||||
|
||||
The non-root user/group needs read/write access to the volume mounts, including `UPLOAD_LOCATION`.
|
||||
The non-root user/group needs read/write access to the volume mounts, including `UPLOAD_LOCATION` and `/cache` for machine-learning.
|
||||
|
||||
:::note Docker Compose Volumes
|
||||
The Docker Compose top level volume element does not support non-root access, all of the above volumes must be local volume mounts.
|
||||
:::
|
||||
|
||||
For a further hardened system, you can add the following block to every container except for `immich_postgres`.
|
||||
|
||||
|
@ -21,6 +21,19 @@ The recommended way to backup and restore the Immich database is to use the `pg_
|
||||
It is not recommended to directly backup the `DB_DATA_LOCATION` folder. Doing so while the database is running can lead to a corrupted backup that cannot be restored.
|
||||
:::
|
||||
|
||||
### Automatic Database Backups
|
||||
|
||||
Immich will automatically create database backups by default. The backups are stored in `UPLOAD_LOCATION/backups`.
|
||||
You can adjust the schedule and amount of kept backups in the [admin settings](http://my.immich.app/admin/system-settings?isOpen=backup).
|
||||
By default, Immich will keep the last 14 backups and create a new backup every day at 2:00 AM.
|
||||
|
||||
#### Restoring
|
||||
|
||||
We hope to make restoring simpler in future versions, for now you can find the backups in the `UPLOAD_LOCATION/backups` folder on your host.
|
||||
Then please follow the steps in the following section for restoring the database.
|
||||
|
||||
### Manual Backup and Restore
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Linux system" label="Linux system" default>
|
||||
|
||||
@ -29,34 +42,38 @@ docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=postgre
|
||||
```
|
||||
|
||||
```bash title='Restore'
|
||||
docker compose down -v # CAUTION! Deletes all Immich data to start from scratch.
|
||||
# rm -rf DB_DATA_LOCATION # CAUTION! Deletes all Immich data to start from scratch.
|
||||
docker compose pull # Update to latest version of Immich (if desired)
|
||||
docker compose create # Create Docker containers for Immich apps without running them.
|
||||
docker compose down -v # CAUTION! Deletes all Immich data to start from scratch
|
||||
## Uncomment the next line and replace DB_DATA_LOCATION with your Postgres path to permanently reset the Postgres database
|
||||
# rm -rf DB_DATA_LOCATION # CAUTION! Deletes all Immich data to start from scratch
|
||||
docker compose pull # Update to latest version of Immich (if desired)
|
||||
docker compose create # Create Docker containers for Immich apps without running them
|
||||
docker start immich_postgres # Start Postgres server
|
||||
sleep 10 # Wait for Postgres server to start up
|
||||
sleep 10 # Wait for Postgres server to start up
|
||||
# Check the database user if you deviated from the default
|
||||
gunzip < "/path/to/backup/dump.sql.gz" \
|
||||
| sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" \
|
||||
| docker exec -i immich_postgres psql --username=postgres # Restore Backup
|
||||
docker compose up -d # Start remainder of Immich apps
|
||||
| docker exec -i immich_postgres psql --username=postgres # Restore Backup
|
||||
docker compose up -d # Start remainder of Immich apps
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="Windows system (PowerShell)" label="Windows system (PowerShell)">
|
||||
|
||||
```powershell title='Backup'
|
||||
docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=postgres > "\path\to\backup\dump.sql"
|
||||
docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=postgres | Set-Content -Encoding utf8 "C:\path\to\backup\dump.sql"
|
||||
```
|
||||
|
||||
```powershell title='Restore'
|
||||
docker compose down -v # CAUTION! Deletes all Immich data to start from scratch.
|
||||
# Remove-Item -Recurse -Force DB_DATA_LOCATION # CAUTION! Deletes all Immich data to start from scratch.
|
||||
docker compose pull # Update to latest version of Immich (if desired)
|
||||
docker compose create # Create Docker containers for Immich apps without running them.
|
||||
docker compose down -v # CAUTION! Deletes all Immich data to start from scratch
|
||||
## Uncomment the next line and replace DB_DATA_LOCATION with your Postgres path to permanently reset the Postgres database
|
||||
# Remove-Item -Recurse -Force DB_DATA_LOCATION # CAUTION! Deletes all Immich data to start from scratch
|
||||
docker compose pull # Update to latest version of Immich (if desired)
|
||||
docker compose create # Create Docker containers for Immich apps without running them
|
||||
docker start immich_postgres # Start Postgres server
|
||||
sleep 10 # Wait for Postgres server to start up
|
||||
gc "C:\path\to\backup\dump.sql" | docker exec -i immich_postgres psql --username=postgres # Restore Backup
|
||||
docker compose up -d # Start remainder of Immich apps
|
||||
sleep 10 # Wait for Postgres server to start up
|
||||
# Check the database user if you deviated from the default
|
||||
gc "C:\path\to\backup\dump.sql" | docker exec -i immich_postgres psql --username=postgres # Restore Backup
|
||||
docker compose up -d # Start remainder of Immich apps
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -68,6 +85,8 @@ Note that for the database restore to proceed properly, it requires a completely
|
||||
Some deployment methods make it difficult to start the database without also starting the server or microservices. In these cases, you may set the environmental variable `DB_SKIP_MIGRATIONS=true` before starting the services. This will prevent the server from running migrations that interfere with the restore process. Note that both the server and microservices must have this variable set to prevent the migrations from running. Be sure to remove this variable and restart the services after the database is restored.
|
||||
:::
|
||||
|
||||
### Automatic Database Backups
|
||||
|
||||
The database dumps can also be automated (using [this image](https://github.com/prodrigestivill/docker-postgres-backup-local)) by editing the docker compose file to match the following:
|
||||
|
||||
```yaml
|
||||
@ -97,6 +116,7 @@ services:
|
||||
Then you can restore with the same command but pointed at the latest dump.
|
||||
|
||||
```bash title='Automated Restore'
|
||||
# Be sure to check the username if you changed it from default
|
||||
gunzip < db_dumps/last/immich-latest.sql.gz \
|
||||
| sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" \
|
||||
| docker exec -i immich_postgres psql --username=postgres
|
||||
@ -157,7 +177,7 @@ for more info read the [release notes](https://github.com/immich-app/immich/rele
|
||||
|
||||
- The Immich database containing all the information to allow the system to function properly.
|
||||
**Note:** This folder will only appear to users who have made the changes mentioned in [v1.102.0](https://github.com/immich-app/immich/discussions/8930) (an optional, non-mandatory change) or who started with this version.
|
||||
- Stored in `UPLOAD_LOCATION/postgres`.
|
||||
- Stored in `DB_DATA_LOCATION`.
|
||||
|
||||
:::danger
|
||||
A backup of this folder does not constitute a backup of your database!
|
||||
@ -191,7 +211,7 @@ When you turn off the storage template engine, it will leave the assets in `UPLO
|
||||
- Stored in `UPLOAD_LOCATION/profile/<userID>`.
|
||||
- **Thumbs Images:**
|
||||
- Preview images (blurred, small, large) for each asset and thumbnails for recognized faces.
|
||||
- Stored in `UPLOCAD_LOCATION/thumbs/<userID>`.
|
||||
- Stored in `UPLOAD_LOCATION/thumbs/<userID>`.
|
||||
- **Encoded Assets:**
|
||||
- Videos that have been re-encoded from the original for wider compatibility. The original is not removed.
|
||||
- Stored in `UPLOAD_LOCATION/encoded-video/<userID>`.
|
||||
@ -203,7 +223,7 @@ When you turn off the storage template engine, it will leave the assets in `UPLO
|
||||
|
||||
- The Immich database containing all the information to allow the system to function properly.
|
||||
**Note:** This folder will only appear to users who have made the changes mentioned in [v1.102.0](https://github.com/immich-app/immich/discussions/8930) (an optional, non-mandatory change) or who started with this version.
|
||||
- Stored in `UPLOAD_LOCATION/postgres`.
|
||||
- Stored in `DB_DATA_LOCATION`.
|
||||
|
||||
:::danger
|
||||
A backup of this folder does not constitute a backup of your database!
|
||||
|
@ -8,13 +8,11 @@ Immich supports the option to send notifications via Email for the following eve
|
||||
|
||||
## SMTP settings
|
||||
|
||||
You can access the settings panel from the web at `Administration -> Settings -> Notification settings`
|
||||
You can access the settings panel from the web at `Administration -> Settings -> Notification settings`.
|
||||
|
||||
Under Email, enter the following details to connect with SMTP servers.
|
||||
Under Email, enter the required details to connect with an SMTP server.
|
||||
|
||||
You can use the following [guide](/docs/guides/smtp-gmail) to use Gmail's SMTP server.
|
||||
|
||||
<img src={require('./img/email-settings.png').default} width="80%" title="SMTP settings" />
|
||||
You can use [this guide](/docs/guides/smtp-gmail) to use Gmail's SMTP server.
|
||||
|
||||
## User's notifications settings
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 MiB |
BIN
docs/docs/administration/img/admin-jobs.webp
Normal file
After Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 218 KiB |
@ -22,7 +22,7 @@ Copy the entire `immich-server` block as a new service and make the following ch
|
||||
- container_name: immich_server
|
||||
...
|
||||
- ports:
|
||||
- - 2283:3001
|
||||
- - 2283:2283
|
||||
+ immich-microservices:
|
||||
+ container_name: immich_microservices
|
||||
```
|
||||
@ -52,4 +52,4 @@ Additionally, some jobs run on a schedule, which is every night at midnight. Thi
|
||||
Storage Migration job can be run after changing the [Storage Template](/docs/administration/storage-template.mdx), in order to apply the change to the existing library.
|
||||
:::
|
||||
|
||||
<img src={require('./img/admin-jobs.png').default} width="80%" title="Admin jobs" />
|
||||
<img src={require('./img/admin-jobs.webp').default} width="60%" title="Admin jobs" />
|
||||
|
@ -3,7 +3,7 @@
|
||||
This page contains details about using OAuth in Immich.
|
||||
|
||||
:::tip
|
||||
Unable to set `app.immich:/` as a valid redirect URI? See [Mobile Redirect URI](#mobile-redirect-uri) for an alternative solution.
|
||||
Unable to set `app.immich:///oauth-callback` as a valid redirect URI? See [Mobile Redirect URI](#mobile-redirect-uri) for an alternative solution.
|
||||
:::
|
||||
|
||||
## Overview
|
||||
@ -11,7 +11,7 @@ Unable to set `app.immich:/` as a valid redirect URI? See [Mobile Redirect URI](
|
||||
Immich supports 3rd party authentication via [OpenID Connect][oidc] (OIDC), an identity layer built on top of OAuth2. OIDC is supported by most identity providers, including:
|
||||
|
||||
- [Authentik](https://goauthentik.io/integrations/sources/oauth/#openid-connect)
|
||||
- [Authelia](https://www.authelia.com/configuration/identity-providers/openid-connect/clients/)
|
||||
- [Authelia](https://www.authelia.com/integration/openid-connect/immich/)
|
||||
- [Okta](https://www.okta.com/openid-connect/)
|
||||
- [Google](https://developers.google.com/identity/openid-connect/openid-connect)
|
||||
|
||||
@ -30,7 +30,7 @@ Before enabling OAuth in Immich, a new client application needs to be configured
|
||||
|
||||
The **Sign-in redirect URIs** should include:
|
||||
|
||||
- `app.immich:/` - for logging in with OAuth from the [Mobile App](/docs/features/mobile-app.mdx)
|
||||
- `app.immich:///oauth-callback` - for logging in with OAuth from the [Mobile App](/docs/features/mobile-app.mdx)
|
||||
- `http://DOMAIN:PORT/auth/login` - for logging in with OAuth from the Web Client
|
||||
- `http://DOMAIN:PORT/user-settings` - for manually linking OAuth in the Web Client
|
||||
|
||||
@ -38,7 +38,7 @@ Before enabling OAuth in Immich, a new client application needs to be configured
|
||||
|
||||
Mobile
|
||||
|
||||
- `app.immich:/` (You **MUST** include this for iOS and Android mobile apps to work properly)
|
||||
- `app.immich:///oauth-callback` (You **MUST** include this for iOS and Android mobile apps to work properly)
|
||||
|
||||
Localhost
|
||||
|
||||
@ -96,16 +96,16 @@ When Auto Launch is enabled, the login page will automatically redirect the user
|
||||
|
||||
## Mobile Redirect URI
|
||||
|
||||
The redirect URI for the mobile app is `app.immich:/`, which is a [Custom Scheme](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app). If this custom scheme is an invalid redirect URI for your OAuth Provider, you can work around this by doing the following:
|
||||
The redirect URI for the mobile app is `app.immich:///oauth-callback`, which is a [Custom Scheme](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app). If this custom scheme is an invalid redirect URI for your OAuth Provider, you can work around this by doing the following:
|
||||
|
||||
1. Configure an http(s) endpoint to forwards requests to `app.immich:/`
|
||||
1. Configure an http(s) endpoint to forwards requests to `app.immich:///oauth-callback`
|
||||
2. Whitelist the new endpoint as a valid redirect URI with your provider.
|
||||
3. Specify the new endpoint as the `Mobile Redirect URI Override`, in the OAuth settings.
|
||||
|
||||
With these steps in place, you should be able to use OAuth from the [Mobile App](/docs/features/mobile-app.mdx) without a custom scheme redirect URI.
|
||||
|
||||
:::info
|
||||
Immich has a route (`/api/oauth/mobile-redirect`) that is already configured to forward requests to `app.immich:/`, and can be used for step 1.
|
||||
Immich has a route (`/api/oauth/mobile-redirect`) that is already configured to forward requests to `app.immich:///oauth-callback`, and can be used for step 1.
|
||||
:::
|
||||
|
||||
## Example Configuration
|
||||
@ -154,21 +154,21 @@ Configuration of Authorised redirect URIs (Google Console)
|
||||
|
||||
Configuration of OAuth in Immich System Settings
|
||||
|
||||
| Setting | Value |
|
||||
| ---------------------------- | ------------------------------------------------------------------------------------------------------ |
|
||||
| Issuer URL | [https://accounts.google.com](https://accounts.google.com) |
|
||||
| Client ID | 7\***\*\*\*\*\*\*\***\*\*\***\*\*\*\*\*\*\***vuls.apps.googleusercontent.com |
|
||||
| Client Secret | G\***\*\*\*\*\*\*\***\*\*\***\*\*\*\*\*\*\***OO |
|
||||
| Scope | openid email profile |
|
||||
| Signing Algorithm | RS256 |
|
||||
| Storage Label Claim | preferred_username |
|
||||
| Storage Quota Claim | immich_quota |
|
||||
| Default Storage Quota (GiB) | 0 (0 for unlimited quota) |
|
||||
| Button Text | Sign in with Google (optional) |
|
||||
| Auto Register | Enabled (optional) |
|
||||
| Auto Launch | Enabled |
|
||||
| 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) |
|
||||
| Setting | Value |
|
||||
| ---------------------------- | ---------------------------------------------------------------------------- |
|
||||
| Issuer URL | `https://accounts.google.com` |
|
||||
| Client ID | 7\***\*\*\*\*\*\*\***\*\*\***\*\*\*\*\*\*\***vuls.apps.googleusercontent.com |
|
||||
| Client Secret | G\***\*\*\*\*\*\*\***\*\*\***\*\*\*\*\*\*\***OO |
|
||||
| Scope | openid email profile |
|
||||
| Signing Algorithm | RS256 |
|
||||
| Storage Label Claim | preferred_username |
|
||||
| Storage Quota Claim | immich_quota |
|
||||
| Default Storage Quota (GiB) | 0 (0 for unlimited quota) |
|
||||
| Button Text | Sign in with Google (optional) |
|
||||
| Auto Register | Enabled (optional) |
|
||||
| Auto Launch | Enabled |
|
||||
| Mobile Redirect URI Override | Enabled (required) |
|
||||
| Mobile Redirect URI | `https://example.immich.app/api/oauth/mobile-redirect` |
|
||||
|
||||
</details>
|
||||
|
||||
|
@ -13,9 +13,9 @@ Running with a pre-existing Postgres server can unlock powerful administrative f
|
||||
You must install pgvecto.rs into your instance of Postgres using their [instructions][vectors-install]. After installation, add `shared_preload_libraries = 'vectors.so'` to your `postgresql.conf`. If you already have some `shared_preload_libraries` set, you can separate each extension with a comma. For example, `shared_preload_libraries = 'pg_stat_statements, vectors.so'`.
|
||||
|
||||
:::note
|
||||
Immich is known to work with Postgres versions 14, 15, and 16. Earlier versions are unsupported.
|
||||
Immich is known to work with Postgres versions 14, 15, and 16. Earlier versions are unsupported. Postgres 17 is nominally compatible, but pgvecto.rs does not have prebuilt images or packages for it as of writing.
|
||||
|
||||
Make sure the installed version of pgvecto.rs is compatible with your version of Immich. For example, if your Immich version uses the dedicated database image `tensorchord/pgvecto-rs:pg14-v0.2.1`, you must install pgvecto.rs `>= 0.2.1, < 0.3.0`.
|
||||
Make sure the installed version of pgvecto.rs is compatible with your version of Immich. The current accepted range for pgvecto.rs is `>= 0.2.0, < 0.4.0`.
|
||||
:::
|
||||
|
||||
## Specifying the connection URL
|
||||
|
@ -1,5 +1,9 @@
|
||||
# Repair Page
|
||||
|
||||
:::warning
|
||||
This feature is currently disabled and will be reworked in the near future.
|
||||
:::
|
||||
|
||||
The repair page is designed to give information to the system administrator about files that are not tracked, or offline paths.
|
||||
|
||||
## Natural State
|
||||
|
@ -40,6 +40,26 @@ server {
|
||||
}
|
||||
```
|
||||
|
||||
#### Compatibility with Let's Encrypt
|
||||
|
||||
In the event that your nginx configuration includes a section for Let's Encrypt, it's likely that you have a segment similar to the following:
|
||||
|
||||
```nginx
|
||||
location ~ /.well-known {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
This particular `location` directive can inadvertently prevent mobile clients from reaching the `/.well-known/immich` path, which is crucial for discovery. Usual error message for this case is: "Your app major version is not compatible with the server". To remedy this, you should introduce an additional location block specifically for this path, ensuring that requests are correctly proxied to the Immich server:
|
||||
|
||||
```nginx
|
||||
location = /.well-known/immich {
|
||||
proxy_pass http://<backend_url>:2283;
|
||||
}
|
||||
```
|
||||
|
||||
By doing so, you'll maintain the functionality of Let's Encrypt while allowing mobile clients to access the necessary Immich path without obstruction.
|
||||
|
||||
### Caddy example config
|
||||
|
||||
As an alternative to nginx, you can also use [Caddy](https://caddyserver.com/) as a reverse proxy (with automatic HTTPS configuration). Below is an example config.
|
||||
@ -64,3 +84,43 @@ Below is an example config for Apache2 site configuration.
|
||||
ProxyPreserveHost On
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
### Traefik Proxy example config
|
||||
|
||||
The example below is for Traefik version 3.
|
||||
|
||||
The most important is to increase the `respondingTimeouts` of the entrypoint used by immich. In this example of entrypoint `websecure` for port `443`. Per default it's set to 60s which leeds to videos stop uploading after 1 minute (Error Code 499). With this config it will fail after 10 minutes which is in most cases enough. Increase it if needed.
|
||||
|
||||
`traefik.yaml`
|
||||
|
||||
```yaml
|
||||
[...]
|
||||
entryPoints:
|
||||
websecure:
|
||||
address: :443
|
||||
# this section needs to be added
|
||||
transport:
|
||||
respondingTimeouts:
|
||||
readTimeout: 600s
|
||||
idleTimeout: 600s
|
||||
writeTimeout: 600s
|
||||
```
|
||||
|
||||
The second part is in the `docker-compose.yml` file where immich is in. Add the Traefik specific labels like in the example.
|
||||
|
||||
`docker-compose.yml`
|
||||
|
||||
```yaml
|
||||
services:
|
||||
immich-server:
|
||||
[...]
|
||||
labels:
|
||||
traefik.enable: true
|
||||
# increase readingTimeouts for the entrypoint used here
|
||||
traefik.http.routers.immich.entrypoints: websecure
|
||||
traefik.http.routers.immich.rule: Host(`immich.your-domain.com`)
|
||||
traefik.http.services.immich.loadbalancer.server.port: 2283
|
||||
```
|
||||
|
||||
Keep in mind, that Traefik needs to communicate with the network where immich is in, usually done
|
||||
by adding the Traefik network to the `immich-server`.
|
||||
|
@ -7,7 +7,7 @@ If a storage quota has been defined for the user, the usage number will be displ
|
||||
:::
|
||||
|
||||
:::info External library
|
||||
External library is not included in the storage quota.
|
||||
External libraries are not included in the storage quota.
|
||||
:::
|
||||
|
||||
<img src={require('./img/server-stats.png').default} title="server statistic" />
|
||||
|
47
docs/docs/administration/system-integrity.md
Normal file
@ -0,0 +1,47 @@
|
||||
# System Integrity
|
||||
|
||||
## Folder checks
|
||||
|
||||
:::info
|
||||
The folders considered for these checks include: `upload/`, `library/`, `thumbs/`, `encoded-video/`, `profile/`
|
||||
:::
|
||||
|
||||
When Immich starts, it performs a series of checks in order to validate that it can read and write files to the volume mounts used by the storage system. If it cannot perform all the required operations, it will fail to start. The checks include:
|
||||
|
||||
- Creating an initial hidden file (`.immich`) in each folder
|
||||
- Reading a hidden file (`.immich`) in each folder
|
||||
- Overwriting a hidden file (`.immich`) in each folder
|
||||
|
||||
The checks are designed to catch the following situations:
|
||||
|
||||
- Incorrect permissions (cannot read/write files)
|
||||
- Missing volume mount (`.immich` files should exist, but are missing)
|
||||
|
||||
### Common issues
|
||||
|
||||
:::note
|
||||
`.immich` files serve as markers and help keep track of volume mounts being used by Immich. Except for the situations listed below, they should never be manually created or deleted.
|
||||
:::
|
||||
|
||||
#### Missing `.immich` files
|
||||
|
||||
```
|
||||
Verifying system mount folder checks (enabled=true)
|
||||
...
|
||||
ENOENT: no such file or directory, open 'upload/encoded-video/.immich'
|
||||
```
|
||||
|
||||
The above error messages show that the server has previously (successfully) written `.immich` files to each folder, but now does not detect them. This could be because any of the following:
|
||||
|
||||
- Permission error - unable to read the file, but it exists
|
||||
- File does not exist - volume mount has changed and should be corrected
|
||||
- File does not exist - user manually deleted it and should be manually re-created (`touch .immich`)
|
||||
- File does not exist - user restored from a backup, but did not restore each folder (user should restore all folders or manually create `.immich` in any missing folders)
|
||||
|
||||
### Ignoring the checks
|
||||
|
||||
The checks are designed to catch common problems that we have seen users have in the past, but if you want to disable them you can set the following environment variable:
|
||||
|
||||
```
|
||||
IMMICH_IGNORE_MOUNT_CHECK_ERRORS=true
|
||||
```
|
@ -104,7 +104,7 @@ You can choose to disable a certain type of machine learning, for example smart
|
||||
|
||||
### Smart Search
|
||||
|
||||
The smart search settings are designed to allow the search tool to be used using [CLIP](https://openai.com/research/clip) models that [can be changed](/docs/FAQ#can-i-use-a-custom-clip-model), different models will necessarily give better results but may consume more processing power, when changing a model it is mandatory to re-run the
|
||||
The [smart search](/docs/features/smart-search) settings are designed to allow the search tool to be used using [CLIP](https://openai.com/research/clip) models that [can be changed](/docs/FAQ#can-i-use-a-custom-clip-model), different models will necessarily give better results but may consume more processing power, when changing a model it is mandatory to re-run the
|
||||
Smart Search job on all images to fully apply the change.
|
||||
|
||||
:::info Internet connection
|
||||
@ -113,15 +113,23 @@ After downloading, there is no need for Immich to connect to the network
|
||||
Unless version checking has been enabled in the settings.
|
||||
:::
|
||||
|
||||
### Duplicate Detection
|
||||
|
||||
Use CLIP embeddings to find likely duplicates. The maximum detection distance can be configured in order to improve / reduce the level of accuracy.
|
||||
|
||||
- **Maximum detection distance -** Maximum distance between two images to consider them duplicates, ranging from 0.001-0.1. Higher values will detect more duplicates, but may result in false positives.
|
||||
|
||||
### Facial Recognition
|
||||
|
||||
Under these settings, you can change the facial recognition settings
|
||||
Editable settings:
|
||||
|
||||
- **Facial Recognition Model -** Models are listed in descending order of size. Larger models are slower and use more memory, but produce better results. Note that you must re-run the Face Detection job for all images upon changing a model.
|
||||
- **Min Detection Score -** Minimum confidence score for a face to be detected from 0-1. Lower values will detect more faces but may result in false positives.
|
||||
- **Max Recognition Distance -** Maximum distance between two faces to be considered the same person, ranging from 0-2. Lowering this can prevent labeling two people as the same person, while raising it can prevent labeling the same person as two different people. Note that it is easier to merge two people than to split one person in two, so err on the side of a lower threshold when possible.
|
||||
- **Min Recognized Faces -** The minimum number of recognized faces for a person to be created (AKA: Core face). Increasing this makes Facial Recognition more precise at the cost of increasing the chance that a face is not assigned to a person.
|
||||
- **Facial Recognition Model**
|
||||
- **Min Detection Score**
|
||||
- **Max Recognition Distance**
|
||||
- **Min Recognized Faces**
|
||||
|
||||
You can learn more about these options on the [Facial Recognition page](/docs/features/facial-recognition#how-face-detection-works)
|
||||
|
||||
:::info
|
||||
When changing the values in Min Detection Score, Max Recognition Distance, and Min Recognized Faces.
|
||||
@ -153,7 +161,7 @@ SMTP server setup, for user creation notifications, new albums, etc. More inform
|
||||
|
||||
### External Domain
|
||||
|
||||
When set, will override the domain name used when viewing and copying a shared link.
|
||||
Overrides the domain name in shared links and email notifications. The URL should not include a trailing slash.
|
||||
|
||||
### Welcome Message
|
||||
|
||||
|
@ -3,6 +3,7 @@ sidebar_position: 1
|
||||
---
|
||||
|
||||
import AppArchitecture from './img/app-architecture.png';
|
||||
import MobileArchitecture from './img/immich_mobile_architecture.svg';
|
||||
|
||||
# Architecture
|
||||
|
||||
@ -28,7 +29,14 @@ All three clients use [OpenAPI](./open-api.md) to auto-generate rest clients for
|
||||
|
||||
### Mobile App
|
||||
|
||||
The mobile app is written in [Flutter](https://flutter.dev/). It uses [Isar Database](https://isar.dev/) for a local database and [Riverpod](https://riverpod.dev/) for state management.
|
||||
The mobile app is written in [Dart](https://dart.dev/) using [Flutter](https://flutter.dev/). Below is an architecture overview:
|
||||
|
||||
<MobileArchitecture className="p-4 dark:bg-immich-dark-primary my-4" />
|
||||
|
||||
The diagrams shows the target architecture, the current state of the code-base is not always following the architecture yet. New code and contributions should follow this architecture.
|
||||
Currently, it uses [Isar Database](https://isar.dev/) for a local database and [Riverpod](https://riverpod.dev/) for state management (providers).
|
||||
Entities and Models are the two types of data classes used. While entities are stored in the on-device database, models are ephemeral and only kept in memory.
|
||||
The Repositories should be the only place where other data classes are used internally (such as OpenAPI DTOs). However, their interfaces must not use foreign data classes!
|
||||
|
||||
### Web Client
|
||||
|
||||
|
@ -15,7 +15,7 @@ Our [GitHub Repository](https://github.com/immich-app/immich) is a [monorepo](ht
|
||||
| `design/` | Screenshots and logos for the README |
|
||||
| `docs/` | Source code for the [https://immich.app](https://immich.app) website |
|
||||
| `machine-learning/` | Source code for the `immich-machine-learning` docker image |
|
||||
| `misc/release/` | Scripts for version pumps and draft releases |
|
||||
| `misc/release/` | Scripts for version bumps and draft releases |
|
||||
| `mobile/` | Source code for the mobile app, both Android and iOS |
|
||||
| `server/` | Source code for the `immich-server` docker image |
|
||||
| `web/` | Source code for the `web` |
|
||||
|
104
docs/docs/developer/img/immich_mobile_architecture.drawio
Normal file
@ -0,0 +1,104 @@
|
||||
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36" version="24.7.16">
|
||||
<diagram name="Page-1" id="Bp2gX--FtC4sSMWxsLrs">
|
||||
<mxGraphModel dx="1728" dy="954" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="0" pageScale="1" pageWidth="850" pageHeight="1100" background="none" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-1" value="" style="verticalLabelPosition=bottom;verticalAlign=top;html=1;shape=mxgraph.basic.polygon;polyCoords=[[0.25,0],[0.75,0],[1,0.25],[1,0.75],[0.75,1],[0.25,1],[0,0.75],[0,0.25]];polyline=0;strokeWidth=4;rounded=1;fillColor=#4251B0;" vertex="1" parent="1">
|
||||
<mxGeometry x="280" y="217.5" width="465" height="465" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-2" value="<b><font style="font-size: 22px;">Mobile App</font></b>" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=1;fontColor=#ffffff;" vertex="1" parent="1">
|
||||
<mxGeometry x="442.5" y="225" width="140" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-25" style="edgeStyle=none;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="zHhczcy2-Jv_nqmJUiNH-4" target="zHhczcy2-Jv_nqmJUiNH-5">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-4" value="Services" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#FFB400;" vertex="1" parent="1">
|
||||
<mxGeometry x="530" y="420" width="80" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-26" style="edgeStyle=none;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="zHhczcy2-Jv_nqmJUiNH-5" target="zHhczcy2-Jv_nqmJUiNH-12">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-5" value="Repositories" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#1E83F7;" vertex="1" parent="1">
|
||||
<mxGeometry x="650" y="420" width="80" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-24" style="edgeStyle=none;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="zHhczcy2-Jv_nqmJUiNH-6" target="zHhczcy2-Jv_nqmJUiNH-4">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-6" value="Providers" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ED79B5;" vertex="1" parent="1">
|
||||
<mxGeometry x="410" y="420" width="80" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-29" style="edgeStyle=none;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="zHhczcy2-Jv_nqmJUiNH-7" target="zHhczcy2-Jv_nqmJUiNH-8">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-30" style="edgeStyle=none;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.75;entryDx=0;entryDy=0;" edge="1" parent="1" source="zHhczcy2-Jv_nqmJUiNH-7" target="zHhczcy2-Jv_nqmJUiNH-6">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-7" value="Pages" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#FA2921;" vertex="1" parent="1">
|
||||
<mxGeometry x="290" y="480" width="80" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-31" style="edgeStyle=none;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;" edge="1" parent="1" source="zHhczcy2-Jv_nqmJUiNH-8" target="zHhczcy2-Jv_nqmJUiNH-6">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-8" value="Widgets" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#FA2921;" vertex="1" parent="1">
|
||||
<mxGeometry x="290" y="360" width="80" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-11" value="User" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;outlineConnect=0;rounded=1;fillColor=#4251B0;" vertex="1" parent="1">
|
||||
<mxGeometry x="180" y="368.5" width="81.5" height="163" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-12" value="platform<div>system</div>" style="rhombus;whiteSpace=wrap;html=1;rounded=1;fillColor=#ED79B5;" vertex="1" parent="1">
|
||||
<mxGeometry x="800" y="410" width="80" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-13" value="on-device<div>database</div>" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;rounded=1;fillColor=#FA2921;" vertex="1" parent="1">
|
||||
<mxGeometry x="810" y="310" width="60" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-14" value="server" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;rounded=1;fillColor=#FFB400;" vertex="1" parent="1">
|
||||
<mxGeometry x="780" y="500" width="120" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-16" style="edgeStyle=none;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;entryX=0.07;entryY=0.4;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="zHhczcy2-Jv_nqmJUiNH-5" target="zHhczcy2-Jv_nqmJUiNH-14">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-39" value="OpenAPI" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];rounded=1;labelBackgroundColor=#1E83F7;" vertex="1" connectable="0" parent="zHhczcy2-Jv_nqmJUiNH-16">
|
||||
<mxGeometry x="0.0697" y="1" relative="1" as="geometry">
|
||||
<mxPoint x="8" y="10" as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-23" style="edgeStyle=none;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1" source="zHhczcy2-Jv_nqmJUiNH-6" target="zHhczcy2-Jv_nqmJUiNH-6">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-27" style="edgeStyle=none;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=0;entryY=1;entryDx=0;entryDy=-15;entryPerimeter=0;" edge="1" parent="1" source="zHhczcy2-Jv_nqmJUiNH-5" target="zHhczcy2-Jv_nqmJUiNH-13">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-34" style="edgeStyle=none;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;dashed=1;" edge="1" parent="1" source="zHhczcy2-Jv_nqmJUiNH-3">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="810" y="360" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-36" value="" style="endArrow=none;dashed=1;html=1;rounded=1;" edge="1" parent="1" source="zHhczcy2-Jv_nqmJUiNH-9">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="512.08" y="665" as="sourcePoint" />
|
||||
<mxPoint x="512.08" y="265" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-37" value="UI part" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontStyle=1;fontSize=14;fontColor=#FFFFFF;" vertex="1" parent="1">
|
||||
<mxGeometry x="387.5" y="640" width="70" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-38" value="non-UI part" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontStyle=1;fontSize=14;fontColor=#FFFFFF;" vertex="1" parent="1">
|
||||
<mxGeometry x="550" y="640" width="90" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-41" value="" style="endArrow=none;dashed=1;html=1;rounded=1;" edge="1" parent="1" target="zHhczcy2-Jv_nqmJUiNH-9">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="512.08" y="665" as="sourcePoint" />
|
||||
<mxPoint x="512.08" y="265" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-9" value="Models" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#18C249;" vertex="1" parent="1">
|
||||
<mxGeometry x="470" y="510" width="80" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zHhczcy2-Jv_nqmJUiNH-3" value="Entities" style="rounded=1;whiteSpace=wrap;html=1;gradientColor=none;fillColor=#18C249;" vertex="1" parent="1">
|
||||
<mxGeometry x="472.5" y="330" width="80" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
3
docs/docs/developer/img/immich_mobile_architecture.svg
Normal file
After Width: | Height: | Size: 20 KiB |
@ -106,7 +106,7 @@ in User `settings.json` (`cmd + shift + p` and search for `Open User Settings JS
|
||||
"editor.suggest.snippetsPreventQuickSuggestions": false,
|
||||
"editor.suggestSelection": "first",
|
||||
"editor.tabCompletion": "onlySnippets",
|
||||
"editor.wordBasedSuggestions": false,
|
||||
"editor.wordBasedSuggestions": "off",
|
||||
"editor.defaultFormatter": "Dart-Code.dart-code"
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,8 @@
|
||||
|
||||
### Unit tests
|
||||
|
||||
Unit are run by calling `npm run test` from the `server` directory.
|
||||
Unit are run by calling `npm run test` from the `server/` directory.
|
||||
You need to run `npm install` (in `server/`) before _once_.
|
||||
|
||||
### End to end tests
|
||||
|
||||
@ -14,6 +15,11 @@ The e2e tests can be run by first starting up a test production environment via:
|
||||
make e2e
|
||||
```
|
||||
|
||||
Before you can run the tests, you need to run the following commands _once_:
|
||||
|
||||
- `npm install` (in `e2e/`)
|
||||
- `make open-api` (in the project root `/`)
|
||||
|
||||
Once the test environment is running, the e2e tests can be run via:
|
||||
|
||||
```bash
|
||||
|
@ -23,7 +23,7 @@ You do not need to redo any transcoding jobs after enabling hardware acceleratio
|
||||
- Raspberry Pi is currently not supported.
|
||||
- Two-pass mode is only supported for NVENC. Other APIs will ignore this setting.
|
||||
- By default, only encoding is currently hardware accelerated. This means the CPU is still used for software decoding and tone-mapping.
|
||||
- NVENC and RKMPP can be fully accelerated by enabling hardware decoding in the video transcoding settings.
|
||||
- You can benefit from end-to-end acceleration by enabling hardware decoding in the video transcoding settings.
|
||||
- Hardware dependent
|
||||
- Codec support varies, but H.264 and HEVC are usually supported.
|
||||
- Notably, NVIDIA and AMD GPUs do not support VP9 encoding.
|
||||
@ -49,7 +49,7 @@ For RKMPP to work:
|
||||
|
||||
- You must have a supported Rockchip ARM SoC.
|
||||
- Only RK3588 supports hardware tonemapping, other SoCs use slower software tonemapping while still using hardware encoding.
|
||||
- Tonemapping requires `/usr/lib/aarch64-linux-gnu/libmali.so.1` to be present on your host system. Install [`libmali-valhall-g610-g6p0-gbm`][libmali-rockchip] and modify the [`hwaccel.transcoding.yml`][hw-file] file:
|
||||
- Tonemapping requires `/usr/lib/aarch64-linux-gnu/libmali.so.1` to be present on your host system. Install the [`libmali`][libmali-rockchip] release that corresponds to your Mali GPU (`libmali-valhall-g610-g13p0-gbm` on RK3588) and modify the [`hwaccel.transcoding.yml`][hw-file] file:
|
||||
- under `rkmpp` uncomment the 3 lines required for OpenCL tonemapping by removing the `#` symbol at the beginning of each line
|
||||
- `- /dev/mali0:/dev/mali0`
|
||||
- `- /etc/OpenCL:/etc/OpenCL:ro`
|
||||
@ -62,11 +62,14 @@ For RKMPP to work:
|
||||
1. If you do not already have it, download the latest [`hwaccel.transcoding.yml`][hw-file] file and ensure it's in the same folder as the `docker-compose.yml`.
|
||||
2. In the `docker-compose.yml` under `immich-server`, uncomment the `extends` section and change `cpu` to the appropriate backend.
|
||||
|
||||
- For VAAPI on WSL2, be sure to use `vaapi-wsl` rather than `vaapi`
|
||||
Note: For VAAPI on WSL2, be sure to use `vaapi-wsl` rather than `vaapi`
|
||||
|
||||
3. Redeploy the `immich-server` container with these updated settings.
|
||||
4. In the Admin page under `Video transcoding settings`, change the hardware acceleration setting to the appropriate option and save.
|
||||
5. (Optional) If using a compatible backend, you may enable hardware decoding for optimal performance.
|
||||
|
||||
Note: For Jasper Lake and Elkhart Lake CPUs, you will need to set the `Hardware Acceleration` -> `Constant quality mode` to `CQP`
|
||||
|
||||
5. (Optional) Enable hardware decoding for optimal performance.
|
||||
|
||||
#### Single Compose File
|
||||
|
||||
@ -89,16 +92,7 @@ immich-server:
|
||||
devices:
|
||||
- /dev/dri:/dev/dri
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- 2283:3001
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
restart: always
|
||||
...
|
||||
```
|
||||
|
||||
Once this is done, you can continue to step 3 of "Basic Setup".
|
||||
|
BIN
docs/docs/features/img/folder-view.png
Normal file
After Width: | Height: | Size: 1.5 MiB |
@ -1,18 +1,14 @@
|
||||
# Libraries
|
||||
# External Libraries
|
||||
|
||||
## Overview
|
||||
External libraries track assets stored in the filesystem outside of Immich. When the external library is scanned, Immich will load videos and photos from disk and create the corresponding assets. These assets will then be shown in the main timeline, and they will look and behave like any other asset, including viewing on the map, adding to albums, etc. Later, if a file is modified outside of Immich, you need to scan the library for the changes to show up.
|
||||
|
||||
Immich supports the creation of libraries which is a top-level asset container. Currently, there are two types of libraries: traditional upload libraries that can sync with a mobile device, and external libraries, that keeps up to date with files on disk. Libraries are different from albums in that an asset can belong to multiple albums but only one library, and deleting a library deletes all assets contained within. As of August 2023, this is a new feature and libraries have a lot of potential for future development beyond what is documented here. This document attempts to describe the current state of libraries.
|
||||
If an external asset is deleted from disk, Immich will move it to trash on rescan. To restore the asset, you need to restore the original file. After 30 days the file will be removed from trash, and any changes to metadata within Immich will be lost.
|
||||
|
||||
## External Libraries
|
||||
:::caution
|
||||
|
||||
External libraries tracks assets stored outside of Immich, i.e. in the file system. When the external library is scanned, Immich will read the metadata from the file and create an asset in the library for each image or video file. These items will then be shown in the main timeline, and they will look and behave like any other asset, including viewing on the map, adding to albums, etc.
|
||||
If you add metadata to an external asset in any way (i.e. add it to an album or edit the description), that metadata is only stored inside Immich and will not be persisted to the external asset file. If you move an asset to another location within the library all such metadata will be lost upon rescan. This is because the asset is considered a new asset after the move. This is a known issue and will be fixed in a future release.
|
||||
|
||||
If a file is modified outside of Immich, the changes will not be reflected in immich until the library is scanned again. There are different ways to scan a library depending on the use case:
|
||||
|
||||
- Scan Library Files: This is the default scan method and also the quickest. It will scan all files in the library and add new files to the library. It will notice if any files are missing (see below) but not check existing assets
|
||||
- Scan All Library Files: Same as above, but will check each existing asset to see if the modification time has changed. If it has, the asset will be updated. Since it has to check each asset, this is slower than Scan Library Files.
|
||||
- Force Scan All Library Files: Same as above, but will read each asset from disk no matter the modification time. This is useful in some cases where an asset has been modified externally but the modification time has not changed. This is the slowest way to scan because it reads each asset from disk.
|
||||
:::
|
||||
|
||||
:::caution
|
||||
|
||||
@ -20,22 +16,6 @@ Due to aggressive caching it can take some time for a refreshed asset to appear
|
||||
|
||||
:::
|
||||
|
||||
In external libraries, the file path is used for duplicate detection. This means that if a file is moved to a different location, it will be added as a new asset. If the file is moved back to its original location, it will be added as a new asset. In contrast to upload libraries, two identical files can be uploaded if they are in different locations. This is a deliberate design choice to make Immich reflect the file system as closely as possible. Remember that duplication detection is only done within the same library, so if you have multiple external libraries, the same file can be added to multiple libraries.
|
||||
|
||||
:::caution
|
||||
|
||||
If you add assets from an external library to an album and then move the asset to another location within the library, the asset will be removed from the album upon rescan. This is because the asset is considered a new asset after the move. This is a known issue and will be fixed in a future release.
|
||||
|
||||
:::
|
||||
|
||||
### Deleted External Assets
|
||||
|
||||
Note: Either a manual or scheduled library scan must have been performed to identify offline assets before this process will work.
|
||||
|
||||
In all above scan methods, Immich will check if any files are missing. This can happen if files are deleted, or if they are on a storage location that is currently unavailable, like a network drive that is not mounted, or a USB drive that has been unplugged. In order to prevent accidental deletion of assets, Immich will not immediately delete an asset from the library if the file is missing. Instead, the asset will be internally marked as offline and will still be visible in the main timeline. If the file is moved back to its original location and the library is scanned again, the asset will be restored.
|
||||
|
||||
Finally, files can be deleted from Immich via the `Remove Offline Files` job. This job can be found by the three dots menu for the associated external storage that was configured under Administration > Libraries (the same location described at [create external libraries](#create-external-libraries)). When this job is run, any assets marked as offline will then be removed from Immich. Run this job whenever files have been deleted from the file system and you want to remove them from Immich.
|
||||
|
||||
### Import Paths
|
||||
|
||||
External libraries use import paths to determine which files to scan. Each library can have multiple import paths so that files from different locations can be added to the same library. Import paths are scanned recursively, and if a file is in multiple import paths, it will only be added once. Each import file must be a readable directory that exists on the filesystem; the import path dialog will alert you of any paths that are not accessible.
|
||||
@ -66,9 +46,13 @@ Some basic examples:
|
||||
- `**/Raw/**` will exclude all files in any directory named `Raw`
|
||||
- `**/*.{tif,jpg}` will exclude all files with the extension `.tif` or `.jpg`
|
||||
|
||||
Special characters such as @ should be escaped, for instance:
|
||||
|
||||
- `**/\@eadir/**` will exclude all files in any directory named `@eadir`
|
||||
|
||||
### Automatic watching (EXPERIMENTAL)
|
||||
|
||||
This feature - currently hidden in the config file - is considered experimental and for advanced users only. If enabled, it will allow automatic watching of the filesystem which means new assets are automatically imported to Immich without needing to rescan. Deleted assets are, as always, marked as offline and can be removed with the "Remove offline files" button.
|
||||
This feature - currently hidden in the config file - is considered experimental and for advanced users only. If enabled, it will allow automatic watching of the filesystem which means new assets are automatically imported to Immich without needing to rescan.
|
||||
|
||||
If your photos are on a network drive, automatic file watching likely won't work. In that case, you will have to rely on a periodic library refresh to pull in your changes.
|
||||
|
||||
@ -84,7 +68,7 @@ In rare cases, the library watcher can hang, preventing Immich from starting up.
|
||||
|
||||
### Nightly job
|
||||
|
||||
There is an automatic job that's run once a day and refreshes all modified files in all libraries as well as cleans up any libraries stuck in deletion.
|
||||
There is an automatic scan job that is scheduled to run once a day. This job also cleans up any libraries stuck in deletion.
|
||||
|
||||
## Usage
|
||||
|
||||
@ -104,22 +88,23 @@ The `immich-server` container will need access to the gallery. Modify your docke
|
||||
immich-server:
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
+ - /mnt/nas/christmas-trip:/mnt/nas/christmas-trip:ro
|
||||
+ - /home/user/old-pics:/home/user/old-pics:ro
|
||||
+ - /mnt/nas/christmas-trip:/mnt/media/christmas-trip:ro
|
||||
+ - /home/user/old-pics:/mnt/media/old-pics: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
|
||||
+ - "C:/Users/user_name/Desktop/my media:/mnt/media/my-media:ro" # import path in Windows system.
|
||||
```
|
||||
|
||||
:::tip
|
||||
The `ro` flag at the end only gives read-only access to the volumes. This will disallow the images from being deleted in the web UI.
|
||||
The `ro` flag at the end only gives read-only access to the volumes.
|
||||
This will disallow the images from being deleted in the web UI, or adding metadata to the library ([XMP sidecars](/docs/features/xmp-sidecars)).
|
||||
:::
|
||||
|
||||
:::info
|
||||
_Remember to run `docker compose up -d` to register the changes. Make sure you can see the mounted path in the container._
|
||||
:::
|
||||
|
||||
### Create External Libraries
|
||||
### Create A New Library
|
||||
|
||||
These actions must be performed by the Immich administrator.
|
||||
|
||||
@ -143,7 +128,7 @@ Next, we'll add an exclusion pattern to filter out raw files.
|
||||
- Enter `**/Raw/**` and click save.
|
||||
- Click save
|
||||
- Click the drop-down menu on the newly created library
|
||||
- Click on Scan Library Files
|
||||
- Click on Scan
|
||||
|
||||
The christmas trip library will now be scanned in the background. In the meantime, let's add the videos and old photos to another library.
|
||||
|
||||
@ -160,10 +145,26 @@ If you get an error here, please rename the other external library to something
|
||||
- Click on Add Path
|
||||
- Enter `/mnt/media/videos` then click Add
|
||||
- Click Save
|
||||
- Click on Scan Library Files
|
||||
- Click on Scan
|
||||
|
||||
Within seconds, the assets from the old-pics and videos folders should show up in the main timeline.
|
||||
|
||||
### Folder view
|
||||
|
||||
:::info
|
||||
This feature also exists for assets uploaded other than through external libraries.
|
||||
:::tip
|
||||
You can use the storage template migration feature for the best experience with uploaded assets in this view.
|
||||
:::
|
||||
|
||||
You can browse your photos and videos by folder like in a file explorer.
|
||||
|
||||
Enable this feature from the Users Settings > Features > Folders.
|
||||
|
||||
The UI is currently only available for the web; mobile will come in a subsequent release.
|
||||
|
||||
<img src={require('./img/folder-view.png').default} width="75%" title='Folder-view' />
|
||||
|
||||
### Set Custom Scan Interval
|
||||
|
||||
:::note
|
||||
|
@ -38,7 +38,7 @@ You do not need to redo any machine learning jobs after enabling hardware accele
|
||||
|
||||
- The GPU must have compute capability 5.2 or greater.
|
||||
- The server must have the official NVIDIA driver installed.
|
||||
- The installed driver must be >= 545 (it must support CUDA 12.3.2).
|
||||
- The installed driver must be >= 535 (it must support CUDA 12.2).
|
||||
- On Linux (except for WSL2), you also need to have [NVIDIA Container Toolkit][nvct] installed.
|
||||
|
||||
#### OpenVINO
|
||||
@ -53,6 +53,12 @@ You do not need to redo any machine learning jobs after enabling hardware accele
|
||||
3. Still in `immich-machine-learning`, add one of -[armnn, cuda, openvino] to the `image` section's tag at the end of the line.
|
||||
4. Redeploy the `immich-machine-learning` container with these updated settings.
|
||||
|
||||
### Confirming Device Usage
|
||||
|
||||
You can confirm the device is being recognized and used by checking its utilization. There are many tools to display this, such as `nvtop` for NVIDIA or Intel and `intel_gpu_top` for Intel.
|
||||
|
||||
You can also check the logs of the `immich-machine-learning` container. When a Smart Search or Face Detection job begins, or when you search with text in Immich, you should either see a log for `Available ORT providers` containing the relevant provider (e.g. `CUDAExecutionProvider` in the case of CUDA), or a `Loaded ANN model` log entry without errors in the case of ARM NN.
|
||||
|
||||
#### Single Compose File
|
||||
|
||||
Some platforms, including Unraid and Portainer, do not support multiple Compose files as of writing. As an alternative, you can "inline" the relevant contents of the [`hwaccel.ml.yml`][hw-file] file into the `immich-machine-learning` service directly.
|
||||
@ -95,9 +101,22 @@ immich-machine-learning:
|
||||
|
||||
Once this is done, you can redeploy the `immich-machine-learning` container.
|
||||
|
||||
:::info
|
||||
You can confirm the device is being recognized and used by checking its utilization (via `nvtop` for CUDA, `intel_gpu_top` for OpenVINO, etc.). You can also enable debug logging by setting `IMMICH_LOG_LEVEL=debug` in the `.env` file and restarting the `immich-machine-learning` container. When a Smart Search or Face Detection job begins, you should see a log for `Available ORT providers` containing the relevant provider. In the case of ARM NN, the absence of a `Could not load ANN shared libraries` log entry means it loaded successfully.
|
||||
:::
|
||||
#### Multi-GPU
|
||||
|
||||
If you want to utilize multiple NVIDIA or Intel GPUs, you can set the `MACHINE_LEARNING_DEVICE_IDS` environmental variable to a comma-separated list of device IDs and set `MACHINE_LEARNING_WORKERS` to the number of listed devices. You can run a command such as `nvidia-smi -L` or `glxinfo -B` to see the currently available devices and their corresponding IDs.
|
||||
|
||||
For example, if you have devices 0 and 1, set the values as follows:
|
||||
|
||||
```
|
||||
MACHINE_LEARNING_DEVICE_IDS=0,1
|
||||
MACHINE_LEARNING_WORKERS=2
|
||||
```
|
||||
|
||||
In this example, the machine learning service will spawn two workers, one of which will allocate models to device 0 and the other to device 1. Different requests will be processed by one worker or the other.
|
||||
|
||||
This approach can be used to simply specify a particular device as well. For example, setting `MACHINE_LEARNING_DEVICE_IDS=1` will ensure device 1 is always used instead of device 0.
|
||||
|
||||
Note that you should increase job concurrencies to increase overall utilization and more effectively distribute work across multiple GPUs. Additionally, each GPU must be able to load all models. It is not possible to distribute a single model to multiple GPUs that individually have insufficient VRAM, or to delegate a specific model to one GPU.
|
||||
|
||||
[hw-file]: https://github.com/immich-app/immich/releases/latest/download/hwaccel.ml.yml
|
||||
[nvct]: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html
|
||||
|
@ -27,3 +27,39 @@ The beta release channel allows users to test upcoming changes before they are o
|
||||
:::info
|
||||
You can enable automatic backup on supported devices. For more information see [Automatic Backup](/docs/features/automatic-backup.md).
|
||||
:::
|
||||
|
||||
## Album Sync
|
||||
|
||||
You can sync or mirror an album from your phone to the Immich server on your account. For example, if you select Recents, Camera and Videos album for backup, the corresponding album with the same name will be created on the server. Once the assets from those albums are uploaded, they will be put into the target albums automatically.
|
||||
|
||||
### Album Synchronization Highlights
|
||||
|
||||
- **One-Way Sync:** Synchronization is one-way, from the device to the server.
|
||||
|
||||
- **Name Matching:** If an album on the server has the same name as the album on the device, images from the device will be merged with the existing images in the server album.
|
||||
|
||||
- **Shared Albums:** If the matching album on the server is shared, the new photos merged into the album will also be shared.
|
||||
|
||||
- **Album Structure:** When an album is created for the first time, its structure is based on the initial state. Future updates made on the phone (such as deleting or repositioning photos) will not be reflected in Immich.
|
||||
|
||||
- **User-Specific Sync:** Album synchronization is unique to each server user and does not sync between different users or partners.
|
||||
|
||||
- **Mobile-Only Feature:** Album synchronization is currently only available on mobile. For similar options on a computer, refer to [Libraries](/docs/features/libraries) for further details.
|
||||
|
||||
### Synchronizing albums from the past
|
||||
|
||||
Albums can be synchronized to the server even if they did not exist on the server before. In order to apply this setting you have to:
|
||||
Enter the cloud on the top right -> cog wheel on the top right -> select the sync option under Sync albums.
|
||||
|
||||
:::info Sync albums delete/move photos
|
||||
If you delete/move photos in the local album on your device, it will not be reflected in the album on the server **even if** you click Sync albums
|
||||
It will only reflect files you add.
|
||||
:::
|
||||
|
||||
If the same asset is in more than one album it will only sync to the first album it's in, after that it won't sync again even if the user clicks sync albums manually.
|
||||
To overcome this limitation, the files must be removed from the blacklist by
|
||||
App settings -> Advanced -> Duplicate Assets -> Clear
|
||||
|
||||
:::info
|
||||
Cleaning duplicate assets from the list will cause all the previously uploaded duplicate files to be re-uploaded, the files will not actually be uploaded and will be rejected on the server side (due to duplication) but will be synchronized to the album and at the end will be added to the black list again at the end of the synchronization.
|
||||
:::
|
||||
|
@ -25,10 +25,10 @@ The metrics in immich are grouped into API (endpoint calls and response times),
|
||||
|
||||
### Configuration
|
||||
|
||||
Immich will not expose an endpoint for metrics by default. To enable this endpoint, you can add the `IMMICH_METRICS=true` environmental variable to your `.env` file. Note that only the server and microservices containers currently use this variable.
|
||||
Immich will not expose an endpoint for metrics by default. To enable this endpoint, you can add the `IMMICH_TELEMETRY_INCLUDE=all` environmental variable to your `.env` file. Note that only the server container currently use this variable.
|
||||
|
||||
:::tip
|
||||
`IMMICH_METRICS` enables all metrics, but there are also [environmental variables](/docs/install/environment-variables.md#prometheus) to toggle specific metric groups. If you'd like to only expose certain kinds of metrics, you can set only those environmental variables to `true`. Explicitly setting the environmental variable for a metric group overrides `IMMICH_METRICS` for that group. For example, setting `IMMICH_METRICS=true` and `IMMICH_API_METRICS=false` will enable all metrics except API metrics.
|
||||
`IMMICH_TELEMETRY_INCLUDE=all` enables all metrics. For a more granular configuration you can enumerate the telemetry metrics that should be included as a comma separated list (e.g. `IMMICH_TELEMETRY_INCLUDE=repo,api`). Alternatively, you can also exclude specific metrics with `IMMICH_TELEMETRY_EXCLUDE`. For more information refer to the [environment section](/docs/install/environment-variables.md#prometheus).
|
||||
:::
|
||||
|
||||
The next step is to configure a new or existing Prometheus instance to scrape this endpoint. The following steps assume that you do not have an existing Prometheus instance, but the steps will be similar either way.
|
||||
|
@ -16,7 +16,7 @@ When sharing shared albums, whats shared is:
|
||||
|
||||
- Download all assets as zip file (Web only).
|
||||
:::info Archive size limited.
|
||||
If the size of the album exceeds 4GB, the archive files will be divided into 4GB each.
|
||||
If the size of the album exceeds 4GB, the archive files will by default be divided into 4GB each. This can be changed on the user settings page.
|
||||
:::
|
||||
- Add a description to the album (Web only).
|
||||
- Slideshow view (Web only).
|
||||
@ -73,14 +73,14 @@ You can edit the link properties, options include;
|
||||
- **Allow public user to download -** whether to allow whoever has the link to download all the images or a certain image (optional).
|
||||
- **Allow public user to upload -** whether to allow whoever has the link to upload assets to the album (optional).
|
||||
:::info
|
||||
whoever has the link and have uploaded files cannot delete them.
|
||||
Whoever has the link and have uploaded files cannot delete them.
|
||||
:::
|
||||
- **Expire after -** adding an expiration date to the link (optional).
|
||||
|
||||
## Share Specific Assets
|
||||
|
||||
A user can share specific assets without linking them to a specific album.
|
||||
in order to do so;
|
||||
In order to do this:
|
||||
|
||||
1. Go to the timeline
|
||||
2. Select the assets (Shift can be used for multiple selection)
|
||||
@ -152,7 +152,7 @@ Some of the features are not available on mobile, to understand what the full fe
|
||||
|
||||
## Sharing Between Users
|
||||
|
||||
#### Add or remove users from the album.
|
||||
#### Add or remove users from the album
|
||||
|
||||
:::info remove user(s)
|
||||
When a user is removed from the album, the photos he uploaded will still appear in the album.
|
||||
|
@ -1,8 +1,22 @@
|
||||
# Create Custom Map Styles for Immich Using Maptiler
|
||||
# Custom Map Styles
|
||||
|
||||
You may decide that you'd like to modify the style document which is used to draw the maps in Immich. This can be done easily using Maptiler, if you do not want to write an entire JSON document by hand.
|
||||
You may decide that you'd like to modify the style document which is used to
|
||||
draw the maps in Immich. In addition to visual customization, this also allows
|
||||
you to pick your own map tile provider instead of the default one. The default
|
||||
`style.json` for [light theme](https://github.com/immich-app/immich/tree/main/server/resources/style-light.json)
|
||||
and [dark theme](https://github.com/immich-app/immich/blob/main/server/resources/style-dark.json)
|
||||
can be used as a basis for creating your own style.
|
||||
|
||||
## Steps
|
||||
There are several sources for already-made `style.json` map themes, as well as
|
||||
online generators you can use.
|
||||
|
||||
1. In **Immich**, navigate to **Administration --> Settings --> Map & GPS Settings** and expand the **Map Settings** subsection.
|
||||
2. Paste the link to your JSON style in either the **Light Style** or **Dark Style**. (You can add different styles which will help make the map style more appropriate depending on whether you set **Immich** to Light or Dark mode.)
|
||||
3. Save your selections. Reload the map, and enjoy your custom map style!
|
||||
|
||||
## Use Maptiler to build a custom style
|
||||
|
||||
Customizing the map style can be done easily using Maptiler, if you do not want to write an entire JSON document by hand.
|
||||
|
||||
1. Create a free account at https://cloud.maptiler.com
|
||||
2. Once logged in, you can either create a brand new map by clicking on **New Map**, selecting a starter map, and then clicking **Customize**, OR by selecting a **Standard Map** and customizing it from there.
|
||||
@ -11,6 +25,3 @@ You may decide that you'd like to modify the style document which is used to dra
|
||||
5. Next, **Publish** your style using the **Publish** button at the top right. This will deploy it to production, which means it is able to be exposed over the Internet. Maptiler will present an interactive side-by-side map with the original and your changes prior to publication.<br/>
|
||||
6. Maptiler will warn you that changing the map will change it across all apps using the map. Since no apps are using the map yet, this is okay.
|
||||
7. Clicking on the name of your new map at the top left will bring you to the item's **details** page. From here, copy the link to the JSON style under **Use vector style**. This link will automatically contain your personal API key to Maptiler.
|
||||
8. In **Immich**, navigate to **Administration --> Settings --> Map & GPS Settings** and expand the **Map Settings** subsection.
|
||||
9. Paste the link to your JSON style in either the **Light Style** or **Dark Style**. (You can add different styles which will help make the map style more appropriate depending on whether you set **Immich** to Light or Dark mode.
|
||||
10. Save your selections. Reload the map, and enjoy your custom map style!
|
||||
|
@ -23,7 +23,7 @@ SELECT * FROM "assets" WHERE "originalFileName" LIKE '%_2023_%'; -- all files wi
|
||||
```
|
||||
|
||||
```sql title="Find by path"
|
||||
SELECT * FROM "assets" WHERE "originalPath" = 'upload/library/admin/2023/2023-09-03/PXL_20230903_232542848.jpg';
|
||||
SELECT * FROM "assets" WHERE "originalPath" = 'upload/library/admin/2023/2023-09-03/PXL_2023.jpg';
|
||||
SELECT * FROM "assets" WHERE "originalPath" LIKE 'upload/library/admin/2023/%';
|
||||
```
|
||||
|
||||
@ -37,6 +37,12 @@ SELECT * FROM "assets" WHERE "checksum" = decode('69de19c87658c4c15d9cacb9967b8e
|
||||
SELECT * FROM "assets" WHERE "checksum" = '\x69de19c87658c4c15d9cacb9967b8e033bf74dd1'; -- alternate notation
|
||||
```
|
||||
|
||||
```sql title="Find duplicate assets with identical checksum (SHA-1) (excluding trashed files)"
|
||||
SELECT T1."checksum", array_agg(T2."id") ids FROM "assets" T1
|
||||
INNER JOIN "assets" T2 ON T1."checksum" = T2."checksum" AND T1."id" != T2."id" AND T2."deletedAt" IS NULL
|
||||
WHERE T1."deletedAt" IS NULL GROUP BY T1."checksum";
|
||||
```
|
||||
|
||||
```sql title="Live photos"
|
||||
SELECT * FROM "assets" WHERE "livePhotoVideoId" IS NOT NULL;
|
||||
```
|
||||
@ -79,8 +85,7 @@ SELECT "assets"."type", COUNT(*) FROM "assets" GROUP BY "assets"."type";
|
||||
```sql title="Count by type (per user)"
|
||||
SELECT "users"."email", "assets"."type", COUNT(*) FROM "assets"
|
||||
JOIN "users" ON "assets"."ownerId" = "users"."id"
|
||||
GROUP BY "assets"."type", "users"."email"
|
||||
ORDER BY "users"."email";
|
||||
GROUP BY "assets"."type", "users"."email" ORDER BY "users"."email";
|
||||
```
|
||||
|
||||
```sql title="Failed file movements"
|
||||
|
@ -7,7 +7,7 @@ in a directory on the same machine.
|
||||
# Mount the directory into the containers.
|
||||
|
||||
Edit `docker-compose.yml` to add one or more new mount points in the section `immich-server:` under `volumes:`.
|
||||
If you want Immich to be able to delete the images in the external library, remove `:ro` from the end of the mount point.
|
||||
If you want Immich to be able to delete the images in the external library or add metadata ([XMP sidecars](/docs/features/xmp-sidecars)), remove `:ro` from the end of the mount point.
|
||||
|
||||
```diff
|
||||
immich-server:
|
||||
|
@ -11,13 +11,13 @@ Never forward port 2283 directly to the internet without additional configuratio
|
||||
|
||||
You may use a VPN service to open an encrypted connection to your Immich instance. OpenVPN and Wireguard are two popular VPN solutions. Here is a guide on setting up VPN access to your server - [Pihole documentation](https://docs.pi-hole.net/guides/vpn/wireguard/overview/)
|
||||
|
||||
### Pros:
|
||||
### Pros
|
||||
|
||||
- Simple to set up and very secure.
|
||||
- Single point of potential failure, i.e., the VPN software itself. Even if there is a zero-day vulnerability on Immich, you will not be at risk.
|
||||
- Both Wireguard and OpenVPN are independently security-audited, so the risk of serious zero-day exploits are minimal.
|
||||
|
||||
### Cons:
|
||||
### Cons
|
||||
|
||||
- If you don't have a static IP address, you would need to set up a [Dynamic DNS](https://www.cloudflare.com/learning/dns/glossary/dynamic-dns/). [DuckDNS](https://www.duckdns.org/) is a free DDNS provider.
|
||||
- VPN software needs to be installed and active on both server-side and client-side.
|
||||
@ -27,6 +27,10 @@ You may use a VPN service to open an encrypted connection to your Immich instanc
|
||||
|
||||
If you are unable to open a port on your router for Wireguard or OpenVPN to your server, [Tailscale](https://tailscale.com/) is a good option. Tailscale mediates a peer-to-peer wireguard tunnel between your server and remote device, even if one or both of them are behind a [NAT firewall](https://en.wikipedia.org/wiki/Network_address_translation).
|
||||
|
||||
:::tip Video tutorial
|
||||
You can learn how to set up Tailscale together with Immich with the [tutorial video](https://www.youtube.com/watch?v=Vt4PDUXB_fg) they created.
|
||||
:::
|
||||
|
||||
### Pros
|
||||
|
||||
- Minimal configuration needed on server and client sides.
|
||||
@ -44,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).
|
||||
|
||||
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.
|
||||
|
||||
|
@ -11,6 +11,10 @@ To alleviate [performance issues on low-memory systems](/docs/FAQ.mdx#why-is-imm
|
||||
Smart Search and Face Detection will use this feature, but Facial Recognition is handled in the server.
|
||||
:::
|
||||
|
||||
:::danger
|
||||
When using remote machine learning, the thumbnails are sent to the remote machine learning container. Use this option carefully when running this on a public computer or a paid processing cloud.
|
||||
:::
|
||||
|
||||
```yaml
|
||||
name: immich_remote_ml
|
||||
|
||||
|
19
docs/docs/guides/scaling-immich.md
Normal file
@ -0,0 +1,19 @@
|
||||
# Scaling Immich
|
||||
|
||||
Immich is built with modern deployment practices in mind, and the backend is designed to be able to run multiple instances in parallel. When doing this, the only requirement you need to be aware of is that every instance needs to be connected to the shared infrastructure. That means they should all have access to the same Postgres and Redis instances, and have the same files mounted into the containers.
|
||||
|
||||
Scaling can be useful for many reasons. Maybe you have a gaming PC that you want to use for transcoding and thumbnail generation, or perhaps you run a Kubernetes cluster across a handful of powerful servers that you want to make use of.
|
||||
|
||||
:::info
|
||||
If you only have a single machine to run Immich on, scaling to multiple containers is unlikely to provide any benefit. An Immich container will run multiple background tasks at once, and you can increase their number from the admin panel.
|
||||
:::
|
||||
|
||||
The details of how to scale across multiple machines will vary widely between different environments and require some knowledge to set up, and as such this guide gives no specific instructions. In some cases scaling up can be as easy as incrementing the amount of replicas on a Kubernetes deployment, in others it might need you to configure network tunnels or NFS mounts. The details are left as an exercise for the reader ;)
|
||||
|
||||
## Workers
|
||||
|
||||
By default, each running `immich-server` container comes with multiple internal workers. If you're scaling up only to handle more background tasks, you can choose to disable the worker responsible for the API. See [workers](../administration/jobs-workers.md) for more detail.
|
||||
|
||||
## Scaling down
|
||||
|
||||
In the same way you can scale up to multiple containers, you can also choose to scale down. All state is stored in Postgres, Redis, and the filesystem so there is no risk in stopping a running immich-server container, for example if you want to use your GPU to play some games. As long as there is an API worker running you will still be able to browse Immich, and jobs will wait to be processed until there is a worker available for them.
|
@ -9,7 +9,7 @@ The database is saved to your Immich upload folder in the `database-backup` subd
|
||||
### Prerequisites
|
||||
|
||||
- Borg needs to be installed on your server as well as the remote machine. You can find instructions to install Borg [here](https://borgbackup.readthedocs.io/en/latest/installation.html).
|
||||
- (Optional) To run this sript as a non-root user, you should [add your username to the docker group](https://docs.docker.com/engine/install/linux-postinstall/).
|
||||
- (Optional) To run this script as a non-root user, you should [add your username to the docker group](https://docs.docker.com/engine/install/linux-postinstall/).
|
||||
- To run this script non-interactively, set up [passwordless ssh](https://www.redhat.com/sysadmin/passwordless-ssh) to your remote machine from your server. If you skipped the previous step, make sure this step is done from your root account.
|
||||
|
||||
To initialize the borg repository, run the following commands once.
|
||||
@ -78,4 +78,4 @@ borg mount "$REMOTE_HOST:$REMOTE_BACKUP_PATH"/immich-borg /tmp/immich-mountpoint
|
||||
cd /tmp/immich-mountpoint
|
||||
```
|
||||
|
||||
You can find available snapshots in seperate sub-directories at `/tmp/immich-mountpoint`. Restore the files you need, and unmount the Borg repository using `borg umount /tmp/immich-mountpoint`
|
||||
You can find available snapshots in separate sub-directories at `/tmp/immich-mountpoint`. Restore the files you need, and unmount the Borg repository using `borg umount /tmp/immich-mountpoint`
|
||||
|
@ -19,7 +19,8 @@ The default configuration looks like this:
|
||||
"targetVideoCodec": "h264",
|
||||
"acceptedVideoCodecs": ["h264"],
|
||||
"targetAudioCodec": "aac",
|
||||
"acceptedAudioCodecs": ["aac", "mp3", "libopus"],
|
||||
"acceptedAudioCodecs": ["aac", "mp3", "libopus", "pcm_s16le"],
|
||||
"acceptedContainers": ["mov", "ogg", "webm"],
|
||||
"targetResolution": "720",
|
||||
"maxBitrate": "0",
|
||||
"bframes": -1,
|
||||
@ -32,7 +33,8 @@ The default configuration looks like this:
|
||||
"preferredHwDevice": "auto",
|
||||
"transcode": "required",
|
||||
"tonemap": "hable",
|
||||
"accel": "disabled"
|
||||
"accel": "disabled",
|
||||
"accelDecode": false
|
||||
},
|
||||
"job": {
|
||||
"backgroundTask": {
|
||||
@ -60,10 +62,13 @@ The default configuration looks like this:
|
||||
"concurrency": 5
|
||||
},
|
||||
"thumbnailGeneration": {
|
||||
"concurrency": 5
|
||||
"concurrency": 3
|
||||
},
|
||||
"videoConversion": {
|
||||
"concurrency": 1
|
||||
},
|
||||
"notifications": {
|
||||
"concurrency": 5
|
||||
}
|
||||
},
|
||||
"logging": {
|
||||
@ -78,40 +83,46 @@ The default configuration looks like this:
|
||||
"modelName": "ViT-B-32__openai"
|
||||
},
|
||||
"duplicateDetection": {
|
||||
"enabled": false,
|
||||
"maxDistance": 0.03
|
||||
"enabled": true,
|
||||
"maxDistance": 0.01
|
||||
},
|
||||
"facialRecognition": {
|
||||
"enabled": true,
|
||||
"modelName": "buffalo_l",
|
||||
"minScore": 0.7,
|
||||
"maxDistance": 0.6,
|
||||
"maxDistance": 0.5,
|
||||
"minFaces": 3
|
||||
}
|
||||
},
|
||||
"map": {
|
||||
"enabled": true,
|
||||
"lightStyle": "",
|
||||
"darkStyle": ""
|
||||
"lightStyle": "https://tiles.immich.cloud/v1/style/light.json",
|
||||
"darkStyle": "https://tiles.immich.cloud/v1/style/dark.json"
|
||||
},
|
||||
"reverseGeocoding": {
|
||||
"enabled": true
|
||||
},
|
||||
"metadata": {
|
||||
"faces": {
|
||||
"import": false
|
||||
}
|
||||
},
|
||||
"oauth": {
|
||||
"enabled": false,
|
||||
"issuerUrl": "",
|
||||
"autoLaunch": false,
|
||||
"autoRegister": true,
|
||||
"buttonText": "Login with OAuth",
|
||||
"clientId": "",
|
||||
"clientSecret": "",
|
||||
"defaultStorageQuota": 0,
|
||||
"enabled": false,
|
||||
"issuerUrl": "",
|
||||
"mobileOverrideEnabled": false,
|
||||
"mobileRedirectUri": "",
|
||||
"scope": "openid email profile",
|
||||
"signingAlgorithm": "RS256",
|
||||
"profileSigningAlgorithm": "none",
|
||||
"storageLabelClaim": "preferred_username",
|
||||
"storageQuotaClaim": "immich_quota",
|
||||
"defaultStorageQuota": 0,
|
||||
"buttonText": "Login with OAuth",
|
||||
"autoRegister": true,
|
||||
"autoLaunch": false,
|
||||
"mobileOverrideEnabled": false,
|
||||
"mobileRedirectUri": ""
|
||||
"storageQuotaClaim": "immich_quota"
|
||||
},
|
||||
"passwordLogin": {
|
||||
"enabled": true
|
||||
@ -122,11 +133,16 @@ The default configuration looks like this:
|
||||
"template": "{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}"
|
||||
},
|
||||
"image": {
|
||||
"thumbnailFormat": "webp",
|
||||
"thumbnailSize": 250,
|
||||
"previewFormat": "jpeg",
|
||||
"previewSize": 1440,
|
||||
"quality": 80,
|
||||
"thumbnail": {
|
||||
"format": "webp",
|
||||
"size": 250,
|
||||
"quality": 80
|
||||
},
|
||||
"preview": {
|
||||
"format": "jpeg",
|
||||
"size": 1440,
|
||||
"quality": 80
|
||||
},
|
||||
"colorspace": "p3",
|
||||
"extractEmbedded": false
|
||||
},
|
||||
@ -140,23 +156,35 @@ The default configuration looks like this:
|
||||
"theme": {
|
||||
"customCss": ""
|
||||
},
|
||||
"user": {
|
||||
"deleteDelay": 7
|
||||
},
|
||||
"library": {
|
||||
"scan": {
|
||||
"enabled": true,
|
||||
"cronExpression": "0 0 * * *"
|
||||
},
|
||||
"watch": {
|
||||
"enabled": false,
|
||||
"usePolling": false,
|
||||
"interval": 10000
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"server": {
|
||||
"externalDomain": "",
|
||||
"loginPageMessage": ""
|
||||
},
|
||||
"notifications": {
|
||||
"smtp": {
|
||||
"enabled": false,
|
||||
"from": "",
|
||||
"replyTo": "",
|
||||
"transport": {
|
||||
"ignoreCert": false,
|
||||
"host": "",
|
||||
"port": 587,
|
||||
"username": "",
|
||||
"password": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"deleteDelay": 7
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -56,7 +56,9 @@ Optionally, you can enable hardware acceleration for machine learning and transc
|
||||
|
||||
- Populate custom database information if necessary.
|
||||
- Populate `UPLOAD_LOCATION` with your preferred location for storing backup assets.
|
||||
- Consider changing `DB_PASSWORD` to something randomly generated
|
||||
- Consider changing `DB_PASSWORD` to a custom value. Postgres is not publically exposed, so this password is only used for local authentication.
|
||||
To avoid issues with Docker parsing this value, it is best to use only the characters `A-Za-z0-9`.
|
||||
- Set your timezone by uncommenting the `TZ=` line.
|
||||
|
||||
### Step 3 - Start the containers
|
||||
|
||||
@ -79,6 +81,10 @@ The Compose file './docker-compose.yml' is invalid because:
|
||||
See the previous paragraph about installing from the official docker repository.
|
||||
:::
|
||||
|
||||
:::info Health check start interval
|
||||
If you get an error `can't set healthcheck.start_interval as feature require Docker Engine v25 or later`, it helps to comment out the line for `start_interval` in the `database` section of the `docker-compose.yml` file.
|
||||
:::
|
||||
|
||||
:::tip
|
||||
For more information on how to use the application, please refer to the [Post Installation](/docs/install/post-install.mdx) guide.
|
||||
:::
|
||||
@ -108,7 +114,7 @@ Immich is currently under heavy development, which means you can expect [breakin
|
||||
[compose-file]: https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
||||
[env-file]: https://github.com/immich-app/immich/releases/latest/download/example.env
|
||||
[watchtower]: https://containrrr.dev/watchtower/
|
||||
[breaking]: https://github.com/immich-app/immich/discussions?discussions_q=label%3Abreaking-change+sort%3Adate_created
|
||||
[breaking]: https://github.com/immich-app/immich/discussions?discussions_q=label%3Achangelog%3Abreaking-change+sort%3Adate_created
|
||||
[container-auth]: https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#authenticating-to-the-container-registry
|
||||
[releases]: https://github.com/immich-app/immich/releases
|
||||
[docker-repo]: https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository
|
||||
|
@ -27,39 +27,30 @@ If this should not work, try running `docker compose up -d --force-recreate`.
|
||||
These environment variables are used by the `docker-compose.yml` file and do **NOT** affect the containers directly.
|
||||
:::
|
||||
|
||||
### Supported filesystems
|
||||
|
||||
The Immich Postgres database (`DB_DATA_LOCATION`) must be located on a filesystem that supports user/group
|
||||
ownership and permissions (EXT2/3/4, ZFS, APFS, BTRFS, XFS, etc.). It will not work on any filesystem formatted in NTFS or ex/FAT/32.
|
||||
It will not work in WSL (Windows Subsystem for Linux) when using a mounted host directory (commonly under `/mnt`).
|
||||
If this is an issue, you can change the bind mount to a Docker volume instead.
|
||||
|
||||
Regardless of filesystem, it is not recommended to use a network share for your database location due to performance and possible data loss issues.
|
||||
|
||||
## General
|
||||
|
||||
| Variable | Description | Default | Containers | Workers |
|
||||
| :---------------------------------- | :-------------------------------------------------- | :--------------------------: | :----------------------- | :----------------- |
|
||||
| `TZ` | Timezone | | server | microservices |
|
||||
| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices |
|
||||
| `IMMICH_LOG_LEVEL` | Log Level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices |
|
||||
| `IMMICH_MEDIA_LOCATION` | Media Location | `./upload`<sup>\*1</sup> | server | api, microservices |
|
||||
| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices |
|
||||
| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | |
|
||||
| `CPU_CORES` | Amount of cores available to the immich server | auto-detected cpu core count | server | |
|
||||
| `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api |
|
||||
| `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics | `8082` | server | microservices |
|
||||
| `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices |
|
||||
| `IMMICH_TRUSTED_PROXIES` | List of comma separated IPs set as trusted proxies | | server | api |
|
||||
|
||||
\*1: With the default `WORKDIR` of `/usr/src/app`, this path will resolve to `/usr/src/app/upload`.
|
||||
It only need to be set if the Immich deployment method is changing.
|
||||
|
||||
:::tip
|
||||
`TZ` should be set to a `TZ identifier` from [this list][tz-list]. For example, `TZ="Etc/UTC"`.
|
||||
| Variable | Description | Default | Containers | Workers |
|
||||
| :---------------------------------- | :---------------------------------------------------------------------------------------- | :--------------------------: | :----------------------- | :----------------- |
|
||||
| `TZ` | Timezone | <sup>\*1</sup> | server | microservices |
|
||||
| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices |
|
||||
| `IMMICH_LOG_LEVEL` | Log Level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices |
|
||||
| `IMMICH_MEDIA_LOCATION` | Media Location inside the container ⚠️**You probably shouldn't set this**<sup>\*2</sup>⚠️ | `./upload`<sup>\*3</sup> | server | api, microservices |
|
||||
| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices |
|
||||
| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | |
|
||||
| `CPU_CORES` | Amount of cores available to the immich server | auto-detected cpu core count | server | |
|
||||
| `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api |
|
||||
| `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics | `8082` | server | microservices |
|
||||
| `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices |
|
||||
| `IMMICH_TRUSTED_PROXIES` | List of comma separated IPs set as trusted proxies | | server | api |
|
||||
| `IMMICH_IGNORE_MOUNT_CHECK_ERRORS` | See [System Integrity](/docs/administration/system-integrity) | | server | api, microservices |
|
||||
|
||||
\*1: `TZ` should be set to a `TZ identifier` from [this list][tz-list]. For example, `TZ="Etc/UTC"`.
|
||||
`TZ` is used by `exiftool` as a fallback in case the timezone cannot be determined from the image metadata. It is also used for logfile timestamps and cron job execution.
|
||||
:::
|
||||
|
||||
\*2: This path is where the Immich code looks for the files, which is internal to the docker container. Setting it to a path on your host will certainly break things, you should use the `UPLOAD_LOCATION` variable instead.
|
||||
|
||||
\*3: With the default `WORKDIR` of `/usr/src/app`, this path will resolve to `/usr/src/app/upload`.
|
||||
It only need to be set if the Immich deployment method is changing.
|
||||
|
||||
## Workers
|
||||
|
||||
@ -77,7 +68,7 @@ Information on the current workers can be found [here](/docs/administration/jobs
|
||||
| Variable | Description | Default |
|
||||
| :------------ | :------------- | :----------------------------------------: |
|
||||
| `IMMICH_HOST` | Listening host | `0.0.0.0` |
|
||||
| `IMMICH_PORT` | Listening port | `3001` (server), `3003` (machine learning) |
|
||||
| `IMMICH_PORT` | Listening port | `2283` (server), `3003` (machine learning) |
|
||||
|
||||
## Database
|
||||
|
||||
@ -123,7 +114,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`.
|
||||
|
||||
`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.
|
||||
:::
|
||||
@ -157,26 +148,33 @@ Redis (Sentinel) URL example JSON before encoding:
|
||||
|
||||
## Machine Learning
|
||||
|
||||
| Variable | Description | Default | Containers |
|
||||
| :----------------------------------------------- | :-------------------------------------------------------------------------------------------------- | :-----------------------------------: | :--------------- |
|
||||
| `MACHINE_LEARNING_MODEL_TTL` | Inactivity time (s) before a model is unloaded (disabled if \<= 0) | `300` | machine learning |
|
||||
| `MACHINE_LEARNING_MODEL_TTL_POLL_S` | Interval (s) between checks for the model TTL (disabled if \<= 0) | `10` | machine learning |
|
||||
| `MACHINE_LEARNING_CACHE_FOLDER` | Directory where models are downloaded | `/cache` | machine learning |
|
||||
| `MACHINE_LEARNING_REQUEST_THREADS`<sup>\*1</sup> | Thread count of the request thread pool (disabled if \<= 0) | number of CPU cores | machine learning |
|
||||
| `MACHINE_LEARNING_MODEL_INTER_OP_THREADS` | Number of parallel model operations | `1` | machine learning |
|
||||
| `MACHINE_LEARNING_MODEL_INTRA_OP_THREADS` | Number of threads for each model operation | `2` | machine learning |
|
||||
| `MACHINE_LEARNING_WORKERS`<sup>\*2</sup> | Number of worker processes to spawn | `1` | machine learning |
|
||||
| `MACHINE_LEARNING_WORKER_TIMEOUT` | Maximum time (s) of unresponsiveness before a worker is killed | `120` (`300` if using OpenVINO image) | machine learning |
|
||||
| `MACHINE_LEARNING_PRELOAD__CLIP` | Name of a CLIP model to be preloaded and kept in cache | | machine learning |
|
||||
| `MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION` | Name of a facial recognition model to be preloaded and kept in cache | | machine learning |
|
||||
| `MACHINE_LEARNING_ANN` | Enable ARM-NN hardware acceleration if supported | `True` | machine learning |
|
||||
| `MACHINE_LEARNING_ANN_FP16_TURBO` | Execute operations in FP16 precision: increasing speed, reducing precision (applies only to ARM-NN) | `False` | machine learning |
|
||||
| `MACHINE_LEARNING_ANN_TUNING_LEVEL` | ARM-NN GPU tuning level (1: rapid, 2: normal, 3: exhaustive) | `2` | machine learning |
|
||||
| Variable | Description | Default | Containers |
|
||||
| :-------------------------------------------------------- | :-------------------------------------------------------------------------------------------------- | :-----------------------------: | :--------------- |
|
||||
| `MACHINE_LEARNING_MODEL_TTL` | Inactivity time (s) before a model is unloaded (disabled if \<= 0) | `300` | machine learning |
|
||||
| `MACHINE_LEARNING_MODEL_TTL_POLL_S` | Interval (s) between checks for the model TTL (disabled if \<= 0) | `10` | machine learning |
|
||||
| `MACHINE_LEARNING_CACHE_FOLDER` | Directory where models are downloaded | `/cache` | machine learning |
|
||||
| `MACHINE_LEARNING_REQUEST_THREADS`<sup>\*1</sup> | Thread count of the request thread pool (disabled if \<= 0) | number of CPU cores | machine learning |
|
||||
| `MACHINE_LEARNING_MODEL_INTER_OP_THREADS` | Number of parallel model operations | `1` | machine learning |
|
||||
| `MACHINE_LEARNING_MODEL_INTRA_OP_THREADS` | Number of threads for each model operation | `2` | machine learning |
|
||||
| `MACHINE_LEARNING_WORKERS`<sup>\*2</sup> | Number of worker processes to spawn | `1` | machine learning |
|
||||
| `MACHINE_LEARNING_HTTP_KEEPALIVE_TIMEOUT_S`<sup>\*3</sup> | HTTP Keep-alive time in seconds | `2` | machine learning |
|
||||
| `MACHINE_LEARNING_WORKER_TIMEOUT` | Maximum time (s) of unresponsiveness before a worker is killed | `120` (`300` if using OpenVINO) | machine learning |
|
||||
| `MACHINE_LEARNING_PRELOAD__CLIP` | Name of a CLIP model to be preloaded and kept in cache | | machine learning |
|
||||
| `MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION` | Name of a facial recognition model to be preloaded and kept in cache | | machine learning |
|
||||
| `MACHINE_LEARNING_ANN` | Enable ARM-NN hardware acceleration if supported | `True` | machine learning |
|
||||
| `MACHINE_LEARNING_ANN_FP16_TURBO` | Execute operations in FP16 precision: increasing speed, reducing precision (applies only to ARM-NN) | `False` | machine learning |
|
||||
| `MACHINE_LEARNING_ANN_TUNING_LEVEL` | ARM-NN GPU tuning level (1: rapid, 2: normal, 3: exhaustive) | `2` | machine learning |
|
||||
| `MACHINE_LEARNING_DEVICE_IDS`<sup>\*4</sup> | Device IDs to use in multi-GPU environments | `0` | machine learning |
|
||||
| `MACHINE_LEARNING_MAX_BATCH_SIZE__FACIAL_RECOGNITION` | Set the maximum number of faces that will be processed at once by the facial recognition model | None (`1` if using OpenVINO) | machine learning |
|
||||
|
||||
\*1: It is recommended to begin with this parameter when changing the concurrency levels of the machine learning service and then tune the other ones.
|
||||
|
||||
\*2: Since each process duplicates models in memory, changing this is not recommended unless you have abundant memory to go around.
|
||||
|
||||
\*3: For scenarios like HPA in K8S. https://github.com/immich-app/immich/discussions/12064
|
||||
|
||||
\*4: Using multiple GPUs requires `MACHINE_LEARNING_WORKERS` to be set greater than 1. A single device is assigned to each worker in round-robin priority.
|
||||
|
||||
:::info
|
||||
|
||||
Other machine learning parameters can be tuned from the admin UI.
|
||||
@ -185,15 +183,10 @@ Other machine learning parameters can be tuned from the admin UI.
|
||||
|
||||
## Prometheus
|
||||
|
||||
| Variable | Description | Default | Containers | Workers |
|
||||
| :----------------------------- | :-------------------------------------------------------------------------------------------- | :-----: | :--------- | :----------------- |
|
||||
| `IMMICH_METRICS`<sup>\*1</sup> | Toggle all metrics (one of [`true`, `false`]) | | server | api, microservices |
|
||||
| `IMMICH_API_METRICS` | Toggle metrics for endpoints and response times (one of [`true`, `false`]) | | server | api, microservices |
|
||||
| `IMMICH_HOST_METRICS` | Toggle metrics for CPU and memory utilization for host and process (one of [`true`, `false`]) | | server | api, microservices |
|
||||
| `IMMICH_IO_METRICS` | Toggle metrics for database queries, image processing, etc. (one of [`true`, `false`]) | | server | api, microservices |
|
||||
| `IMMICH_JOB_METRICS` | Toggle metrics for jobs and queues (one of [`true`, `false`]) | | server | api, microservices |
|
||||
|
||||
\*1: Overridden for a metric group when its corresponding environmental variable is set.
|
||||
| Variable | Description | Default | Containers | Workers |
|
||||
| :------------------------- | :-------------------------------------------------------------------------------------------------------------------- | :-----: | :--------- | :----------------- |
|
||||
| `IMMICH_TELEMETRY_INCLUDE` | Collect these telemetries. List of `host`, `api`, `io`, `repo`, `job`. Note: You can also specify `all` to enable all | | server | api, microservices |
|
||||
| `IMMICH_TELEMETRY_EXCLUDE` | Do not collect these telemetries. List of `host`, `api`, `io`, `repo`, `job` | | server | api, microservices |
|
||||
|
||||
## Docker Secrets
|
||||
|
||||
@ -221,4 +214,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-docs]: https://github.com/docker-library/docs/tree/master/postgres#docker-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
|
||||
|
@ -8,6 +8,7 @@ import StorageTemplate from '/docs/partials/_storage-template.md';
|
||||
import MobileAppDownload from '/docs/partials/_mobile-app-download.md';
|
||||
import MobileAppLogin from '/docs/partials/_mobile-app-login.md';
|
||||
import MobileAppBackup from '/docs/partials/_mobile-app-backup.md';
|
||||
import ServerBackup from '/docs/partials/_server-backup.md';
|
||||
|
||||
# Post Install Steps
|
||||
|
||||
@ -33,6 +34,10 @@ A list of common steps to take after installing Immich include:
|
||||
|
||||
<MobileAppLogin />
|
||||
|
||||
## Step 6 - Backup Your Library
|
||||
## Step 6 - Upload Your Library
|
||||
|
||||
<MobileAppBackup />
|
||||
|
||||
## Step 7 - Setup Server Backups
|
||||
|
||||
<ServerBackup />
|
||||
|
@ -4,7 +4,7 @@ sidebar_position: 10
|
||||
|
||||
# Requirements
|
||||
|
||||
Hardware and software requirements for Immich
|
||||
Hardware and software requirements for Immich:
|
||||
|
||||
## Software
|
||||
|
||||
@ -23,7 +23,33 @@ Immich requires the command `docker compose` - the similarly named `docker-compo
|
||||
- **RAM**: Minimum 4GB, recommended 6GB.
|
||||
- **CPU**: Minimum 2 cores, recommended 4 cores.
|
||||
- **Storage**: Recommended Unix-compatible filesystem (EXT4, ZFS, APFS, etc.) with support for user/group ownership and permissions.
|
||||
- This can present an issue for Windows users. See [here](/docs/install/environment-variables#supported-filesystems)
|
||||
for more details and alternatives.
|
||||
- This can present an issue for Windows users. See below for details and an alternative setup.
|
||||
- The generation of thumbnails and transcoded video can increase the size of the photo library by 10-20% on average.
|
||||
- Network shares are supported for the storage of image and video assets only.
|
||||
- Network shares are supported for the storage of image and video assets only. It is not recommended to use a network share for your database location due to performance and possible data loss issues.
|
||||
|
||||
### Special requirements for Windows users
|
||||
|
||||
<details>
|
||||
<summary>Database storage on Windows systems</summary>
|
||||
|
||||
The Immich Postgres database (`DB_DATA_LOCATION`) must be located on a filesystem that supports user/group
|
||||
ownership and permissions (EXT2/3/4, ZFS, APFS, BTRFS, XFS, etc.). It will not work on any filesystem formatted in NTFS or ex/FAT/32.
|
||||
It will not work in WSL (Windows Subsystem for Linux) when using a mounted host directory (commonly under `/mnt`).
|
||||
If this is an issue, you can change the bind mount to a Docker volume instead as follows:
|
||||
|
||||
Make the following change to `.env`:
|
||||
|
||||
```diff
|
||||
- DB_DATA_LOCATION=./postgres
|
||||
+ DB_DATA_LOCATION=pgdata
|
||||
```
|
||||
|
||||
Add the following line to the bottom of `docker-compose.yml`:
|
||||
|
||||
```diff
|
||||
volumes:
|
||||
model-cache:
|
||||
+ pgdata:
|
||||
```
|
||||
|
||||
</details>
|
||||
|
@ -30,6 +30,8 @@ You can organize these as one parent with seven child datasets, for example `mnt
|
||||
|
||||
:::info Permissions
|
||||
The **pgData** dataset must be owned by the user `netdata` (UID 999) for postgres to start. The other datasets must be owned by the user `root` (UID 0) or a group that includes the user `root` (UID 0) for immich to have the necessary permissions.
|
||||
|
||||
The **library** dataset must have [ACL mode](https://www.truenas.com/docs/core/coretutorials/storage/pools/permissions/#access-control-lists) set to `Passthrough` if you plan on using a [storage template](/docs/administration/storage-template.mdx) and the dataset is configured for network sharing (its ACL type is set to `SMB/NFSv4`). When the template is applied and files need to be moved from **uploads** to **library**, immich performs `chmod` internally and needs to be allowed to execute the command.
|
||||
:::
|
||||
|
||||
## Installing the Immich Application
|
||||
|
@ -45,7 +45,7 @@ width="70%"
|
||||
alt="Select Plugins > Compose.Manager > Add New Stack > Label it Immich"
|
||||
/>
|
||||
|
||||
3. Select the cog ⚙️ next to Immich then click "**Edit Stack**"
|
||||
3. Select the cogwheel ⚙️ next to Immich and click "**Edit Stack**"
|
||||
4. Click "**Compose File**" and then paste the entire contents of the [Immich Docker Compose](https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml) file into the Unraid editor. Remove any text that may be in the text area by default. Note that Unraid v6.12.10 uses version 24.0.9 of the Docker Engine, which does not support healthcheck `start_interval` as defined in the `database` service of the Docker compose file (version 25 or higher is needed). This parameter defines an initial waiting period before starting health checks, to give the container time to start up. Commenting out the `start_interval` and `start_period` parameters will allow the containers to start up normally. The only downside to this is that the database container will not receive an initial health check until `interval` time has passed.
|
||||
|
||||
<details >
|
||||
@ -77,6 +77,7 @@ alt="Select Plugins > Compose.Manager > Add New Stack > Label it Immich"
|
||||
7. Paste the entire contents of the [Immich example.env](https://github.com/immich-app/immich/releases/latest/download/example.env) file into the Unraid editor, then **before saving** edit the following:
|
||||
|
||||
- `UPLOAD_LOCATION`: Create a folder in your Images Unraid share and place the **absolute** location here > For example my _"images"_ share has a folder within it called _"immich"_. If I browse to this directory in the terminal and type `pwd` the output is `/mnt/user/images/immich`. This is the exact value I need to enter as my `UPLOAD_LOCATION`
|
||||
- `DB_DATA_LOCATION`: Change this to use an Unraid share (preferably a cache pool, e.g. `/mnt/user/appdata`). If left at default it will try to use Unraid's `/boot/config/plugins/compose.manager/projects/[stack_name]/postgres` folder which it doesn't have permissions to, resulting in this container continuously restarting.
|
||||
|
||||
<img
|
||||
src={require('./img/unraid05.webp').default}
|
||||
@ -130,7 +131,7 @@ For more information on how to use the application once installed, please refer
|
||||
|
||||
## Updating Steps
|
||||
|
||||
Updating is extremely easy however it's important to be aware that containers managed via the Docker Compose Manager plugin do not integrate with Unraid's native dockerman ui, the label "_update ready_" will always be present on containers installed via the Docker Compose Manager.
|
||||
Updating is extremely easy however it's important to be aware that containers managed via the Docker Compose Manager plugin do not integrate with Unraid's native dockerman UI, the label "_update ready_" will always be present on containers installed via the Docker Compose Manager.
|
||||
|
||||
<img
|
||||
src={require('./img/unraid09.png').default}
|
||||
|
@ -16,5 +16,9 @@ Support the project by localizing on [Weblate](https://hosted.weblate.org/projec
|
||||
|
||||
If you are a programmer or developer, take a look at Immich's [technology stack](/docs/developer/architecture.mdx) and consider fixing bugs or building new features. The team and I are always looking for new contributors. For information about how to contribute as a developer, see the [Developer](/docs/developer/architecture.mdx) section.
|
||||
|
||||
## Purchase Immich
|
||||
|
||||
You can also [purchase Immich](https://buy.immich.app), for either one user or your entire server. Building Immich takes a lot of time and effort, and we have full-time engineers working on it to make it as good as we possibly can, so any support is greatly appreciated. Don't worry, all features will be free, forever! Nothing will ever be put behind any paywalls.
|
||||
|
||||
[github-issue]: https://github.com/immich-app/immich/issues/new/choose
|
||||
[github-langs]: https://github.com/immich-app/immich/tree/main/mobile/assets/i18n
|
||||
|
2
docs/docs/partials/_server-backup.md
Normal file
@ -0,0 +1,2 @@
|
||||
Now that you have imported some pictures, you should setup server backups to preserve your memories.
|
||||
You can do so by following our [backup guide](/docs/administration/backup-and-restore.md).
|
@ -27,3 +27,9 @@ If an asset is in multiple albums, `{{album}}` will be set to the name of the al
|
||||
:::
|
||||
|
||||
Immich also provides a mechanism to migrate between templates so that if the template you set now doesn't work in the future, you can always migrate all the existing files to the new template. The mechanism is run as a job on the Job page.
|
||||
|
||||
If you want to store assets in album folders, but you also have assets that do not belong to any album, you can use `{{#if album}}`, `{{else}}` and `{{/if}}` to create a conditional statement. For example, the following template will store assets in album folders if they belong to an album, and in a folder named "Other/Month" if they do not belong to an album:
|
||||
|
||||
```
|
||||
{{y}}/{{#if album}}{{album}}{{else}}Other{{/if}}/{{MM}}/{{filename}}
|
||||
```
|
||||
|
Before Width: | Height: | Size: 404 KiB After Width: | Height: | Size: 1.5 MiB |
@ -72,14 +72,9 @@ const config = {
|
||||
themeConfig:
|
||||
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
|
||||
({
|
||||
colorMode: {
|
||||
defaultMode: 'dark',
|
||||
},
|
||||
announcementBar: {
|
||||
id: 'site_announcement_immich',
|
||||
content: `⚠️ The project is under <strong>very active</strong> development. Expect bugs and changes. Do not use it as <strong>the only way</strong> to store your photos and videos!`,
|
||||
backgroundColor: '#593f00',
|
||||
textColor: '#ffefc9',
|
||||
isCloseable: false,
|
||||
},
|
||||
docs: {
|
||||
@ -145,28 +140,36 @@ const config = {
|
||||
label: 'Installation',
|
||||
to: '/docs/install/requirements',
|
||||
},
|
||||
{
|
||||
label: 'Contributing',
|
||||
to: '/docs/overview/support-the-project',
|
||||
},
|
||||
{
|
||||
label: 'Privacy Policy',
|
||||
to: '/privacy-policy',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Community',
|
||||
title: 'Documentation',
|
||||
items: [
|
||||
{
|
||||
label: 'Discord',
|
||||
href: 'https://discord.immich.app',
|
||||
label: 'Roadmap',
|
||||
to: '/roadmap',
|
||||
},
|
||||
{
|
||||
label: 'Reddit',
|
||||
href: 'https://www.reddit.com/r/immich/',
|
||||
label: 'API',
|
||||
to: '/docs/api',
|
||||
},
|
||||
{
|
||||
label: 'Cursed Knowledge',
|
||||
to: '/cursed-knowledge',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Links',
|
||||
items: [
|
||||
// {
|
||||
// label: "Blog",
|
||||
// to: "/blog",
|
||||
// },
|
||||
{
|
||||
label: 'GitHub',
|
||||
href: 'https://github.com/immich-app/immich',
|
||||
@ -175,6 +178,14 @@ const config = {
|
||||
label: 'YouTube',
|
||||
href: 'https://www.youtube.com/@immich-app',
|
||||
},
|
||||
{
|
||||
label: 'Discord',
|
||||
href: 'https://discord.immich.app',
|
||||
},
|
||||
{
|
||||
label: 'Reddit',
|
||||
href: 'https://www.reddit.com/r/immich/',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
@ -185,7 +196,7 @@ const config = {
|
||||
darkTheme: prism.themes.dracula,
|
||||
additionalLanguages: ['sql', 'diff', 'bash', 'powershell', 'nginx'],
|
||||
},
|
||||
image: 'overview/img/feature-panel.png',
|
||||
image: 'img/feature-panel.png',
|
||||
}),
|
||||
};
|
||||
|
||||
|
615
docs/package-lock.json
generated
@ -2155,9 +2155,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/core": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.4.0.tgz",
|
||||
"integrity": "sha512-g+0wwmN2UJsBqy2fQRQ6fhXruoEa62JDeEa5d8IdTJlMoaDaEDfHh7WjwGRn4opuTQWpjAwP/fbcgyHKlE+64w==",
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.5.2.tgz",
|
||||
"integrity": "sha512-4Z1WkhCSkX4KO0Fw5m/Vuc7Q3NxBG53NE5u59Rs96fWkMPZVSrzEPP16/Nk6cWb/shK7xXPndTmalJtw7twL/w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.23.3",
|
||||
@ -2170,12 +2170,12 @@
|
||||
"@babel/runtime": "^7.22.6",
|
||||
"@babel/runtime-corejs3": "^7.22.6",
|
||||
"@babel/traverse": "^7.22.8",
|
||||
"@docusaurus/cssnano-preset": "3.4.0",
|
||||
"@docusaurus/logger": "3.4.0",
|
||||
"@docusaurus/mdx-loader": "3.4.0",
|
||||
"@docusaurus/utils": "3.4.0",
|
||||
"@docusaurus/utils-common": "3.4.0",
|
||||
"@docusaurus/utils-validation": "3.4.0",
|
||||
"@docusaurus/cssnano-preset": "3.5.2",
|
||||
"@docusaurus/logger": "3.5.2",
|
||||
"@docusaurus/mdx-loader": "3.5.2",
|
||||
"@docusaurus/utils": "3.5.2",
|
||||
"@docusaurus/utils-common": "3.5.2",
|
||||
"@docusaurus/utils-validation": "3.5.2",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"babel-loader": "^9.1.3",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||
@ -2236,14 +2236,15 @@
|
||||
"node": ">=18.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/cssnano-preset": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.4.0.tgz",
|
||||
"integrity": "sha512-qwLFSz6v/pZHy/UP32IrprmH5ORce86BGtN0eBtG75PpzQJAzp9gefspox+s8IEOr0oZKuQ/nhzZ3xwyc3jYJQ==",
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.5.2.tgz",
|
||||
"integrity": "sha512-D3KiQXOMA8+O0tqORBrTOEQyQxNIfPm9jEaJoALjjSjc2M/ZAWcUfPQEnwr2JB2TadHw2gqWgpZckQmrVWkytA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cssnano-preset-advanced": "^6.1.2",
|
||||
@ -2256,9 +2257,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/logger": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.4.0.tgz",
|
||||
"integrity": "sha512-bZwkX+9SJ8lB9kVRkXw+xvHYSMGG4bpYHKGXeXFvyVc79NMeeBSGgzd4TQLHH+DYeOJoCdl8flrFJVxlZ0wo/Q==",
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.5.2.tgz",
|
||||
"integrity": "sha512-LHC540SGkeLfyT3RHK3gAMK6aS5TRqOD4R72BEU/DE2M/TY8WwEUAMY576UUc/oNJXv8pGhBmQB6N9p3pt8LQw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chalk": "^4.1.2",
|
||||
@ -2269,14 +2270,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/mdx-loader": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.4.0.tgz",
|
||||
"integrity": "sha512-kSSbrrk4nTjf4d+wtBA9H+FGauf2gCax89kV8SUSJu3qaTdSIKdWERlngsiHaCFgZ7laTJ8a67UFf+xlFPtuTw==",
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.5.2.tgz",
|
||||
"integrity": "sha512-ku3xO9vZdwpiMIVd8BzWV0DCqGEbCP5zs1iHfKX50vw6jX8vQo0ylYo1YJMZyz6e+JFJ17HYHT5FzVidz2IflA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/logger": "3.4.0",
|
||||
"@docusaurus/utils": "3.4.0",
|
||||
"@docusaurus/utils-validation": "3.4.0",
|
||||
"@docusaurus/logger": "3.5.2",
|
||||
"@docusaurus/utils": "3.5.2",
|
||||
"@docusaurus/utils-validation": "3.5.2",
|
||||
"@mdx-js/mdx": "^3.0.0",
|
||||
"@slorber/remark-comment": "^1.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
@ -2308,12 +2309,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/module-type-aliases": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.4.0.tgz",
|
||||
"integrity": "sha512-A1AyS8WF5Bkjnb8s+guTDuYmUiwJzNrtchebBHpc0gz0PyHJNMaybUlSrmJjHVcGrya0LKI4YcR3lBDQfXRYLw==",
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.5.2.tgz",
|
||||
"integrity": "sha512-Z+Xu3+2rvKef/YKTMxZHsEXp1y92ac0ngjDiExRdqGTmEKtCUpkbNYH8v5eXo5Ls+dnW88n6WTa+Q54kLOkwPg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/types": "3.4.0",
|
||||
"@docusaurus/types": "3.5.2",
|
||||
"@types/history": "^4.7.11",
|
||||
"@types/react": "*",
|
||||
"@types/react-router-config": "*",
|
||||
@ -2326,52 +2327,21 @@
|
||||
"react-dom": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/plugin-content-blog": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.4.0.tgz",
|
||||
"integrity": "sha512-vv6ZAj78ibR5Jh7XBUT4ndIjmlAxkijM3Sx5MAAzC1gyv0vupDQNhzuFg1USQmQVj3P5I6bquk12etPV3LJ+Xw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.4.0",
|
||||
"@docusaurus/logger": "3.4.0",
|
||||
"@docusaurus/mdx-loader": "3.4.0",
|
||||
"@docusaurus/types": "3.4.0",
|
||||
"@docusaurus/utils": "3.4.0",
|
||||
"@docusaurus/utils-common": "3.4.0",
|
||||
"@docusaurus/utils-validation": "3.4.0",
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"feed": "^4.2.2",
|
||||
"fs-extra": "^11.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
"reading-time": "^1.5.0",
|
||||
"srcset": "^4.0.0",
|
||||
"tslib": "^2.6.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"utility-types": "^3.10.0",
|
||||
"webpack": "^5.88.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/plugin-content-docs": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.4.0.tgz",
|
||||
"integrity": "sha512-HkUCZffhBo7ocYheD9oZvMcDloRnGhBMOZRyVcAQRFmZPmNqSyISlXA1tQCIxW+r478fty97XXAGjNYzBjpCsg==",
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.5.2.tgz",
|
||||
"integrity": "sha512-Bt+OXn/CPtVqM3Di44vHjE7rPCEsRCB/DMo2qoOuozB9f7+lsdrHvD0QCHdBs0uhz6deYJDppAr2VgqybKPlVQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.4.0",
|
||||
"@docusaurus/logger": "3.4.0",
|
||||
"@docusaurus/mdx-loader": "3.4.0",
|
||||
"@docusaurus/module-type-aliases": "3.4.0",
|
||||
"@docusaurus/types": "3.4.0",
|
||||
"@docusaurus/utils": "3.4.0",
|
||||
"@docusaurus/utils-common": "3.4.0",
|
||||
"@docusaurus/utils-validation": "3.4.0",
|
||||
"@docusaurus/core": "3.5.2",
|
||||
"@docusaurus/logger": "3.5.2",
|
||||
"@docusaurus/mdx-loader": "3.5.2",
|
||||
"@docusaurus/module-type-aliases": "3.5.2",
|
||||
"@docusaurus/theme-common": "3.5.2",
|
||||
"@docusaurus/types": "3.5.2",
|
||||
"@docusaurus/utils": "3.5.2",
|
||||
"@docusaurus/utils-common": "3.5.2",
|
||||
"@docusaurus/utils-validation": "3.5.2",
|
||||
"@types/react-router-config": "^5.0.7",
|
||||
"combine-promises": "^1.1.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
@ -2389,38 +2359,15 @@
|
||||
"react-dom": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/plugin-content-pages": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.4.0.tgz",
|
||||
"integrity": "sha512-h2+VN/0JjpR8fIkDEAoadNjfR3oLzB+v1qSXbIAKjQ46JAHx3X22n9nqS+BWSQnTnp1AjkjSvZyJMekmcwxzxg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.4.0",
|
||||
"@docusaurus/mdx-loader": "3.4.0",
|
||||
"@docusaurus/types": "3.4.0",
|
||||
"@docusaurus/utils": "3.4.0",
|
||||
"@docusaurus/utils-validation": "3.4.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
"tslib": "^2.6.0",
|
||||
"webpack": "^5.88.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/plugin-debug": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.4.0.tgz",
|
||||
"integrity": "sha512-uV7FDUNXGyDSD3PwUaf5YijX91T5/H9SX4ErEcshzwgzWwBtK37nUWPU3ZLJfeTavX3fycTOqk9TglpOLaWkCg==",
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.5.2.tgz",
|
||||
"integrity": "sha512-kBK6GlN0itCkrmHuCS6aX1wmoWc5wpd5KJlqQ1FyrF0cLDnvsYSnh7+ftdwzt7G6lGBho8lrVwkkL9/iQvaSOA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.4.0",
|
||||
"@docusaurus/types": "3.4.0",
|
||||
"@docusaurus/utils": "3.4.0",
|
||||
"@docusaurus/core": "3.5.2",
|
||||
"@docusaurus/types": "3.5.2",
|
||||
"@docusaurus/utils": "3.5.2",
|
||||
"fs-extra": "^11.1.1",
|
||||
"react-json-view-lite": "^1.2.0",
|
||||
"tslib": "^2.6.0"
|
||||
@ -2434,14 +2381,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/plugin-google-analytics": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.4.0.tgz",
|
||||
"integrity": "sha512-mCArluxEGi3cmYHqsgpGGt3IyLCrFBxPsxNZ56Mpur0xSlInnIHoeLDH7FvVVcPJRPSQ9/MfRqLsainRw+BojA==",
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.5.2.tgz",
|
||||
"integrity": "sha512-rjEkJH/tJ8OXRE9bwhV2mb/WP93V441rD6XnM6MIluu7rk8qg38iSxS43ga2V2Q/2ib53PcqbDEJDG/yWQRJhQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.4.0",
|
||||
"@docusaurus/types": "3.4.0",
|
||||
"@docusaurus/utils-validation": "3.4.0",
|
||||
"@docusaurus/core": "3.5.2",
|
||||
"@docusaurus/types": "3.5.2",
|
||||
"@docusaurus/utils-validation": "3.5.2",
|
||||
"tslib": "^2.6.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -2453,14 +2400,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/plugin-google-gtag": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.4.0.tgz",
|
||||
"integrity": "sha512-Dsgg6PLAqzZw5wZ4QjUYc8Z2KqJqXxHxq3vIoyoBWiLEEfigIs7wHR+oiWUQy3Zk9MIk6JTYj7tMoQU0Jm3nqA==",
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.5.2.tgz",
|
||||
"integrity": "sha512-lm8XL3xLkTPHFKKjLjEEAHUrW0SZBSHBE1I+i/tmYMBsjCcUB5UJ52geS5PSiOCFVR74tbPGcPHEV/gaaxFeSA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.4.0",
|
||||
"@docusaurus/types": "3.4.0",
|
||||
"@docusaurus/utils-validation": "3.4.0",
|
||||
"@docusaurus/core": "3.5.2",
|
||||
"@docusaurus/types": "3.5.2",
|
||||
"@docusaurus/utils-validation": "3.5.2",
|
||||
"@types/gtag.js": "^0.0.12",
|
||||
"tslib": "^2.6.0"
|
||||
},
|
||||
@ -2473,14 +2420,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/plugin-google-tag-manager": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.4.0.tgz",
|
||||
"integrity": "sha512-O9tX1BTwxIhgXpOLpFDueYA9DWk69WCbDRrjYoMQtFHSkTyE7RhNgyjSPREUWJb9i+YUg3OrsvrBYRl64FCPCQ==",
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.5.2.tgz",
|
||||
"integrity": "sha512-QkpX68PMOMu10Mvgvr5CfZAzZQFx8WLlOiUQ/Qmmcl6mjGK6H21WLT5x7xDmcpCoKA/3CegsqIqBR+nA137lQg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.4.0",
|
||||
"@docusaurus/types": "3.4.0",
|
||||
"@docusaurus/utils-validation": "3.4.0",
|
||||
"@docusaurus/core": "3.5.2",
|
||||
"@docusaurus/types": "3.5.2",
|
||||
"@docusaurus/utils-validation": "3.5.2",
|
||||
"tslib": "^2.6.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -2492,17 +2439,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/plugin-sitemap": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.4.0.tgz",
|
||||
"integrity": "sha512-+0VDvx9SmNrFNgwPoeoCha+tRoAjopwT0+pYO1xAbyLcewXSemq+eLxEa46Q1/aoOaJQ0qqHELuQM7iS2gp33Q==",
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.5.2.tgz",
|
||||
"integrity": "sha512-DnlqYyRAdQ4NHY28TfHuVk414ft2uruP4QWCH//jzpHjqvKyXjj2fmDtI8RPUBh9K8iZKFMHRnLtzJKySPWvFA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.4.0",
|
||||
"@docusaurus/logger": "3.4.0",
|
||||
"@docusaurus/types": "3.4.0",
|
||||
"@docusaurus/utils": "3.4.0",
|
||||
"@docusaurus/utils-common": "3.4.0",
|
||||
"@docusaurus/utils-validation": "3.4.0",
|
||||
"@docusaurus/core": "3.5.2",
|
||||
"@docusaurus/logger": "3.5.2",
|
||||
"@docusaurus/types": "3.5.2",
|
||||
"@docusaurus/utils": "3.5.2",
|
||||
"@docusaurus/utils-common": "3.5.2",
|
||||
"@docusaurus/utils-validation": "3.5.2",
|
||||
"fs-extra": "^11.1.1",
|
||||
"sitemap": "^7.1.1",
|
||||
"tslib": "^2.6.0"
|
||||
@ -2516,24 +2463,81 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/preset-classic": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.4.0.tgz",
|
||||
"integrity": "sha512-Ohj6KB7siKqZaQhNJVMBBUzT3Nnp6eTKqO+FXO3qu/n1hJl3YLwVKTWBg28LF7MWrKu46UuYavwMRxud0VyqHg==",
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.5.2.tgz",
|
||||
"integrity": "sha512-3ihfXQ95aOHiLB5uCu+9PRy2gZCeSZoDcqpnDvf3B+sTrMvMTr8qRUzBvWkoIqc82yG5prCboRjk1SVILKx6sg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.4.0",
|
||||
"@docusaurus/plugin-content-blog": "3.4.0",
|
||||
"@docusaurus/plugin-content-docs": "3.4.0",
|
||||
"@docusaurus/plugin-content-pages": "3.4.0",
|
||||
"@docusaurus/plugin-debug": "3.4.0",
|
||||
"@docusaurus/plugin-google-analytics": "3.4.0",
|
||||
"@docusaurus/plugin-google-gtag": "3.4.0",
|
||||
"@docusaurus/plugin-google-tag-manager": "3.4.0",
|
||||
"@docusaurus/plugin-sitemap": "3.4.0",
|
||||
"@docusaurus/theme-classic": "3.4.0",
|
||||
"@docusaurus/theme-common": "3.4.0",
|
||||
"@docusaurus/theme-search-algolia": "3.4.0",
|
||||
"@docusaurus/types": "3.4.0"
|
||||
"@docusaurus/core": "3.5.2",
|
||||
"@docusaurus/plugin-content-blog": "3.5.2",
|
||||
"@docusaurus/plugin-content-docs": "3.5.2",
|
||||
"@docusaurus/plugin-content-pages": "3.5.2",
|
||||
"@docusaurus/plugin-debug": "3.5.2",
|
||||
"@docusaurus/plugin-google-analytics": "3.5.2",
|
||||
"@docusaurus/plugin-google-gtag": "3.5.2",
|
||||
"@docusaurus/plugin-google-tag-manager": "3.5.2",
|
||||
"@docusaurus/plugin-sitemap": "3.5.2",
|
||||
"@docusaurus/theme-classic": "3.5.2",
|
||||
"@docusaurus/theme-common": "3.5.2",
|
||||
"@docusaurus/theme-search-algolia": "3.5.2",
|
||||
"@docusaurus/types": "3.5.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/preset-classic/node_modules/@docusaurus/plugin-content-blog": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.5.2.tgz",
|
||||
"integrity": "sha512-R7ghWnMvjSf+aeNDH0K4fjyQnt5L0KzUEnUhmf1e3jZrv3wogeytZNN6n7X8yHcMsuZHPOrctQhXWnmxu+IRRg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.5.2",
|
||||
"@docusaurus/logger": "3.5.2",
|
||||
"@docusaurus/mdx-loader": "3.5.2",
|
||||
"@docusaurus/theme-common": "3.5.2",
|
||||
"@docusaurus/types": "3.5.2",
|
||||
"@docusaurus/utils": "3.5.2",
|
||||
"@docusaurus/utils-common": "3.5.2",
|
||||
"@docusaurus/utils-validation": "3.5.2",
|
||||
"cheerio": "1.0.0-rc.12",
|
||||
"feed": "^4.2.2",
|
||||
"fs-extra": "^11.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
"reading-time": "^1.5.0",
|
||||
"srcset": "^4.0.0",
|
||||
"tslib": "^2.6.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"utility-types": "^3.10.0",
|
||||
"webpack": "^5.88.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@docusaurus/plugin-content-docs": "*",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/preset-classic/node_modules/@docusaurus/plugin-content-pages": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.5.2.tgz",
|
||||
"integrity": "sha512-WzhHjNpoQAUz/ueO10cnundRz+VUtkjFhhaQ9jApyv1a46FPURO4cef89pyNIOMny1fjDz/NUN2z6Yi+5WUrCw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.5.2",
|
||||
"@docusaurus/mdx-loader": "3.5.2",
|
||||
"@docusaurus/types": "3.5.2",
|
||||
"@docusaurus/utils": "3.5.2",
|
||||
"@docusaurus/utils-validation": "3.5.2",
|
||||
"fs-extra": "^11.1.1",
|
||||
"tslib": "^2.6.0",
|
||||
"webpack": "^5.88.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0"
|
||||
@ -2544,27 +2548,27 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/theme-classic": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.4.0.tgz",
|
||||
"integrity": "sha512-0IPtmxsBYv2adr1GnZRdMkEQt1YW6tpzrUPj02YxNpvJ5+ju4E13J5tB4nfdaen/tfR1hmpSPlTFPvTf4kwy8Q==",
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.5.2.tgz",
|
||||
"integrity": "sha512-XRpinSix3NBv95Rk7xeMF9k4safMkwnpSgThn0UNQNumKvmcIYjfkwfh2BhwYh/BxMXQHJ/PdmNh22TQFpIaYg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.4.0",
|
||||
"@docusaurus/mdx-loader": "3.4.0",
|
||||
"@docusaurus/module-type-aliases": "3.4.0",
|
||||
"@docusaurus/plugin-content-blog": "3.4.0",
|
||||
"@docusaurus/plugin-content-docs": "3.4.0",
|
||||
"@docusaurus/plugin-content-pages": "3.4.0",
|
||||
"@docusaurus/theme-common": "3.4.0",
|
||||
"@docusaurus/theme-translations": "3.4.0",
|
||||
"@docusaurus/types": "3.4.0",
|
||||
"@docusaurus/utils": "3.4.0",
|
||||
"@docusaurus/utils-common": "3.4.0",
|
||||
"@docusaurus/utils-validation": "3.4.0",
|
||||
"@docusaurus/core": "3.5.2",
|
||||
"@docusaurus/mdx-loader": "3.5.2",
|
||||
"@docusaurus/module-type-aliases": "3.5.2",
|
||||
"@docusaurus/plugin-content-blog": "3.5.2",
|
||||
"@docusaurus/plugin-content-docs": "3.5.2",
|
||||
"@docusaurus/plugin-content-pages": "3.5.2",
|
||||
"@docusaurus/theme-common": "3.5.2",
|
||||
"@docusaurus/theme-translations": "3.5.2",
|
||||
"@docusaurus/types": "3.5.2",
|
||||
"@docusaurus/utils": "3.5.2",
|
||||
"@docusaurus/utils-common": "3.5.2",
|
||||
"@docusaurus/utils-validation": "3.5.2",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"clsx": "^2.0.0",
|
||||
"copy-text-to-clipboard": "^3.2.0",
|
||||
"infima": "0.2.0-alpha.43",
|
||||
"infima": "0.2.0-alpha.44",
|
||||
"lodash": "^4.17.21",
|
||||
"nprogress": "^0.2.0",
|
||||
"postcss": "^8.4.26",
|
||||
@ -2583,19 +2587,73 @@
|
||||
"react-dom": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/theme-common": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.4.0.tgz",
|
||||
"integrity": "sha512-0A27alXuv7ZdCg28oPE8nH/Iz73/IUejVaCazqu9elS4ypjiLhK3KfzdSQBnL/g7YfHSlymZKdiOHEo8fJ0qMA==",
|
||||
"node_modules/@docusaurus/theme-classic/node_modules/@docusaurus/plugin-content-blog": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.5.2.tgz",
|
||||
"integrity": "sha512-R7ghWnMvjSf+aeNDH0K4fjyQnt5L0KzUEnUhmf1e3jZrv3wogeytZNN6n7X8yHcMsuZHPOrctQhXWnmxu+IRRg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/mdx-loader": "3.4.0",
|
||||
"@docusaurus/module-type-aliases": "3.4.0",
|
||||
"@docusaurus/plugin-content-blog": "3.4.0",
|
||||
"@docusaurus/plugin-content-docs": "3.4.0",
|
||||
"@docusaurus/plugin-content-pages": "3.4.0",
|
||||
"@docusaurus/utils": "3.4.0",
|
||||
"@docusaurus/utils-common": "3.4.0",
|
||||
"@docusaurus/core": "3.5.2",
|
||||
"@docusaurus/logger": "3.5.2",
|
||||
"@docusaurus/mdx-loader": "3.5.2",
|
||||
"@docusaurus/theme-common": "3.5.2",
|
||||
"@docusaurus/types": "3.5.2",
|
||||
"@docusaurus/utils": "3.5.2",
|
||||
"@docusaurus/utils-common": "3.5.2",
|
||||
"@docusaurus/utils-validation": "3.5.2",
|
||||
"cheerio": "1.0.0-rc.12",
|
||||
"feed": "^4.2.2",
|
||||
"fs-extra": "^11.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
"reading-time": "^1.5.0",
|
||||
"srcset": "^4.0.0",
|
||||
"tslib": "^2.6.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"utility-types": "^3.10.0",
|
||||
"webpack": "^5.88.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@docusaurus/plugin-content-docs": "*",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/theme-classic/node_modules/@docusaurus/plugin-content-pages": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.5.2.tgz",
|
||||
"integrity": "sha512-WzhHjNpoQAUz/ueO10cnundRz+VUtkjFhhaQ9jApyv1a46FPURO4cef89pyNIOMny1fjDz/NUN2z6Yi+5WUrCw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.5.2",
|
||||
"@docusaurus/mdx-loader": "3.5.2",
|
||||
"@docusaurus/types": "3.5.2",
|
||||
"@docusaurus/utils": "3.5.2",
|
||||
"@docusaurus/utils-validation": "3.5.2",
|
||||
"fs-extra": "^11.1.1",
|
||||
"tslib": "^2.6.0",
|
||||
"webpack": "^5.88.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/theme-common": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.5.2.tgz",
|
||||
"integrity": "sha512-QXqlm9S6x9Ibwjs7I2yEDgsCocp708DrCrgHgKwg2n2AY0YQ6IjU0gAK35lHRLOvAoJUfCKpQAwUykB0R7+Eew==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/mdx-loader": "3.5.2",
|
||||
"@docusaurus/module-type-aliases": "3.5.2",
|
||||
"@docusaurus/utils": "3.5.2",
|
||||
"@docusaurus/utils-common": "3.5.2",
|
||||
"@types/history": "^4.7.11",
|
||||
"@types/react": "*",
|
||||
"@types/react-router-config": "*",
|
||||
@ -2609,24 +2667,25 @@
|
||||
"node": ">=18.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@docusaurus/plugin-content-docs": "*",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/theme-search-algolia": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.4.0.tgz",
|
||||
"integrity": "sha512-aiHFx7OCw4Wck1z6IoShVdUWIjntC8FHCw9c5dR8r3q4Ynh+zkS8y2eFFunN/DL6RXPzpnvKCg3vhLQYJDmT9Q==",
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.5.2.tgz",
|
||||
"integrity": "sha512-qW53kp3VzMnEqZGjakaV90sst3iN1o32PH+nawv1uepROO8aEGxptcq2R5rsv7aBShSRbZwIobdvSYKsZ5pqvA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docsearch/react": "^3.5.2",
|
||||
"@docusaurus/core": "3.4.0",
|
||||
"@docusaurus/logger": "3.4.0",
|
||||
"@docusaurus/plugin-content-docs": "3.4.0",
|
||||
"@docusaurus/theme-common": "3.4.0",
|
||||
"@docusaurus/theme-translations": "3.4.0",
|
||||
"@docusaurus/utils": "3.4.0",
|
||||
"@docusaurus/utils-validation": "3.4.0",
|
||||
"@docusaurus/core": "3.5.2",
|
||||
"@docusaurus/logger": "3.5.2",
|
||||
"@docusaurus/plugin-content-docs": "3.5.2",
|
||||
"@docusaurus/theme-common": "3.5.2",
|
||||
"@docusaurus/theme-translations": "3.5.2",
|
||||
"@docusaurus/utils": "3.5.2",
|
||||
"@docusaurus/utils-validation": "3.5.2",
|
||||
"algoliasearch": "^4.18.0",
|
||||
"algoliasearch-helper": "^3.13.3",
|
||||
"clsx": "^2.0.0",
|
||||
@ -2645,9 +2704,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/theme-translations": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.4.0.tgz",
|
||||
"integrity": "sha512-zSxCSpmQCCdQU5Q4CnX/ID8CSUUI3fvmq4hU/GNP/XoAWtXo9SAVnM3TzpU8Gb//H3WCsT8mJcTfyOk3d9ftNg==",
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.5.2.tgz",
|
||||
"integrity": "sha512-GPZLcu4aT1EmqSTmbdpVrDENGR2yObFEX8ssEFYTCiAIVc0EihNSdOIBTazUvgNqwvnoU1A8vIs1xyzc3LITTw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fs-extra": "^11.1.1",
|
||||
@ -2658,9 +2717,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/types": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.4.0.tgz",
|
||||
"integrity": "sha512-4jcDO8kXi5Cf9TcyikB/yKmz14f2RZ2qTRerbHAsS+5InE9ZgSLBNLsewtFTcTOXSVcbU3FoGOzcNWAmU1TR0A==",
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.5.2.tgz",
|
||||
"integrity": "sha512-N6GntLXoLVUwkZw7zCxwy9QiuEXIcTVzA9AkmNw16oc0AP3SXLrMmDMMBIfgqwuKWa6Ox6epHol9kMtJqekACw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mdx-js/mdx": "^3.0.0",
|
||||
@ -2679,13 +2738,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/utils": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.4.0.tgz",
|
||||
"integrity": "sha512-fRwnu3L3nnWaXOgs88BVBmG1yGjcQqZNHG+vInhEa2Sz2oQB+ZjbEMO5Rh9ePFpZ0YDiDUhpaVjwmS+AU2F14g==",
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.5.2.tgz",
|
||||
"integrity": "sha512-33QvcNFh+Gv+C2dP9Y9xWEzMgf3JzrpL2nW9PopidiohS1nDcyknKRx2DWaFvyVTTYIkkABVSr073VTj/NITNA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/logger": "3.4.0",
|
||||
"@docusaurus/utils-common": "3.4.0",
|
||||
"@docusaurus/logger": "3.5.2",
|
||||
"@docusaurus/utils-common": "3.5.2",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"file-loader": "^6.2.0",
|
||||
@ -2718,9 +2777,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/utils-common": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.4.0.tgz",
|
||||
"integrity": "sha512-NVx54Wr4rCEKsjOH5QEVvxIqVvm+9kh7q8aYTU5WzUU9/Hctd6aTrcZ3G0Id4zYJ+AeaG5K5qHA4CY5Kcm2iyQ==",
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.5.2.tgz",
|
||||
"integrity": "sha512-i0AZjHiRgJU6d7faQngIhuHKNrszpL/SHQPgF1zH4H+Ij6E9NBYGy6pkcGWToIv7IVPbs+pQLh1P3whn0gWXVg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.0"
|
||||
@ -2738,14 +2797,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/utils-validation": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.4.0.tgz",
|
||||
"integrity": "sha512-hYQ9fM+AXYVTWxJOT1EuNaRnrR2WGpRdLDQG07O8UOpsvCPWUVOeo26Rbm0JWY2sGLfzAb+tvJ62yF+8F+TV0g==",
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.5.2.tgz",
|
||||
"integrity": "sha512-m+Foq7augzXqB6HufdS139PFxDC5d5q2QKZy8q0qYYvGdI6nnlNsGH4cIGsgBnV7smz+mopl3g4asbSDvMV0jA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/logger": "3.4.0",
|
||||
"@docusaurus/utils": "3.4.0",
|
||||
"@docusaurus/utils-common": "3.4.0",
|
||||
"@docusaurus/logger": "3.5.2",
|
||||
"@docusaurus/utils": "3.5.2",
|
||||
"@docusaurus/utils-common": "3.5.2",
|
||||
"fs-extra": "^11.2.0",
|
||||
"joi": "^17.9.2",
|
||||
"js-yaml": "^4.1.0",
|
||||
@ -2947,9 +3006,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mdx-js/react": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.0.1.tgz",
|
||||
"integrity": "sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz",
|
||||
"integrity": "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mdx": "^2.0.0"
|
||||
},
|
||||
@ -4237,9 +4297,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.4.19",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz",
|
||||
"integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==",
|
||||
"version": "10.4.20",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
|
||||
"integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@ -4254,12 +4314,13 @@
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.23.0",
|
||||
"caniuse-lite": "^1.0.30001599",
|
||||
"browserslist": "^4.23.3",
|
||||
"caniuse-lite": "^1.0.30001646",
|
||||
"fraction.js": "^4.3.7",
|
||||
"normalize-range": "^0.1.2",
|
||||
"picocolors": "^1.0.0",
|
||||
"picocolors": "^1.0.1",
|
||||
"postcss-value-parser": "^4.2.0"
|
||||
},
|
||||
"bin": {
|
||||
@ -4531,9 +4592,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.23.0",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
|
||||
"integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
|
||||
"version": "4.23.3",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz",
|
||||
"integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@ -4548,11 +4609,12 @@
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001587",
|
||||
"electron-to-chromium": "^1.4.668",
|
||||
"node-releases": "^2.0.14",
|
||||
"update-browserslist-db": "^1.0.13"
|
||||
"caniuse-lite": "^1.0.30001646",
|
||||
"electron-to-chromium": "^1.5.4",
|
||||
"node-releases": "^2.0.18",
|
||||
"update-browserslist-db": "^1.1.0"
|
||||
},
|
||||
"bin": {
|
||||
"browserslist": "cli.js"
|
||||
@ -4699,9 +4761,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001614",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001614.tgz",
|
||||
"integrity": "sha512-jmZQ1VpmlRwHgdP1/uiKzgiAuGOfLEJsYFP4+GBou/QQ4U6IOJCB4NP1c+1p9RGLpwObcT94jA5/uO+F1vBbog==",
|
||||
"version": "1.0.30001651",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz",
|
||||
"integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@ -4715,7 +4777,8 @@
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
]
|
||||
],
|
||||
"license": "CC-BY-4.0"
|
||||
},
|
||||
"node_modules/ccount": {
|
||||
"version": "2.0.1",
|
||||
@ -6006,9 +6069,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/docusaurus-lunr-search": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/docusaurus-lunr-search/-/docusaurus-lunr-search-3.4.0.tgz",
|
||||
"integrity": "sha512-GfllnNXCLgTSPH9TAKWmbn8VMfwpdOAZ1xl3T2GgX8Pm26qSDLfrrdVwjguaLfMJfzciFL97RKrAJlgrFM48yw==",
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/docusaurus-lunr-search/-/docusaurus-lunr-search-3.5.0.tgz",
|
||||
"integrity": "sha512-k3zN4jYMi/prWInJILGKOxE+BVcgYinwj9+gcECsYm52tS+4ZKzXQzbPnVJAEXmvKOfFMcDFvS3MSmm6cEaxIQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"autocomplete.js": "^0.37.0",
|
||||
"clsx": "^1.2.1",
|
||||
@ -6035,14 +6099,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/docusaurus-lunr-search/node_modules/@types/unist": {
|
||||
"version": "2.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz",
|
||||
"integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA=="
|
||||
"version": "2.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
|
||||
"integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/docusaurus-lunr-search/node_modules/bail": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz",
|
||||
"integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
@ -6052,6 +6118,7 @@
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@ -6060,6 +6127,7 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
|
||||
"integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@ -6068,6 +6136,7 @@
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz",
|
||||
"integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
@ -6077,6 +6146,7 @@
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz",
|
||||
"integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bail": "^1.0.0",
|
||||
"extend": "^3.0.0",
|
||||
@ -6094,6 +6164,7 @@
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz",
|
||||
"integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/unist": "^2.0.2"
|
||||
},
|
||||
@ -6106,6 +6177,7 @@
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz",
|
||||
"integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/unist": "^2.0.0",
|
||||
"is-buffer": "^2.0.0",
|
||||
@ -6121,6 +6193,7 @@
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz",
|
||||
"integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/unist": "^2.0.0",
|
||||
"unist-util-stringify-position": "^2.0.0"
|
||||
@ -6342,9 +6415,10 @@
|
||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.751",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.751.tgz",
|
||||
"integrity": "sha512-2DEPi++qa89SMGRhufWTiLmzqyuGmNF3SK4+PQetW1JKiZdEpF4XQonJXJCzyuYSA6mauiMhbyVhqYAP45Hvfw=="
|
||||
"version": "1.5.6",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.6.tgz",
|
||||
"integrity": "sha512-jwXWsM5RPf6j9dPYzaorcBSUg6AiqocPEyMpkchkvntaH9HGfOOMZwxMJjDY/XEs3T5dM7uyH1VhRMkqUU9qVw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
@ -8763,9 +8837,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/infima": {
|
||||
"version": "0.2.0-alpha.43",
|
||||
"resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.43.tgz",
|
||||
"integrity": "sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ==",
|
||||
"version": "0.2.0-alpha.44",
|
||||
"resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.44.tgz",
|
||||
"integrity": "sha512-tuRkUSO/lB3rEhLJk25atwAjgLuzq070+pOW8XcvpHky/YbENnRRdPd85IBkyeTgttmOy5ah+yHYsK1HhUd4lQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@ -11958,9 +12033,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.14",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
|
||||
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw=="
|
||||
"version": "2.0.18",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
|
||||
"integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nopt": {
|
||||
"version": "1.0.10",
|
||||
@ -12640,9 +12716,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
|
||||
"integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
|
||||
"integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
@ -12754,9 +12830,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.40",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.40.tgz",
|
||||
"integrity": "sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==",
|
||||
"version": "8.4.47",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
|
||||
"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@ -12774,8 +12850,8 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.0.1",
|
||||
"source-map-js": "^1.2.0"
|
||||
"picocolors": "^1.1.0",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
@ -13633,9 +13709,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prism-react-renderer": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.3.1.tgz",
|
||||
"integrity": "sha512-Rdf+HzBLR7KYjzpJ1rSoxT9ioO85nZngQEoFIhL07XhtJHlCU3SOz0GJ6+qvMyQe0Se+BV3qpe6Yd/NmQF5Juw==",
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.4.0.tgz",
|
||||
"integrity": "sha512-327BsVCD/unU4CNLZTWVHyUHKnsqcvj2qbPlQ8MiBE2eq2rgctjigPA1Gp9HLF83kZ20zNN6jgizHJeEsyFYOw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/prismjs": "^1.26.0",
|
||||
"clsx": "^2.0.0"
|
||||
@ -13747,9 +13824,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.12.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz",
|
||||
"integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==",
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.6"
|
||||
},
|
||||
@ -15593,9 +15671,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
||||
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@ -16014,9 +16093,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "3.4.7",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.7.tgz",
|
||||
"integrity": "sha512-rxWZbe87YJb4OcSopb7up2Ba4U82BoiSGUdoDr3Ydrg9ckxFS/YWsvhN323GMcddgU65QRy7JndC7ahhInhvlQ==",
|
||||
"version": "3.4.14",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.14.tgz",
|
||||
"integrity": "sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@alloc/quick-lru": "^5.2.0",
|
||||
@ -16376,9 +16455,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.5.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
|
||||
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
|
||||
"version": "5.6.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
|
||||
"integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
@ -16607,9 +16686,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.0.13",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
|
||||
"integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
|
||||
"integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@ -16624,9 +16703,10 @@
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"escalade": "^3.1.1",
|
||||
"picocolors": "^1.0.0"
|
||||
"escalade": "^3.1.2",
|
||||
"picocolors": "^1.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"update-browserslist-db": "cli.js"
|
||||
@ -16714,12 +16794,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/url": {
|
||||
"version": "0.11.3",
|
||||
"resolved": "https://registry.npmjs.org/url/-/url-0.11.3.tgz",
|
||||
"integrity": "sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==",
|
||||
"version": "0.11.4",
|
||||
"resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz",
|
||||
"integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"punycode": "^1.4.1",
|
||||
"qs": "^6.11.2"
|
||||
"qs": "^6.12.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/url-loader": {
|
||||
@ -16783,7 +16867,8 @@
|
||||
"node_modules/url/node_modules/punycode": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
|
||||
"integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ=="
|
||||
"integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/util": {
|
||||
"version": "0.10.4",
|
||||
|
@ -56,6 +56,6 @@
|
||||
"node": ">=20"
|
||||
},
|
||||
"volta": {
|
||||
"node": "20.16.0"
|
||||
"node": "22.11.0"
|
||||
}
|
||||
}
|
||||
|
@ -43,24 +43,29 @@ const guides: CommunityGuidesProps[] = [
|
||||
description: 'Access your local Immich installation over the internet using your own domain',
|
||||
url: 'https://github.com/ppr88/immich-guides/blob/main/open-immich-custom-domain.md',
|
||||
},
|
||||
{
|
||||
title: 'Nginx caching map server',
|
||||
description: 'Increase privacy by using nginx as a caching proxy in front of a map tile server',
|
||||
url: 'https://github.com/pcouy/pcouy.github.io/blob/main/_posts/2024-08-30-proxying-a-map-tile-server-for-increased-privacy.md',
|
||||
},
|
||||
];
|
||||
|
||||
function CommunityGuide({ title, description, url }: CommunityGuidesProps): JSX.Element {
|
||||
return (
|
||||
<section className="flex flex-col gap-4 justify-between dark:bg-immich-dark-gray bg-immich-gray dark:border-0 border-gray-200 border border-solid rounded-2xl p-4">
|
||||
<section className="flex flex-col gap-4 justify-between dark:bg-immich-dark-gray bg-immich-gray dark:border-0 border-gray-200 border border-solid rounded-2xl px-4 py-6">
|
||||
<div className="flex flex-col gap-2">
|
||||
<p className="m-0 items-start flex gap-2">
|
||||
<p className="m-0 items-start flex gap-2 text-2xl font-bold text-immich-primary dark:text-immich-dark-primary">
|
||||
<span>{title}</span>
|
||||
</p>
|
||||
|
||||
<p className="m-0 text-sm text-gray-600 dark:text-gray-300">{description}</p>
|
||||
<p className="m-0 text-sm text-gray-600 dark:text-gray-300">
|
||||
<p className="m-0 text-sm text-gray-600 dark:text-gray-300 my-4">
|
||||
<a href={url}>{url}</a>
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<Link
|
||||
className="px-4 py-2 bg-immich-primary/10 dark:bg-gray-300 rounded-full hover:no-underline text-immich-primary dark:text-immich-dark-bg font-bold uppercase"
|
||||
className="px-4 py-2 bg-immich-primary/10 dark:bg-gray-300 rounded-xl text-sm hover:no-underline text-immich-primary dark:text-immich-dark-bg font-semibold"
|
||||
to={url}
|
||||
>
|
||||
View Guide
|
||||
|
@ -28,11 +28,6 @@ const projects: CommunityProjectProps[] = [
|
||||
description: 'A simple way to remove orphaned offline assets from the Immich database',
|
||||
url: 'https://github.com/Thoroslives/immich_remove_offline_files',
|
||||
},
|
||||
{
|
||||
title: 'Create albums from folders',
|
||||
description: 'A Python script to create albums based on the folder structure of an external library.',
|
||||
url: 'https://github.com/Salvoxia/immich-folder-album-creator',
|
||||
},
|
||||
{
|
||||
title: 'Immich-Tools',
|
||||
description: 'Provides scripts for handling problems on the repair page.',
|
||||
@ -43,6 +38,11 @@ const projects: CommunityProjectProps[] = [
|
||||
description: 'Lightroom plugin to publish photos from Lightroom collections to Immich albums.',
|
||||
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',
|
||||
description: 'Webapp that uses machine learning to identify near-duplicate images.',
|
||||
@ -58,32 +58,52 @@ const projects: CommunityProjectProps[] = [
|
||||
description: 'Unofficial Immich Android TV app.',
|
||||
url: 'https://github.com/giejay/Immich-Android-TV',
|
||||
},
|
||||
{
|
||||
title: 'Create albums from folders',
|
||||
description: 'A Python script to create albums based on the folder structure of an external library.',
|
||||
url: 'https://github.com/Salvoxia/immich-folder-album-creator',
|
||||
},
|
||||
{
|
||||
title: 'Powershell Module PSImmich',
|
||||
description: 'Powershell Module for the Immich API',
|
||||
url: 'https://github.com/hanpq/PSImmich',
|
||||
},
|
||||
{
|
||||
title: 'Immich Distribution',
|
||||
description: 'Snap package for easy install and zero-care auto updates of Immich. Self-hosted photo management.',
|
||||
url: 'https://immich-distribution.nsg.cc',
|
||||
},
|
||||
{
|
||||
title: 'Immich Kiosk',
|
||||
description: 'Lightweight slideshow to run on kiosk devices and browsers.',
|
||||
url: 'https://github.com/damongolding/immich-kiosk',
|
||||
},
|
||||
{
|
||||
title: 'Immich Power Tools',
|
||||
description: 'Power tools for organizing your immich library.',
|
||||
url: 'https://github.com/varun-raj/immich-power-tools',
|
||||
},
|
||||
];
|
||||
|
||||
function CommunityProject({ title, description, url }: CommunityProjectProps): JSX.Element {
|
||||
return (
|
||||
<section className="flex flex-col gap-4 justify-between dark:bg-immich-dark-gray bg-immich-gray dark:border-0 border-gray-200 border border-solid rounded-2xl p-4">
|
||||
<section className="flex flex-col gap-4 justify-between dark:bg-immich-dark-gray bg-immich-gray dark:border-0 border-gray-200 border border-solid rounded-2xl px-4 py-6">
|
||||
<div className="flex flex-col gap-2">
|
||||
<p className="m-0 items-start flex gap-2">
|
||||
<p className="m-0 items-start flex gap-2 text-2xl font-bold text-immich-primary dark:text-immich-dark-primary">
|
||||
<span>{title}</span>
|
||||
</p>
|
||||
|
||||
<p className="m-0 text-sm text-gray-600 dark:text-gray-300">{description}</p>
|
||||
<p className="m-0 text-sm text-gray-600 dark:text-gray-300">
|
||||
<p className="m-0 text-sm text-gray-600 dark:text-gray-300 my-4">
|
||||
<a href={url}>{url}</a>
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<Link
|
||||
className="px-4 py-2 bg-immich-primary/10 dark:bg-gray-300 rounded-full hover:no-underline text-immich-primary dark:text-immich-dark-bg font-bold uppercase"
|
||||
className="px-4 py-2 bg-immich-primary/10 dark:bg-gray-300 rounded-xl text-sm hover:no-underline text-immich-primary dark:text-immich-dark-bg font-semibold"
|
||||
to={url}
|
||||
>
|
||||
View Project
|
||||
View Link
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
|
2
docs/src/components/svg-paths.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export const discordPath =
|
||||
'M 9.1367188 3.8691406 C 9.1217187 3.8691406 9.1067969 3.8700938 9.0917969 3.8710938 C 8.9647969 3.8810937 5.9534375 4.1403594 4.0234375 5.6933594 C 3.0154375 6.6253594 1 12.073203 1 16.783203 C 1 16.866203 1.0215 16.946531 1.0625 17.019531 C 2.4535 19.462531 6.2473281 20.102859 7.1113281 20.130859 L 7.1269531 20.130859 C 7.2799531 20.130859 7.4236719 20.057594 7.5136719 19.933594 L 8.3886719 18.732422 C 6.0296719 18.122422 4.8248594 17.086391 4.7558594 17.025391 C 4.5578594 16.850391 4.5378906 16.549563 4.7128906 16.351562 C 4.8068906 16.244563 4.9383125 16.189453 5.0703125 16.189453 C 5.1823125 16.189453 5.2957188 16.228594 5.3867188 16.308594 C 5.4157187 16.334594 7.6340469 18.216797 11.998047 18.216797 C 16.370047 18.216797 18.589328 16.325641 18.611328 16.306641 C 18.702328 16.227641 18.815734 16.189453 18.927734 16.189453 C 19.059734 16.189453 19.190156 16.243562 19.285156 16.351562 C 19.459156 16.549563 19.441141 16.851391 19.244141 17.025391 C 19.174141 17.087391 17.968375 18.120469 15.609375 18.730469 L 16.484375 19.933594 C 16.574375 20.057594 16.718094 20.130859 16.871094 20.130859 L 16.886719 20.130859 C 17.751719 20.103859 21.5465 19.463531 22.9375 17.019531 C 22.9785 16.947531 23 16.866203 23 16.783203 C 23 12.073203 20.984172 6.624875 19.951172 5.671875 C 18.047172 4.140875 15.036203 3.8820937 14.908203 3.8710938 C 14.895203 3.8700938 14.880188 3.8691406 14.867188 3.8691406 C 14.681188 3.8691406 14.510594 3.9793906 14.433594 4.1503906 C 14.427594 4.1623906 14.362062 4.3138281 14.289062 4.5488281 C 15.548063 4.7608281 17.094141 5.1895937 18.494141 6.0585938 C 18.718141 6.1975938 18.787437 6.4917969 18.648438 6.7167969 C 18.558438 6.8627969 18.402188 6.9433594 18.242188 6.9433594 C 18.156188 6.9433594 18.069234 6.9200937 17.990234 6.8710938 C 15.584234 5.3800938 12.578 5.3046875 12 5.3046875 C 11.422 5.3046875 8.4157187 5.3810469 6.0117188 6.8730469 C 5.9327188 6.9210469 5.8457656 6.9433594 5.7597656 6.9433594 C 5.5997656 6.9433594 5.4425625 6.86475 5.3515625 6.71875 C 5.2115625 6.49375 5.2818594 6.1985938 5.5058594 6.0585938 C 6.9058594 5.1905937 8.4528906 4.7627812 9.7128906 4.5507812 C 9.6388906 4.3147813 9.5714062 4.1643437 9.5664062 4.1523438 C 9.4894063 3.9813438 9.3217188 3.8691406 9.1367188 3.8691406 z M 12 7.3046875 C 12.296 7.3046875 14.950594 7.3403125 16.933594 8.5703125 C 17.326594 8.8143125 17.777234 8.9453125 18.240234 8.9453125 C 18.633234 8.9453125 19.010656 8.8555 19.347656 8.6875 C 19.964656 10.2405 20.690828 12.686219 20.923828 15.199219 C 20.883828 15.143219 20.840922 15.089109 20.794922 15.037109 C 20.324922 14.498109 19.644687 14.191406 18.929688 14.191406 C 18.332687 14.191406 17.754078 14.405437 17.330078 14.773438 C 17.257078 14.832437 15.505 16.21875 12 16.21875 C 8.496 16.21875 6.7450313 14.834687 6.7070312 14.804688 C 6.2540312 14.407687 5.6742656 14.189453 5.0722656 14.189453 C 4.3612656 14.189453 3.6838438 14.494391 3.2148438 15.025391 C 3.1658438 15.080391 3.1201719 15.138266 3.0761719 15.197266 C 3.3091719 12.686266 4.0344375 10.235594 4.6484375 8.6835938 C 4.9864375 8.8525938 5.3657656 8.9433594 5.7597656 8.9433594 C 6.2217656 8.9433594 6.6724531 8.8143125 7.0644531 8.5703125 C 9.0494531 7.3393125 11.704 7.3046875 12 7.3046875 z M 8.890625 10.044922 C 7.966625 10.044922 7.2167969 10.901031 7.2167969 11.957031 C 7.2167969 13.013031 7.965625 13.869141 8.890625 13.869141 C 9.815625 13.869141 10.564453 13.013031 10.564453 11.957031 C 10.564453 10.900031 9.815625 10.044922 8.890625 10.044922 z M 15.109375 10.044922 C 14.185375 10.044922 13.435547 10.901031 13.435547 11.957031 C 13.435547 13.013031 14.184375 13.869141 15.109375 13.869141 C 16.034375 13.869141 16.783203 13.013031 16.783203 11.957031 C 16.783203 10.900031 16.033375 10.044922 15.109375 10.044922 z';
|
@ -1,4 +1,3 @@
|
||||
import '@docusaurus/theme-classic/lib/theme/Unlisted/index';
|
||||
import { useWindowSize } from '@docusaurus/theme-common';
|
||||
import DropdownNavbarItem from '@theme/NavbarItem/DropdownNavbarItem';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
@ -7,11 +7,12 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Overpass:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Be+Vietnam+Pro:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
|
||||
|
||||
html,
|
||||
button {
|
||||
font-family: 'Overpass', sans-serif;
|
||||
font-family: 'Be Vietnam Pro', sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
}
|
||||
|
||||
img {
|
||||
@ -27,7 +28,6 @@ img {
|
||||
--ifm-color-primary-light: #4250af;
|
||||
--ifm-color-primary-lighter: #4250af;
|
||||
--ifm-color-primary-lightest: #4250af;
|
||||
--ifm-code-font-size: 95%;
|
||||
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
@ -40,10 +40,28 @@ img {
|
||||
--ifm-color-primary-light: #d5e4fc;
|
||||
--ifm-color-primary-lighter: #e9f1fe;
|
||||
--ifm-color-primary-lightest: #ffffff;
|
||||
--ifm-background-color: #000000;
|
||||
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
|
||||
--ifm-background-color: #000000;
|
||||
}
|
||||
|
||||
div[class^='announcementBar_'] {
|
||||
min-height: 2rem;
|
||||
background-color: #2b3336;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.menu__link {
|
||||
padding: 10px;
|
||||
padding-left: 16px;
|
||||
border-radius: 10px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.menu__list-item-collapsible {
|
||||
border-radius: 10px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
code {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
@ -1,13 +1,74 @@
|
||||
import { mdiCalendarToday, mdiLeadPencil, mdiLockOutline, mdiSpeedometerSlow, mdiWeb } from '@mdi/js';
|
||||
import {
|
||||
mdiBug,
|
||||
mdiCalendarToday,
|
||||
mdiCrosshairsOff,
|
||||
mdiDatabase,
|
||||
mdiLeadPencil,
|
||||
mdiLockOff,
|
||||
mdiLockOutline,
|
||||
mdiMicrosoftWindows,
|
||||
mdiSecurity,
|
||||
mdiSpeedometerSlow,
|
||||
mdiTrashCan,
|
||||
mdiWeb,
|
||||
mdiWrap,
|
||||
} from '@mdi/js';
|
||||
import Layout from '@theme/Layout';
|
||||
import React from 'react';
|
||||
import { Item as TimelineItem, Timeline } from '../components/timeline';
|
||||
import { Timeline, Item as TimelineItem } from '../components/timeline';
|
||||
|
||||
const withLanguage = (date: Date) => (language: string) => date.toLocaleDateString(language);
|
||||
|
||||
type Item = Omit<TimelineItem, 'done' | 'getDateLabel'> & { date: Date };
|
||||
|
||||
const items: Item[] = [
|
||||
{
|
||||
icon: mdiMicrosoftWindows,
|
||||
iconColor: '#357EC7',
|
||||
title: 'Hidden files in Windows are cursed',
|
||||
description:
|
||||
'Hidden files in Windows cannot be opened with the "w" flag. That, combined with SMB option "hide dot files" leads to a lot of confusion.',
|
||||
link: {
|
||||
url: 'https://github.com/immich-app/immich/pull/12812',
|
||||
text: '#12812',
|
||||
},
|
||||
date: new Date(2024, 8, 20),
|
||||
},
|
||||
{
|
||||
icon: mdiWrap,
|
||||
iconColor: 'gray',
|
||||
title: 'Carriage returns in bash scripts are cursed',
|
||||
description: 'Git can be configured to automatically convert LF to CRLF on checkout and CRLF breaks bash scripts.',
|
||||
link: {
|
||||
url: 'https://github.com/immich-app/immich/pull/11613',
|
||||
text: '#11613',
|
||||
},
|
||||
date: new Date(2024, 7, 7),
|
||||
},
|
||||
{
|
||||
icon: mdiLockOff,
|
||||
iconColor: 'red',
|
||||
title: 'Fetch inside Cloudflare Workers is cursed',
|
||||
description:
|
||||
'Fetch requests in Cloudflare Workers use http by default, even if you explicitly specify https, which can often cause redirect loops.',
|
||||
link: {
|
||||
url: 'https://community.cloudflare.com/t/does-cloudflare-worker-allow-secure-https-connection-to-fetch-even-on-flexible-ssl/68051/5',
|
||||
text: 'Cloudflare',
|
||||
},
|
||||
date: new Date(2024, 7, 7),
|
||||
},
|
||||
{
|
||||
icon: mdiCrosshairsOff,
|
||||
iconColor: 'gray',
|
||||
title: 'GPS sharing on mobile is cursed',
|
||||
description:
|
||||
'Some phones will silently strip GPS data from images when apps without location permission try to access them.',
|
||||
link: {
|
||||
url: 'https://github.com/immich-app/immich/discussions/11268',
|
||||
text: '#11268',
|
||||
},
|
||||
date: new Date(2024, 6, 21),
|
||||
},
|
||||
{
|
||||
icon: mdiLeadPencil,
|
||||
iconColor: 'gold',
|
||||
@ -52,6 +113,51 @@ const items: Item[] = [
|
||||
link: { url: 'https://github.com/immich-app/immich/pull/6787', text: '#6787' },
|
||||
date: new Date(2024, 0, 31),
|
||||
},
|
||||
{
|
||||
icon: mdiBug,
|
||||
iconColor: 'green',
|
||||
title: 'ESM imports are cursed',
|
||||
description:
|
||||
'Prior to Node.js v20.8 using --experimental-vm-modules in a CommonJS project that imported an ES module that imported a CommonJS modules would create a segfault and crash Node.js',
|
||||
link: {
|
||||
url: 'https://github.com/immich-app/immich/pull/6719',
|
||||
text: '#6179',
|
||||
},
|
||||
date: new Date(2024, 0, 9),
|
||||
},
|
||||
{
|
||||
icon: mdiDatabase,
|
||||
iconColor: 'gray',
|
||||
title: 'PostgreSQL parameters are cursed',
|
||||
description: `PostgresSQL has a limit of ${Number(65535).toLocaleString()} parameters, so bulk inserts can fail with large datasets.`,
|
||||
link: {
|
||||
url: 'https://github.com/immich-app/immich/pull/6034',
|
||||
text: '#6034',
|
||||
},
|
||||
date: new Date(2023, 11, 28),
|
||||
},
|
||||
{
|
||||
icon: mdiSecurity,
|
||||
iconColor: 'gold',
|
||||
title: 'Secure contexts are cursed',
|
||||
description: `Some web features like the clipboard API only work in "secure contexts" (ie. https or localhost)`,
|
||||
link: {
|
||||
url: 'https://github.com/immich-app/immich/issues/2981',
|
||||
text: '#2981',
|
||||
},
|
||||
date: new Date(2023, 5, 26),
|
||||
},
|
||||
{
|
||||
icon: mdiTrashCan,
|
||||
iconColor: 'gray',
|
||||
title: 'TypeORM deletes are cursed',
|
||||
description: `The remove implementation in TypeORM mutates the input, deleting the id property from the original object.`,
|
||||
link: {
|
||||
url: 'https://github.com/typeorm/typeorm/issues/7024#issuecomment-948519328',
|
||||
text: 'typeorm#6034',
|
||||
},
|
||||
date: new Date(2023, 1, 23),
|
||||
},
|
||||
];
|
||||
|
||||
export default function CursedKnowledgePage(): JSX.Element {
|
||||
|