Compare commits

..

5 Commits

Author SHA1 Message Date
bwees
97329d488b chore: code review changes from @shenlong-tanwen 2025-06-24 09:18:11 -05:00
bwees
c6cd4dce78 fix: include port in baseURL 2025-06-24 09:17:42 -05:00
bwees
0a90f490f2 cleanly migrate the server URL value to a JSON array (ie coming from 1.135.0 app) 2025-06-23 17:55:41 -05:00
bwees
88e008c4c2 fix: remove duplicate endpoints 2025-06-23 17:44:51 -05:00
bwees
99a9914da2 feat: ios widget supports alternate server URLs 2025-06-23 17:41:55 -05:00
2122 changed files with 112667 additions and 182844 deletions

View File

@@ -5,8 +5,7 @@
"immich-server", "immich-server",
"redis", "redis",
"database", "database",
"immich-machine-learning", "immich-machine-learning"
"init"
], ],
"dockerComposeFile": [ "dockerComposeFile": [
"../docker/docker-compose.dev.yml", "../docker/docker-compose.dev.yml",

View File

@@ -11,8 +11,8 @@ services:
- open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules - open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules
- server_node_modules:/workspaces/immich/server/node_modules - server_node_modules:/workspaces/immich/server/node_modules
- web_node_modules:/workspaces/immich/web/node_modules - web_node_modules:/workspaces/immich/web/node_modules
- ${UPLOAD_LOCATION}/photos:/data - ${UPLOAD_LOCATION}/photos:/workspaces/immich/server/upload
- ${UPLOAD_LOCATION}/photos/upload:/data/upload - ${UPLOAD_LOCATION}/photos/upload:/workspaces/immich/server/upload/upload
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
database: database:

View File

@@ -49,11 +49,10 @@ fix_permissions() {
log "Fixing permissions for ${IMMICH_WORKSPACE}" log "Fixing permissions for ${IMMICH_WORKSPACE}"
run_cmd sudo find "${IMMICH_WORKSPACE}/server/upload" -not -path "${IMMICH_WORKSPACE}/server/upload/postgres/*" -not -path "${IMMICH_WORKSPACE}/server/upload/postgres" -exec chown node {} +
# Change ownership for directories that exist # Change ownership for directories that exist
for dir in "${IMMICH_WORKSPACE}/.vscode" \ for dir in "${IMMICH_WORKSPACE}/.vscode" \
"${IMMICH_WORKSPACE}/server/upload" \
"${IMMICH_WORKSPACE}/.pnpm-store" \
"${IMMICH_WORKSPACE}/.github/node_modules" \
"${IMMICH_WORKSPACE}/cli/node_modules" \ "${IMMICH_WORKSPACE}/cli/node_modules" \
"${IMMICH_WORKSPACE}/e2e/node_modules" \ "${IMMICH_WORKSPACE}/e2e/node_modules" \
"${IMMICH_WORKSPACE}/open-api/typescript-sdk/node_modules" \ "${IMMICH_WORKSPACE}/open-api/typescript-sdk/node_modules" \
@@ -74,8 +73,10 @@ install_dependencies() {
log "Installing dependencies" log "Installing dependencies"
( (
cd "${IMMICH_WORKSPACE}" || exit 1 cd "${IMMICH_WORKSPACE}" || exit 1
export CI=1 FROZEN=1 OFFLINE=1 run_cmd make install-server
run_cmd make setup-web-dev setup-server-dev run_cmd make install-sdk
run_cmd make build-sdk
run_cmd make install-web
) )
log "" log ""
} }

View File

@@ -3,32 +3,25 @@ services:
build: build:
target: dev-container-server target: dev-container-server
env_file: !reset [] env_file: !reset []
hostname: immich-dev
environment: environment:
- IMMICH_SERVER_URL=http://127.0.0.1:2283/ - IMMICH_SERVER_URL=http://127.0.0.1:2283/
volumes: !override volumes: !override
- ..:/workspaces/immich - ..:/workspaces/immich
- ${UPLOAD_LOCATION:-upload1-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/data - cli_node_modules:/workspaces/immich/cli/node_modules
- ${UPLOAD_LOCATION:-upload2-devcontainer-volume}${UPLOAD_LOCATION:+/photos/upload}:/data/upload - e2e_node_modules:/workspaces/immich/e2e/node_modules
- open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules
- server_node_modules:/workspaces/immich/server/node_modules
- web_node_modules:/workspaces/immich/web/node_modules
- ${UPLOAD_LOCATION:-upload1-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/workspaces/immich/server/upload
- ${UPLOAD_LOCATION:-upload2-devcontainer-volume}${UPLOAD_LOCATION:+/photos/upload}:/workspaces/immich/server/upload/upload
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
- pnpm-store:/usr/src/app/.pnpm-store
- server-node_modules:/usr/src/app/server/node_modules
- web-node_modules:/usr/src/app/web/node_modules
- github-node_modules:/usr/src/app/.github/node_modules
- cli-node_modules:/usr/src/app/cli/node_modules
- docs-node_modules:/usr/src/app/docs/node_modules
- e2e-node_modules:/usr/src/app/e2e/node_modules
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
- app-node_modules:/usr/src/app/node_modules
- sveltekit:/usr/src/app/web/.svelte-kit
- coverage:/usr/src/app/web/coverage
immich-web: immich-web:
env_file: !reset [] env_file: !reset []
init:
env_file: !reset []
command: sh -c 'for path in /data /data/upload /usr/src/app/.pnpm-store /usr/src/app/server/node_modules /usr/src/app/server/dist /usr/src/app/.github/node_modules /usr/src/app/cli/node_modules /usr/src/app/docs/node_modules /usr/src/app/e2e/node_modules /usr/src/app/open-api/typescript-sdk/node_modules /usr/src/app/web/.svelte-kit /usr/src/app/web/coverage /usr/src/app/node_modules /usr/src/app/web/node_modules; do [ -e "$$path" ] && chown -R ${UID:-1000}:${GID:-1000} "$$path" || true; done'
immich-machine-learning: immich-machine-learning:
env_file: !reset [] env_file: !reset []
database: database:
env_file: !reset [] env_file: !reset []
environment: !override environment: !override
@@ -37,12 +30,19 @@ services:
POSTGRES_DB: ${DB_DATABASE_NAME-immich} POSTGRES_DB: ${DB_DATABASE_NAME-immich}
POSTGRES_INITDB_ARGS: '--data-checksums' POSTGRES_INITDB_ARGS: '--data-checksums'
POSTGRES_HOST_AUTH_METHOD: md5 POSTGRES_HOST_AUTH_METHOD: md5
volumes: volumes:
- ${UPLOAD_LOCATION:-postgres-devcontainer-volume}${UPLOAD_LOCATION:+/postgres}:/var/lib/postgresql/data - ${UPLOAD_LOCATION:-postgres-devcontainer-volume}${UPLOAD_LOCATION:+/postgres}:/var/lib/postgresql/data
redis: redis:
env_file: !reset [] env_file: !reset []
volumes: volumes:
# Node modules for each service to avoid conflicts and ensure consistent dependencies # Node modules for each service to avoid conflicts and ensure consistent dependencies
cli_node_modules:
e2e_node_modules:
open_api_node_modules:
server_node_modules:
web_node_modules:
upload1-devcontainer-volume: upload1-devcontainer-volume:
upload2-devcontainer-volume: upload2-devcontainer-volume:
postgres-devcontainer-volume: postgres-devcontainer-volume:

View File

@@ -3,20 +3,15 @@
# shellcheck disable=SC1091 # shellcheck disable=SC1091
source /immich-devcontainer/container-common.sh source /immich-devcontainer/container-common.sh
log "Preparing Immich Nest API Server"
log ""
export CI=1
run_cmd pnpm --filter immich install
log "Starting Nest API Server" log "Starting Nest API Server"
log "" log ""
cd "${IMMICH_WORKSPACE}/server" || ( cd "${IMMICH_WORKSPACE}/server" || (
log "Immich workspace not found"jj log "Immich workspace not found"
exit 1 exit 1
) )
while true; do while true; do
run_cmd pnpm --filter immich exec nest start --debug "0.0.0.0:9230" --watch run_cmd node ./node_modules/.bin/nest start --debug "0.0.0.0:9230" --watch
log "Nest API Server crashed with exit code $?. Respawning in 3s ..." log "Nest API Server crashed with exit code $?. Respawning in 3s ..."
sleep 3 sleep 3
done done

View File

@@ -3,13 +3,6 @@
# shellcheck disable=SC1091 # shellcheck disable=SC1091
source /immich-devcontainer/container-common.sh source /immich-devcontainer/container-common.sh
export CI=1
log "Preparing Immich Web Frontend"
log ""
run_cmd pnpm --filter @immich/sdk install
run_cmd pnpm --filter @immich/sdk build
run_cmd pnpm --filter immich-web install
log "Starting Immich Web Frontend" log "Starting Immich Web Frontend"
log "" log ""
cd "${IMMICH_WORKSPACE}/web" || ( cd "${IMMICH_WORKSPACE}/web" || (
@@ -23,7 +16,7 @@ until curl --output /dev/null --silent --head --fail "http://127.0.0.1:${IMMICH_
done done
while true; do while true; do
run_cmd pnpm --filter immich-web exec vite dev --host 0.0.0.0 --port "${DEV_PORT}" run_cmd node ./node_modules/.bin/vite dev --host 0.0.0.0 --port "${DEV_PORT}"
log "Web crashed with exit code $?. Respawning in 3s ..." log "Web crashed with exit code $?. Respawning in 3s ..."
sleep 3 sleep 3
done done

View File

@@ -6,6 +6,9 @@ source /immich-devcontainer/container-common.sh
log "Setting up Immich dev container..." log "Setting up Immich dev container..."
fix_permissions fix_permissions
log "Installing npm dependencies (node_modules)..."
install_dependencies
log "Setup complete, please wait while backend and frontend services automatically start" log "Setup complete, please wait while backend and frontend services automatically start"
log log
log "If necessary, the services may be manually started using" log "If necessary, the services may be manually started using"

View File

@@ -1,41 +1,33 @@
.vscode/ .vscode/
.github/ .github/
.git/ .git/
.env*
*.log
*.tmp
*.temp
**/Dockerfile
**/node_modules/
**/.pnpm-store/
**/dist/
**/coverage/
**/build/
design/ design/
docker/ docker/
!docker/scripts !docker/scripts
docs/ docs/
!docs/package.json
!docs/package-lock.json
e2e/ e2e/
!e2e/package.json
!e2e/package-lock.json
fastlane/ fastlane/
machine-learning/ machine-learning/
misc/ misc/
mobile/ mobile/
open-api/typescript-sdk/build/ cli/coverage/
!open-api/typescript-sdk/package.json cli/dist/
!open-api/typescript-sdk/package-lock.json cli/node_modules/
open-api/typescript-sdk/build/
open-api/typescript-sdk/node_modules/
server/coverage/
server/node_modules/
server/upload/ server/upload/
server/src/queries server/src/queries
server/dist/
server/www/ server/www/
web/node_modules/
web/coverage/
web/.svelte-kit web/.svelte-kit
web/build/
web/.env

6
.gitattributes vendored
View File

@@ -12,12 +12,6 @@ mobile/lib/**/*.drift.dart linguist-generated=true
mobile/drift_schemas/main/drift_schema_*.json -diff -merge mobile/drift_schemas/main/drift_schema_*.json -diff -merge
mobile/drift_schemas/main/drift_schema_*.json linguist-generated=true mobile/drift_schemas/main/drift_schema_*.json linguist-generated=true
mobile/lib/infrastructure/repositories/db.repository.steps.dart -diff -merge
mobile/lib/infrastructure/repositories/db.repository.steps.dart linguist-generated=true
mobile/test/drift/main/generated/** -diff -merge
mobile/test/drift/main/generated/** linguist-generated=true
open-api/typescript-sdk/fetch-client.ts -diff -merge open-api/typescript-sdk/fetch-client.ts -diff -merge
open-api/typescript-sdk/fetch-client.ts linguist-generated=true open-api/typescript-sdk/fetch-client.ts linguist-generated=true

2
.github/.nvmrc vendored
View File

@@ -1 +1 @@
22.18.0 22.16.0

View File

@@ -1,4 +0,0 @@
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

View File

@@ -64,11 +64,6 @@ body:
- label: Web - label: Web
- label: Mobile - label: Mobile
- type: input
attributes:
label: Device make and model
placeholder: Samsung S25 Android 16
- type: textarea - type: textarea
validations: validations:
required: true required: true

28
.github/package-lock.json generated vendored Normal file
View File

@@ -0,0 +1,28 @@
{
"name": ".github",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"devDependencies": {
"prettier": "^3.5.3"
}
},
"node_modules/prettier": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
"dev": true,
"license": "MIT",
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
}
}
}

View File

@@ -35,7 +35,7 @@ jobs:
should_run: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }} should_run: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }}
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
@@ -58,55 +58,39 @@ jobs:
contents: read contents: read
# Skip when PR from a fork # Skip when PR from a fork
if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' && needs.pre-job.outputs.should_run == 'true' }} if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' && needs.pre-job.outputs.should_run == 'true' }}
runs-on: mich runs-on: macos-14
steps: steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
ref: ${{ inputs.ref || github.sha }} ref: ${{ inputs.ref || github.sha }}
persist-credentials: false persist-credentials: false
- name: Create the Keystore
env:
KEY_JKS: ${{ secrets.KEY_JKS }}
working-directory: ./mobile
run: printf "%s" $KEY_JKS | base64 -d > android/key.jks
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
with: with:
distribution: 'zulu' distribution: 'zulu'
java-version: '17' java-version: '17'
cache: 'gradle'
- name: Restore Gradle Cache
id: cache-gradle-restore
uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
~/.android/sdk
mobile/android/.gradle
mobile/.dart_tool
key: build-mobile-gradle-${{ runner.os }}-main
- name: Setup Flutter SDK - name: Setup Flutter SDK
uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # v2.21.0 uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0
with: with:
channel: 'stable' channel: 'stable'
flutter-version-file: ./mobile/pubspec.yaml flutter-version-file: ./mobile/pubspec.yaml
cache: true cache: true
- name: Setup Android SDK - name: Create the Keystore
uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3.2.2 env:
with: KEY_JKS: ${{ secrets.KEY_JKS }}
packages: '' working-directory: ./mobile
run: echo $KEY_JKS | base64 -d > android/key.jks
- name: Get Packages - name: Get Packages
working-directory: ./mobile working-directory: ./mobile
run: flutter pub get run: flutter pub get
- name: Generate translation file - name: Generate translation file
run: dart run easy_localization:generate -S ../i18n && dart run bin/generate_keys.dart run: make translation
working-directory: ./mobile working-directory: ./mobile
- name: Generate platform APIs - name: Generate platform APIs
@@ -119,30 +103,12 @@ jobs:
ALIAS: ${{ secrets.ALIAS }} ALIAS: ${{ secrets.ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }} ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
IS_MAIN: ${{ github.ref == 'refs/heads/main' }}
run: | run: |
if [[ $IS_MAIN == 'true' ]]; then flutter build apk --release
flutter build apk --release flutter build apk --release --split-per-abi --target-platform android-arm,android-arm64,android-x64
flutter build apk --release --split-per-abi --target-platform android-arm,android-arm64,android-x64
else
flutter build apk --debug --split-per-abi --target-platform android-arm64
fi
- name: Publish Android Artifact - name: Publish Android Artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with: with:
name: release-apk-signed name: release-apk-signed
path: mobile/build/app/outputs/flutter-apk/*.apk path: mobile/build/app/outputs/flutter-apk/*.apk
- name: Save Gradle Cache
id: cache-gradle-save
uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4
if: github.ref == 'refs/heads/main'
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
~/.android/sdk
mobile/android/.gradle
mobile/.dart_tool
key: ${{ steps.cache-gradle-restore.outputs.cache-primary-key }}

View File

@@ -19,7 +19,7 @@ jobs:
actions: write actions: write
steps: steps:
- name: Check out code - name: Check out code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false

View File

@@ -29,28 +29,22 @@ jobs:
working-directory: ./cli working-directory: ./cli
steps: steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup pnpm # Setup .npmrc file to publish to npm
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
- name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with: with:
node-version-file: './cli/.nvmrc' node-version-file: './cli/.nvmrc'
registry-url: 'https://registry.npmjs.org' registry-url: 'https://registry.npmjs.org'
cache: 'pnpm' - name: Prepare SDK
cache-dependency-path: '**/pnpm-lock.yaml' run: npm ci --prefix ../open-api/typescript-sdk/
- name: Build SDK
- name: Setup typescript-sdk run: npm run build --prefix ../open-api/typescript-sdk/
run: pnpm install && pnpm run build - run: npm ci
working-directory: ./open-api/typescript-sdk - run: npm run build
- run: npm publish
- run: pnpm install --frozen-lockfile
- run: pnpm build
- run: pnpm publish
if: ${{ github.event_name == 'release' }} if: ${{ github.event_name == 'release' }}
env: env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -65,7 +59,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
@@ -73,10 +67,10 @@ jobs:
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
if: ${{ !github.event.pull_request.head.repo.fork }} if: ${{ !github.event.pull_request.head.repo.fork }}
with: with:
registry: ghcr.io registry: ghcr.io
@@ -91,7 +85,7 @@ jobs:
- name: Generate docker image tags - name: Generate docker image tags
id: metadata id: metadata
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0 uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
with: with:
flavor: | flavor: |
latest=false latest=false
@@ -102,7 +96,7 @@ jobs:
type=raw,value=latest,enable=${{ github.event_name == 'release' }} type=raw,value=latest,enable=${{ github.event_name == 'release' }}
- name: Build and push image - name: Build and push image
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0
with: with:
file: cli/Dockerfile file: cli/Dockerfile
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64

View File

@@ -1,107 +0,0 @@
on:
issues:
types: [opened]
discussion:
types: [created]
name: Close likely duplicates
permissions: {}
jobs:
should_run:
runs-on: ubuntu-latest
outputs:
should_run: ${{ steps.should_run.outputs.run }}
steps:
- id: should_run
run: echo "run=${{ github.event_name == 'issues' || github.event.discussion.category.name == 'Feature Request' }}" >> $GITHUB_OUTPUT
get_body:
runs-on: ubuntu-latest
needs: should_run
if: ${{ needs.should_run.outputs.should_run == 'true' }}
env:
EVENT: ${{ toJSON(github.event) }}
outputs:
body: ${{ steps.get_body.outputs.body }}
steps:
- id: get_body
run: |
BODY=$(echo """$EVENT""" | jq -r '.issue // .discussion | .body' | base64 -w 0)
echo "body=$BODY" >> $GITHUB_OUTPUT
get_checkbox_json:
runs-on: ubuntu-latest
needs: [get_body, should_run]
if: ${{ needs.should_run.outputs.should_run == 'true' }}
container:
image: yshavit/mdq:0.8.0@sha256:c69224d34224a0043d9a3ee46679ba4a2a25afaac445f293d92afe13cd47fcea
outputs:
json: ${{ steps.get_checkbox.outputs.json }}
steps:
- id: get_checkbox
env:
BODY: ${{ needs.get_body.outputs.body }}
run: |
JSON=$(echo "$BODY" | base64 -d | /mdq --output json '# I have searched | - [?] Yes')
echo "json=$JSON" >> $GITHUB_OUTPUT
close_and_comment:
runs-on: ubuntu-latest
needs: [get_checkbox_json, should_run]
if: ${{ needs.should_run.outputs.should_run == 'true' && !fromJSON(needs.get_checkbox_json.outputs.json).items[0].list[0].checked }}
permissions:
issues: write
discussions: write
steps:
- name: Close issue
if: ${{ github.event_name == 'issues' }}
env:
GH_TOKEN: ${{ github.token }}
NODE_ID: ${{ github.event.issue.node_id }}
run: |
gh api graphql \
-f issueId="$NODE_ID" \
-f body="This issue has automatically been closed as it is likely a duplicate. We get a lot of duplicate threads each day, which is why we ask you in the template to confirm that you searched for duplicates before opening one. If you're sure this is not a duplicate, please leave a comment and we will reopen the thread if necessary." \
-f query='
mutation CommentAndCloseIssue($issueId: ID!, $body: String!) {
addComment(input: {
subjectId: $issueId,
body: $body
}) {
__typename
}
closeIssue(input: {
issueId: $issueId,
stateReason: DUPLICATE
}) {
__typename
}
}'
- name: Close discussion
if: ${{ github.event_name == 'discussion' && github.event.discussion.category.name == 'Feature Request' }}
env:
GH_TOKEN: ${{ github.token }}
NODE_ID: ${{ github.event.discussion.node_id }}
run: |
gh api graphql \
-f discussionId="$NODE_ID" \
-f body="This discussion has automatically been closed as it is likely a duplicate. We get a lot of duplicate threads each day, which is why we ask you in the template to confirm that you searched for duplicates before opening one. If you're sure this is not a duplicate, please leave a comment and we will reopen the thread if necessary." \
-f query='
mutation CommentAndCloseDiscussion($discussionId: ID!, $body: String!) {
addDiscussionComment(input: {
discussionId: $discussionId,
body: $body
}) {
__typename
}
closeDiscussion(input: {
discussionId: $discussionId,
reason: DUPLICATE
}) {
__typename
}
}'

View File

@@ -44,13 +44,13 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.9 uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file. # If you wish to specify custom queries, you can do so here or in a config file.
@@ -63,7 +63,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below) # If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.9 uses: github/codeql-action/autobuild@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
# Command-line programs to run using the OS shell. # Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -76,6 +76,6 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh # ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.9 uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
with: with:
category: '/language:${{matrix.language}}' category: '/language:${{matrix.language}}'

View File

@@ -24,7 +24,7 @@ jobs:
should_run_ml: ${{ steps.found_paths.outputs.machine-learning == '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: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- id: found_paths - id: found_paths
@@ -60,7 +60,7 @@ jobs:
suffix: ['', '-cuda', '-rocm', '-openvino', '-armnn', '-rknn'] suffix: ['', '-cuda', '-rocm', '-openvino', '-armnn', '-rknn']
steps: steps:
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
@@ -89,7 +89,7 @@ jobs:
suffix: [''] suffix: ['']
steps: steps:
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
@@ -131,7 +131,7 @@ jobs:
tag-suffix: '-rocm' tag-suffix: '-rocm'
platforms: linux/amd64 platforms: linux/amd64
runner-mapping: '{"linux/amd64": "mich"}' runner-mapping: '{"linux/amd64": "mich"}'
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@129aeda75a450666ce96e8bc8126652e717917a7 # multi-runner-build-workflow-0.1.1 uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@094bfb927b8cd75b343abaac27b3241be0fccfe9 # multi-runner-build-workflow-0.1.0
permissions: permissions:
contents: read contents: read
actions: read actions: read
@@ -154,7 +154,7 @@ jobs:
name: Build and Push Server name: Build and Push Server
needs: pre-job needs: pre-job
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }} if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@129aeda75a450666ce96e8bc8126652e717917a7 # multi-runner-build-workflow-0.1.1 uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@094bfb927b8cd75b343abaac27b3241be0fccfe9 # multi-runner-build-workflow-0.1.0
permissions: permissions:
contents: read contents: read
actions: read actions: read
@@ -177,7 +177,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: always() if: always()
steps: steps:
- uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4 - uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
with: with:
needs: ${{ toJSON(needs) }} needs: ${{ toJSON(needs) }}
@@ -188,6 +188,6 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: always() if: always()
steps: steps:
- uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4 - uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
with: with:
needs: ${{ toJSON(needs) }} needs: ${{ toJSON(needs) }}

View File

@@ -18,10 +18,10 @@ jobs:
permissions: permissions:
contents: read contents: read
outputs: outputs:
should_run: ${{ steps.found_paths.outputs.docs == 'true' || steps.found_paths.outputs.open-api == 'true' || steps.should_force.outputs.should_force == 'true' }} should_run: ${{ steps.found_paths.outputs.docs == 'true' || steps.should_force.outputs.should_force == 'true' }}
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- id: found_paths - id: found_paths
@@ -32,8 +32,6 @@ jobs:
- 'docs/**' - 'docs/**'
workflow: workflow:
- '.github/workflows/docs-build.yml' - '.github/workflows/docs-build.yml'
open-api:
- 'open-api/immich-openapi-specs.json'
- name: Check if we should force jobs to run - name: Check if we should force jobs to run
id: should_force id: should_force
run: echo "should_force=${{ steps.found_paths.outputs.workflow == 'true' || github.event_name == 'release' || github.ref_name == 'main' }}" >> "$GITHUB_OUTPUT" run: echo "should_force=${{ steps.found_paths.outputs.workflow == 'true' || github.event_name == 'release' || github.ref_name == 'main' }}" >> "$GITHUB_OUTPUT"
@@ -51,28 +49,23 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup pnpm
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
- name: Setup Node - name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with: with:
node-version-file: './docs/.nvmrc' node-version-file: './docs/.nvmrc'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- name: Run install - name: Run npm install
run: pnpm install run: npm ci
- name: Check formatting - name: Check formatting
run: pnpm format run: npm run format
- name: Run build - name: Run build
run: pnpm build run: npm run build
- name: Upload build output - name: Upload build output
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2

View File

@@ -108,7 +108,7 @@ jobs:
if: ${{ fromJson(needs.checks.outputs.artifact).found && fromJson(needs.checks.outputs.parameters).shouldDeploy }} if: ${{ fromJson(needs.checks.outputs.artifact).found && fromJson(needs.checks.outputs.parameters).shouldDeploy }}
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false

View File

@@ -14,7 +14,7 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false

View File

@@ -16,13 +16,13 @@ jobs:
steps: steps:
- name: Generate a token - name: Generate a token
id: generate-token id: generate-token
uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
with: with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }} app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: 'Checkout' - name: 'Checkout'
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
ref: ${{ github.event.pull_request.head.ref }} ref: ${{ github.event.pull_request.head.ref }}
token: ${{ steps.generate-token.outputs.token }} token: ${{ steps.generate-token.outputs.token }}
@@ -32,8 +32,6 @@ jobs:
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with: with:
node-version-file: './server/.nvmrc' node-version-file: './server/.nvmrc'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- name: Fix formatting - name: Fix formatting
run: make install-all && make format-all run: make install-all && make format-all

View File

@@ -1,13 +0,0 @@
name: Org Checks
on:
pull_request_review:
pull_request:
jobs:
check-approvals:
name: Check for Team/Admin Review
uses: immich-app/devtools/.github/workflows/required-approval.yml@main
permissions:
pull-requests: read
contents: read

View File

@@ -14,7 +14,7 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- name: Require PR to have a changelog label - name: Require PR to have a changelog label
uses: mheap/github-action-required-labels@8afbe8ae6ab7647d0c9f0cfa7c2f939650d22509 # v5.5.1 uses: mheap/github-action-required-labels@fb29a14a076b0f74099f6198f77750e8fc236016 # v5.5.0
with: with:
mode: exactly mode: exactly
count: 1 count: 1

View File

@@ -32,13 +32,13 @@ jobs:
steps: steps:
- name: Generate a token - name: Generate a token
id: generate-token id: generate-token
uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
with: with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }} app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout - name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
token: ${{ steps.generate-token.outputs.token }} token: ${{ steps.generate-token.outputs.token }}
persist-credentials: true persist-credentials: true
@@ -46,16 +46,6 @@ jobs:
- name: Install uv - name: Install uv
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
- name: Setup pnpm
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
- name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: './server/.nvmrc'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- name: Bump version - name: Bump version
env: env:
SERVER_BUMP: ${{ inputs.serverBump }} SERVER_BUMP: ${{ inputs.serverBump }}
@@ -93,13 +83,13 @@ jobs:
steps: steps:
- name: Generate a token - name: Generate a token
id: generate-token id: generate-token
uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
with: with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }} app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout - name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
token: ${{ steps.generate-token.outputs.token }} token: ${{ steps.generate-token.outputs.token }}
persist-credentials: false persist-credentials: false
@@ -110,7 +100,7 @@ jobs:
name: release-apk-signed name: release-apk-signed
- name: Create draft release - name: Create draft release
uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8 # v2.3.2 uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2
with: with:
draft: true draft: true
tag_name: ${{ env.IMMICH_VERSION }} tag_name: ${{ env.IMMICH_VERSION }}

View File

@@ -20,7 +20,7 @@ jobs:
remove-label: remove-label:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ (github.event.action == 'closed' || github.event.pull_request.head.repo.fork) && contains(github.event.pull_request.labels.*.name, 'preview') }} if: ${{ github.event.action == 'closed' && contains(github.event.pull_request.labels.*.name, 'preview') }}
permissions: permissions:
pull-requests: write pull-requests: write
steps: steps:
@@ -33,15 +33,3 @@ jobs:
repo: context.repo.repo, repo: context.repo.repo,
name: 'preview' name: 'preview'
}) })
- uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2.8.2
if: ${{ github.event.pull_request.head.repo.fork }}
with:
message-id: 'preview-status'
message: 'PRs from forks cannot have preview environments.'
- uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2.8.2
if: ${{ !github.event.pull_request.head.repo.fork }}
with:
message-id: 'preview-status'
message: 'Preview environment has been removed.'

View File

@@ -16,25 +16,20 @@ jobs:
run: run:
working-directory: ./open-api/typescript-sdk working-directory: ./open-api/typescript-sdk
steps: steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup pnpm
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
# Setup .npmrc file to publish to npm # Setup .npmrc file to publish to npm
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with: with:
node-version-file: './open-api/typescript-sdk/.nvmrc' node-version-file: './open-api/typescript-sdk/.nvmrc'
registry-url: 'https://registry.npmjs.org' registry-url: 'https://registry.npmjs.org'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- name: Install deps - name: Install deps
run: pnpm install --frozen-lockfile run: npm ci
- name: Build - name: Build
run: pnpm build run: npm run build
- name: Publish - name: Publish
run: pnpm publish run: npm publish
env: env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@@ -20,7 +20,7 @@ jobs:
should_run: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }} should_run: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }}
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- id: found_paths - id: found_paths
@@ -42,39 +42,41 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
contents: read contents: read
defaults:
run:
working-directory: ./mobile
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup Flutter SDK - name: Setup Flutter SDK
uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # v2.21.0 uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0
with: with:
channel: 'stable' channel: 'stable'
flutter-version-file: ./mobile/pubspec.yaml flutter-version-file: ./mobile/pubspec.yaml
- name: Install dependencies - name: Install dependencies
run: dart pub get run: dart pub get
working-directory: ./mobile
- name: Install DCM - name: Install DCM
uses: CQLabs/setup-dcm@8697ae0790c0852e964a6ef1d768d62a6675481a # v2.0.1 run: |
with: sudo apt-get update
github-token: ${{ secrets.GITHUB_TOKEN }} wget -qO- https://dcm.dev/pgp-key.public | sudo gpg --dearmor -o /usr/share/keyrings/dcm.gpg
version: auto echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list
working-directory: ./mobile sudo apt-get update
sudo apt-get install dcm
- name: Generate translation file - name: Generate translation file
run: dart run easy_localization:generate -S ../i18n && dart run bin/generate_keys.dart run: make translation
working-directory: ./mobile
- name: Run Build Runner - name: Run Build Runner
run: make build run: make build
working-directory: ./mobile
- name: Generate platform API - name: Generate platform API
run: make pigeon run: make pigeon
working-directory: ./mobile
- name: Find file changes - name: Find file changes
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4 uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
@@ -90,22 +92,25 @@ jobs:
env: env:
CHANGED_FILES: ${{ steps.verify-changed-files.outputs.changed_files }} CHANGED_FILES: ${{ steps.verify-changed-files.outputs.changed_files }}
run: | run: |
echo "ERROR: Generated files not up to date! Run 'make build' and 'make pigeon' inside the mobile directory" echo "ERROR: Generated files not up to date! Run make_build inside the mobile directory"
echo "Changed files: ${CHANGED_FILES}" echo "Changed files: ${CHANGED_FILES}"
exit 1 exit 1
- name: Run dart analyze - name: Run dart analyze
run: dart analyze --fatal-infos run: dart analyze --fatal-infos
working-directory: ./mobile
- name: Run dart format - name: Run dart format
run: make format run: dart format lib/ --set-exit-if-changed
working-directory: ./mobile
- name: Run dart custom_lint - name: Run dart custom_lint
run: dart run custom_lint run: dart run custom_lint
working-directory: ./mobile
# TODO: Use https://github.com/CQLabs/dcm-action
- name: Run DCM - name: Run DCM
run: dcm analyze lib --fatal-style --fatal-warnings run: dcm analyze lib
working-directory: ./mobile
zizmor: zizmor:
name: zizmor name: zizmor
@@ -116,7 +121,7 @@ jobs:
actions: read actions: read
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
@@ -129,7 +134,7 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload SARIF file - name: Upload SARIF file
uses: github/codeql-action/upload-sarif@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.9 uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
with: with:
sarif_file: results.sarif sarif_file: results.sarif
category: zizmor category: zizmor

View File

@@ -4,10 +4,13 @@ on:
pull_request: pull_request:
push: push:
branches: [main] branches: [main]
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true cancel-in-progress: true
permissions: {} permissions: {}
jobs: jobs:
pre-job: pre-job:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -26,9 +29,10 @@ jobs:
should_run_.github: ${{ steps.found_paths.outputs['.github'] == 'true' || steps.should_force.outputs.should_force == 'true' }} # redundant to have should_force but if someone changes the trigger then this won't have to be changed should_run_.github: ${{ steps.found_paths.outputs['.github'] == 'true' || steps.should_force.outputs.should_force == 'true' }} # redundant to have should_force but if someone changes the trigger then this won't have to be changed
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- id: found_paths - id: found_paths
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
with: with:
@@ -54,9 +58,11 @@ jobs:
- '.github/workflows/test.yml' - '.github/workflows/test.yml'
.github: .github:
- '.github/**' - '.github/**'
- name: Check if we should force jobs to run - name: Check if we should force jobs to run
id: should_force id: should_force
run: echo "should_force=${{ steps.found_paths.outputs.workflow == 'true' || github.event_name == 'workflow_dispatch' }}" >> "$GITHUB_OUTPUT" run: echo "should_force=${{ steps.found_paths.outputs.workflow == 'true' || github.event_name == 'workflow_dispatch' }}" >> "$GITHUB_OUTPUT"
server-unit-tests: server-unit-tests:
name: Test & Lint Server name: Test & Lint Server
needs: pre-job needs: pre-job
@@ -67,33 +73,37 @@ jobs:
defaults: defaults:
run: run:
working-directory: ./server working-directory: ./server
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup pnpm
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
- name: Setup Node - name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with: with:
node-version-file: './server/.nvmrc' node-version-file: './server/.nvmrc'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml' - name: Run npm install
- name: Run package manager install run: npm ci
run: pnpm install
- name: Run linter - name: Run linter
run: pnpm lint run: npm run lint
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Run formatter - name: Run formatter
run: pnpm format run: npm run format
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Run tsc - name: Run tsc
run: pnpm check run: npm run check
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Run small tests & coverage - name: Run small tests & coverage
run: pnpm test run: npm test
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
cli-unit-tests: cli-unit-tests:
name: Unit Test CLI name: Unit Test CLI
needs: pre-job needs: pre-job
@@ -104,36 +114,41 @@ jobs:
defaults: defaults:
run: run:
working-directory: ./cli working-directory: ./cli
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup pnpm
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
- name: Setup Node - name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with: with:
node-version-file: './cli/.nvmrc' node-version-file: './cli/.nvmrc'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- name: Setup typescript-sdk - name: Setup typescript-sdk
run: pnpm install && pnpm run build run: npm ci && npm run build
working-directory: ./open-api/typescript-sdk working-directory: ./open-api/typescript-sdk
- name: Install deps - name: Install deps
run: pnpm install run: npm ci
- name: Run linter - name: Run linter
run: pnpm lint run: npm run lint
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Run formatter - name: Run formatter
run: pnpm format run: npm run format
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Run tsc - name: Run tsc
run: pnpm check run: npm run check
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Run unit tests & coverage - name: Run unit tests & coverage
run: pnpm test run: npm run test
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
cli-unit-tests-win: cli-unit-tests-win:
name: Unit Test CLI (Windows) name: Unit Test CLI (Windows)
needs: pre-job needs: pre-job
@@ -144,31 +159,34 @@ jobs:
defaults: defaults:
run: run:
working-directory: ./cli working-directory: ./cli
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup pnpm
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
- name: Setup Node - name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with: with:
node-version-file: './cli/.nvmrc' node-version-file: './cli/.nvmrc'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- name: Setup typescript-sdk - name: Setup typescript-sdk
run: pnpm install --frozen-lockfile && pnpm build run: npm ci && npm run build
working-directory: ./open-api/typescript-sdk working-directory: ./open-api/typescript-sdk
- name: Install deps - name: Install deps
run: pnpm install --frozen-lockfile run: npm ci
# Skip linter & formatter in Windows test. # Skip linter & formatter in Windows test.
- name: Run tsc - name: Run tsc
run: pnpm check run: npm run check
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Run unit tests & coverage - name: Run unit tests & coverage
run: pnpm test run: npm run test
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
web-lint: web-lint:
name: Lint Web name: Lint Web
needs: pre-job needs: pre-job
@@ -179,33 +197,37 @@ jobs:
defaults: defaults:
run: run:
working-directory: ./web working-directory: ./web
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup pnpm
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
- name: Setup Node - name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with: with:
node-version-file: './web/.nvmrc' node-version-file: './web/.nvmrc'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- name: Run setup typescript-sdk - name: Run setup typescript-sdk
run: pnpm install --frozen-lockfile && pnpm build run: npm ci && npm run build
working-directory: ./open-api/typescript-sdk working-directory: ./open-api/typescript-sdk
- name: Run pnpm install
run: pnpm rebuild && pnpm install --frozen-lockfile - name: Run npm install
run: npm ci
- name: Run linter - name: Run linter
run: pnpm lint:p run: npm run lint:p
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Run formatter - name: Run formatter
run: pnpm format run: npm run format
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Run svelte checks - name: Run svelte checks
run: pnpm check:svelte run: npm run check:svelte
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
web-unit-tests: web-unit-tests:
name: Test Web name: Test Web
needs: pre-job needs: pre-job
@@ -216,30 +238,33 @@ jobs:
defaults: defaults:
run: run:
working-directory: ./web working-directory: ./web
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup pnpm
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
- name: Setup Node - name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with: with:
node-version-file: './web/.nvmrc' node-version-file: './web/.nvmrc'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- name: Run setup typescript-sdk - name: Run setup typescript-sdk
run: pnpm install --frozen-lockfile && pnpm build run: npm ci && npm run build
working-directory: ./open-api/typescript-sdk working-directory: ./open-api/typescript-sdk
- name: Run npm install - name: Run npm install
run: pnpm install --frozen-lockfile run: npm ci
- name: Run tsc - name: Run tsc
run: pnpm check:typescript run: npm run check:typescript
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Run unit tests & coverage - name: Run unit tests & coverage
run: pnpm test run: npm run test
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
i18n-tests: i18n-tests:
name: Test i18n name: Test i18n
needs: pre-job needs: pre-job
@@ -249,27 +274,28 @@ jobs:
contents: read contents: read
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup pnpm
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
- name: Setup Node - name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with: with:
node-version-file: './web/.nvmrc' node-version-file: './web/.nvmrc'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- name: Install dependencies - name: Install dependencies
run: pnpm --filter=immich-web install --frozen-lockfile run: npm --prefix=web ci
- name: Format - name: Format
run: pnpm --filter=immich-web format:i18n run: npm --prefix=web run format:i18n
- name: Find file changes - name: Find file changes
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4 uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
id: verify-changed-files id: verify-changed-files
with: with:
files: | files: |
i18n/** i18n/**
- name: Verify files have not changed - name: Verify files have not changed
if: steps.verify-changed-files.outputs.files_changed == 'true' if: steps.verify-changed-files.outputs.files_changed == 'true'
env: env:
@@ -278,6 +304,7 @@ jobs:
echo "ERROR: i18n files not up to date!" echo "ERROR: i18n files not up to date!"
echo "Changed files: ${CHANGED_FILES}" echo "Changed files: ${CHANGED_FILES}"
exit 1 exit 1
e2e-tests-lint: e2e-tests-lint:
name: End-to-End Lint name: End-to-End Lint
needs: pre-job needs: pre-job
@@ -288,35 +315,39 @@ jobs:
defaults: defaults:
run: run:
working-directory: ./e2e working-directory: ./e2e
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup pnpm
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
- name: Setup Node - name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with: with:
node-version-file: './e2e/.nvmrc' node-version-file: './e2e/.nvmrc'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- name: Run setup typescript-sdk - name: Run setup typescript-sdk
run: pnpm install --frozen-lockfile && pnpm build run: npm ci && npm run build
working-directory: ./open-api/typescript-sdk working-directory: ./open-api/typescript-sdk
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Install dependencies - name: Install dependencies
run: pnpm install --frozen-lockfile run: npm ci
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Run linter - name: Run linter
run: pnpm lint run: npm run lint
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Run formatter - name: Run formatter
run: pnpm format run: npm run format
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Run tsc - name: Run tsc
run: pnpm check run: npm run check
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
server-medium-tests: server-medium-tests:
name: Medium Tests (Server) name: Medium Tests (Server)
needs: pre-job needs: pre-job
@@ -327,24 +358,25 @@ jobs:
defaults: defaults:
run: run:
working-directory: ./server working-directory: ./server
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup pnpm
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
- name: Setup Node - name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with: with:
node-version-file: './server/.nvmrc' node-version-file: './server/.nvmrc'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml' - name: Run npm install
- name: Run pnpm install run: npm ci
run: SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm install --frozen-lockfile
- name: Run medium tests - name: Run medium tests
run: pnpm test:medium run: npm run test:medium
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
e2e-tests-server-cli: e2e-tests-server-cli:
name: End-to-End Tests (Server & CLI) name: End-to-End Tests (Server & CLI)
needs: pre-job needs: pre-job
@@ -358,41 +390,41 @@ jobs:
strategy: strategy:
matrix: matrix:
runner: [ubuntu-latest, ubuntu-24.04-arm] runner: [ubuntu-latest, ubuntu-24.04-arm]
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
submodules: 'recursive' submodules: 'recursive'
- name: Setup pnpm
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
- name: Setup Node - name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with: with:
node-version-file: './e2e/.nvmrc' node-version-file: './e2e/.nvmrc'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- name: Run setup typescript-sdk - name: Run setup typescript-sdk
run: pnpm install --frozen-lockfile && pnpm build run: npm ci && npm run build
working-directory: ./open-api/typescript-sdk working-directory: ./open-api/typescript-sdk
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Run setup web
run: pnpm install --frozen-lockfile && pnpm exec svelte-kit sync
working-directory: ./web
if: ${{ !cancelled() }}
- name: Run setup cli - name: Run setup cli
run: pnpm install --frozen-lockfile && pnpm build run: npm ci && npm run build
working-directory: ./cli working-directory: ./cli
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Install dependencies - name: Install dependencies
run: pnpm install --frozen-lockfile run: npm ci
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Docker build - name: Docker build
run: docker compose build run: docker compose build
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Run e2e tests (api & cli) - name: Run e2e tests (api & cli)
run: pnpm test run: npm run test
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
e2e-tests-web: e2e-tests-web:
name: End-to-End Tests (Web) name: End-to-End Tests (Web)
needs: pre-job needs: pre-job
@@ -406,36 +438,40 @@ jobs:
strategy: strategy:
matrix: matrix:
runner: [ubuntu-latest, ubuntu-24.04-arm] runner: [ubuntu-latest, ubuntu-24.04-arm]
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
submodules: 'recursive' submodules: 'recursive'
- name: Setup pnpm
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
- name: Setup Node - name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with: with:
node-version-file: './e2e/.nvmrc' node-version-file: './e2e/.nvmrc'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- name: Run setup typescript-sdk - name: Run setup typescript-sdk
run: pnpm install --frozen-lockfile && pnpm build run: npm ci && npm run build
working-directory: ./open-api/typescript-sdk working-directory: ./open-api/typescript-sdk
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Install dependencies - name: Install dependencies
run: pnpm install --frozen-lockfile run: npm ci
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Install Playwright Browsers - name: Install Playwright Browsers
run: npx playwright install chromium --only-shell run: npx playwright install chromium --only-shell
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Docker build - name: Docker build
run: docker compose build run: docker compose build
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
- name: Run e2e tests (web) - name: Run e2e tests (web)
run: npx playwright test run: npx playwright test
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
success-check-e2e: success-check-e2e:
name: End-to-End Tests Success name: End-to-End Tests Success
needs: [e2e-tests-server-cli, e2e-tests-web] needs: [e2e-tests-server-cli, e2e-tests-web]
@@ -443,9 +479,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: always() if: always()
steps: steps:
- uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4 - uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
with: with:
needs: ${{ toJSON(needs) }} needs: ${{ toJSON(needs) }}
mobile-unit-tests: mobile-unit-tests:
name: Unit Test Mobile name: Unit Test Mobile
needs: pre-job needs: pre-job
@@ -454,20 +491,24 @@ jobs:
permissions: permissions:
contents: read contents: read
steps: steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup Flutter SDK - name: Setup Flutter SDK
uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # v2.21.0 uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0
with: with:
channel: 'stable' channel: 'stable'
flutter-version-file: ./mobile/pubspec.yaml flutter-version-file: ./mobile/pubspec.yaml
- name: Generate translation file - name: Generate translation file
run: dart run easy_localization:generate -S ../i18n && dart run bin/generate_keys.dart run: make translation
working-directory: ./mobile working-directory: ./mobile
- name: Run tests - name: Run tests
working-directory: ./mobile working-directory: ./mobile
run: flutter test -j 1 run: flutter test -j 1
ml-unit-tests: ml-unit-tests:
name: Unit Test ML name: Unit Test ML
needs: pre-job needs: pre-job
@@ -479,9 +520,10 @@ jobs:
run: run:
working-directory: ./machine-learning working-directory: ./machine-learning
steps: steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- name: Install uv - name: Install uv
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
@@ -504,6 +546,7 @@ jobs:
- name: Run tests and coverage - name: Run tests and coverage
run: | run: |
uv run pytest --cov=immich_ml --cov-report term-missing uv run pytest --cov=immich_ml --cov-report term-missing
github-files-formatting: github-files-formatting:
name: .github Files Formatting name: .github Files Formatting
needs: pre-job needs: pre-job
@@ -514,38 +557,43 @@ jobs:
defaults: defaults:
run: run:
working-directory: ./.github working-directory: ./.github
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup pnpm
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
- name: Setup Node - name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with: with:
node-version-file: './.github/.nvmrc' node-version-file: './.github/.nvmrc'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml' - name: Run npm install
- name: Run pnpm install run: npm ci
run: pnpm install --frozen-lockfile
- name: Run formatter - name: Run formatter
run: pnpm format run: npm run format
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
shellcheck: shellcheck:
name: ShellCheck name: ShellCheck
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
contents: read contents: read
steps: steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- name: Run ShellCheck - name: Run ShellCheck
uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0 uses: ludeeus/action-shellcheck@master
with: with:
ignore_paths: >- ignore_paths: >-
**/open-api/** **/openapi** **/node_modules/** **/open-api/**
**/openapi**
**/node_modules/**
generated-api-up-to-date: generated-api-up-to-date:
name: OpenAPI Clients name: OpenAPI Clients
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -553,23 +601,24 @@ jobs:
contents: read contents: read
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup pnpm
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
- name: Setup Node - name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with: with:
node-version-file: './server/.nvmrc' node-version-file: './server/.nvmrc'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- name: Install server dependencies - name: Install server dependencies
run: SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter immich install --frozen-lockfile run: npm --prefix=server ci
- name: Build the app - name: Build the app
run: pnpm --filter immich build run: npm --prefix=server run build
- name: Run API generation - name: Run API generation
run: make open-api run: make open-api
- name: Find file changes - name: Find file changes
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4 uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
id: verify-changed-files id: verify-changed-files
@@ -578,6 +627,7 @@ jobs:
mobile/openapi mobile/openapi
open-api/typescript-sdk open-api/typescript-sdk
open-api/immich-openapi-specs.json open-api/immich-openapi-specs.json
- name: Verify files have not changed - name: Verify files have not changed
if: steps.verify-changed-files.outputs.files_changed == 'true' if: steps.verify-changed-files.outputs.files_changed == 'true'
env: env:
@@ -586,6 +636,7 @@ jobs:
echo "ERROR: Generated files not up to date!" echo "ERROR: Generated files not up to date!"
echo "Changed files: ${CHANGED_FILES}" echo "Changed files: ${CHANGED_FILES}"
exit 1 exit 1
sql-schema-up-to-date: sql-schema-up-to-date:
name: SQL Schema Checks name: SQL Schema Checks
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -593,42 +644,49 @@ jobs:
contents: read contents: read
services: services:
postgres: postgres:
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3@sha256:ec713143dca1a426eba2e03707c319e2ec3cc9d304ef767f777f8e297dee820c image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3
env: env:
POSTGRES_PASSWORD: postgres POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres POSTGRES_USER: postgres
POSTGRES_DB: immich POSTGRES_DB: immich
options: >- options: >-
--health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 --health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports: ports:
- 5432:5432 - 5432:5432
defaults: defaults:
run: run:
working-directory: ./server working-directory: ./server
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup pnpm
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
- name: Setup Node - name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with: with:
node-version-file: './server/.nvmrc' node-version-file: './server/.nvmrc'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- name: Install server dependencies - name: Install server dependencies
run: SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm install --frozen-lockfile run: npm ci
- name: Build the app - name: Build the app
run: pnpm build run: npm run build
- name: Run existing migrations - name: Run existing migrations
run: pnpm migrations:run run: npm run migrations:run
- name: Test npm run schema:reset command works - name: Test npm run schema:reset command works
run: pnpm schema:reset run: npm run schema:reset
- name: Generate new migrations - name: Generate new migrations
continue-on-error: true continue-on-error: true
run: pnpm migrations:generate src/TestMigration run: npm run migrations:generate src/TestMigration
- name: Find file changes - name: Find file changes
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4 uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
id: verify-changed-files id: verify-changed-files
@@ -644,16 +702,19 @@ jobs:
echo "Changed files: ${CHANGED_FILES}" echo "Changed files: ${CHANGED_FILES}"
cat ./src/*-TestMigration.ts cat ./src/*-TestMigration.ts
exit 1 exit 1
- name: Run SQL generation - name: Run SQL generation
run: pnpm sync:sql run: npm run sync:sql
env: env:
DB_URL: postgres://postgres:postgres@localhost:5432/immich DB_URL: postgres://postgres:postgres@localhost:5432/immich
- name: Find file changes - name: Find file changes
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4 uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
id: verify-changed-sql-files id: verify-changed-sql-files
with: with:
files: | files: |
server/src/queries server/src/queries
- name: Verify SQL files have not changed - name: Verify SQL files have not changed
if: steps.verify-changed-sql-files.outputs.files_changed == 'true' if: steps.verify-changed-sql-files.outputs.files_changed == 'true'
env: env:
@@ -664,77 +725,77 @@ jobs:
git diff git diff
exit 1 exit 1
# mobile-integration-tests: # mobile-integration-tests:
# name: Run mobile end-to-end integration tests # name: Run mobile end-to-end integration tests
# runs-on: macos-latest # runs-on: macos-latest
# steps: # steps:
# - uses: actions/checkout@v4 # - uses: actions/checkout@v4
# - uses: actions/setup-java@v3 # - uses: actions/setup-java@v3
# with: # with:
# distribution: 'zulu' # distribution: 'zulu'
# java-version: '12.x' # java-version: '12.x'
# cache: 'gradle' # cache: 'gradle'
# - name: Cache android SDK # - name: Cache android SDK
# uses: actions/cache@v3 # uses: actions/cache@v3
# id: android-sdk # id: android-sdk
# with: # with:
# key: android-sdk # key: android-sdk
# path: | # path: |
# /usr/local/lib/android/ # /usr/local/lib/android/
# ~/.android # ~/.android
# - name: Cache Gradle # - name: Cache Gradle
# uses: actions/cache@v3 # uses: actions/cache@v3
# with: # with:
# path: | # path: |
# ./mobile/build/ # ./mobile/build/
# ./mobile/android/.gradle/ # ./mobile/android/.gradle/
# key: ${{ runner.os }}-flutter-${{ hashFiles('**/*.gradle*', 'pubspec.lock') }} # key: ${{ runner.os }}-flutter-${{ hashFiles('**/*.gradle*', 'pubspec.lock') }}
# - name: Setup Android SDK # - name: Setup Android SDK
# if: steps.android-sdk.outputs.cache-hit != 'true' # if: steps.android-sdk.outputs.cache-hit != 'true'
# uses: android-actions/setup-android@v2 # uses: android-actions/setup-android@v2
# - name: AVD cache # - name: AVD cache
# uses: actions/cache@v3 # uses: actions/cache@v3
# id: avd-cache # id: avd-cache
# with: # with:
# path: | # path: |
# ~/.android/avd/* # ~/.android/avd/*
# ~/.android/adb* # ~/.android/adb*
# key: avd-29 # key: avd-29
# - name: create AVD and generate snapshot for caching # - name: create AVD and generate snapshot for caching
# if: steps.avd-cache.outputs.cache-hit != 'true' # if: steps.avd-cache.outputs.cache-hit != 'true'
# uses: reactivecircus/android-emulator-runner@v2.27.0 # uses: reactivecircus/android-emulator-runner@v2.27.0
# with: # with:
# working-directory: ./mobile # working-directory: ./mobile
# cores: 2 # cores: 2
# api-level: 29 # api-level: 29
# arch: x86_64 # arch: x86_64
# profile: pixel # profile: pixel
# target: default # target: default
# force-avd-creation: false # force-avd-creation: false
# emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none # emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
# disable-animations: false # disable-animations: false
# script: echo "Generated AVD snapshot for caching." # script: echo "Generated AVD snapshot for caching."
# - name: Setup Flutter SDK # - name: Setup Flutter SDK
# uses: subosito/flutter-action@v2 # uses: subosito/flutter-action@v2
# with: # with:
# channel: 'stable' # channel: 'stable'
# flutter-version: '3.7.3' # flutter-version: '3.7.3'
# cache: true # cache: true
# - name: Run integration tests # - name: Run integration tests
# uses: Wandalen/wretry.action@master # uses: Wandalen/wretry.action@master
# with: # with:
# action: reactivecircus/android-emulator-runner@v2.27.0 # action: reactivecircus/android-emulator-runner@v2.27.0
# with: | # with: |
# working-directory: ./mobile # working-directory: ./mobile
# cores: 2 # cores: 2
# api-level: 29 # api-level: 29
# arch: x86_64 # arch: x86_64
# profile: pixel # profile: pixel
# target: default # target: default
# force-avd-creation: false # force-avd-creation: false
# emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none # emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
# disable-animations: true # disable-animations: true
# script: | # script: |
# flutter pub get # flutter pub get
# flutter test integration_test # flutter test integration_test
# attempt_limit: 3 # attempt_limit: 3

View File

@@ -15,7 +15,7 @@ jobs:
should_run: ${{ steps.found_paths.outputs.i18n == 'true' && github.head_ref != 'chore/translations'}} should_run: ${{ steps.found_paths.outputs.i18n == 'true' && github.head_ref != 'chore/translations'}}
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
- id: found_paths - id: found_paths
@@ -38,7 +38,7 @@ jobs:
exit 1 exit 1
fi fi
- name: Find Pull Request - name: Find Pull Request
uses: juliangruber/find-pull-request-action@952b3bb1ddb2dcc0aa3479e98bb1c2d1a922f096 # v1.10.0 uses: juliangruber/find-pull-request-action@48b6133aa6c826f267ebd33aa2d29470f9d9e7d0 # v1.9.0
id: find-pr id: find-pr
with: with:
branch: chore/translations branch: chore/translations
@@ -52,6 +52,6 @@ jobs:
permissions: {} permissions: {}
if: always() if: always()
steps: steps:
- uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4 - uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
with: with:
needs: ${{ toJSON(needs) }} needs: ${{ toJSON(needs) }}

1
.gitignore vendored
View File

@@ -24,4 +24,3 @@ mobile/android/fastlane/report.xml
mobile/ios/fastlane/report.xml mobile/ios/fastlane/report.xml
vite.config.js.timestamp-* vite.config.js.timestamp-*
.pnpm-store

View File

@@ -1,18 +0,0 @@
module.exports = {
hooks: {
readPackage: (pkg) => {
if (!pkg.name) {
return pkg;
}
if (pkg.name === "exiftool-vendored") {
if (pkg.optionalDependencies["exiftool-vendored.pl"]) {
// make exiftool-vendored.pl a regular dependency
pkg.dependencies["exiftool-vendored.pl"] =
pkg.optionalDependencies["exiftool-vendored.pl"];
delete pkg.optionalDependencies["exiftool-vendored.pl"];
}
}
return pkg;
},
},
};

4
.vscode/launch.json vendored
View File

@@ -7,7 +7,7 @@
"restart": true, "restart": true,
"port": 9231, "port": 9231,
"name": "Immich API Server", "name": "Immich API Server",
"remoteRoot": "/usr/src/app/server", "remoteRoot": "/usr/src/app",
"localRoot": "${workspaceFolder}/server" "localRoot": "${workspaceFolder}/server"
}, },
{ {
@@ -16,7 +16,7 @@
"restart": true, "restart": true,
"port": 9230, "port": 9230,
"name": "Immich Workers", "name": "Immich Workers",
"remoteRoot": "/usr/src/app/server", "remoteRoot": "/usr/src/app",
"localRoot": "${workspaceFolder}/server" "localRoot": "${workspaceFolder}/server"
} }
] ]

View File

@@ -56,8 +56,7 @@
"explorer.fileNesting.enabled": true, "explorer.fileNesting.enabled": true,
"explorer.fileNesting.patterns": { "explorer.fileNesting.patterns": {
"*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart", "*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart",
"*.ts": "${capture}.spec.ts,${capture}.mock.ts", "*.ts": "${capture}.spec.ts,${capture}.mock.ts"
"package.json": "package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb, bun.lock, pnpm-workspace.yaml, .pnpmfile.cjs"
}, },
"svelte.enable-ts-plugin": true, "svelte.enable-ts-plugin": true,
"typescript.preferences.importModuleSpecifier": "non-relative" "typescript.preferences.importModuleSpecifier": "non-relative"

141
Makefile
View File

@@ -1,49 +1,40 @@
dev: prepare-volumes dev:
@trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --remove-orphans docker compose -f ./docker/docker-compose.dev.yml up --remove-orphans || make dev-down
dev-down: dev-down:
docker compose -f ./docker/docker-compose.dev.yml down --remove-orphans docker compose -f ./docker/docker-compose.dev.yml down --remove-orphans
dev-update: prepare-volumes dev-update:
@trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --build -V --remove-orphans docker compose -f ./docker/docker-compose.dev.yml up --build -V --remove-orphans
dev-scale: prepare-volumes dev-scale:
@trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --build -V --scale immich-server=3 --remove-orphans docker compose -f ./docker/docker-compose.dev.yml up --build -V --scale immich-server=3 --remove-orphans
dev-docs: prepare-volumes
npm --prefix docs run start
.PHONY: e2e .PHONY: e2e
e2e: prepare-volumes e2e:
@trap 'make e2e-down' EXIT; COMPOSE_BAKE=true docker compose -f ./e2e/docker-compose.yml up --remove-orphans docker compose -f ./e2e/docker-compose.yml up --build -V --remove-orphans
e2e-update: prepare-volumes prod:
@trap 'make e2e-down' EXIT; COMPOSE_BAKE=true docker compose -f ./e2e/docker-compose.yml up --build -V --remove-orphans docker compose -f ./docker/docker-compose.prod.yml up --build -V --remove-orphans
e2e-down:
docker compose -f ./e2e/docker-compose.yml down --remove-orphans
prod:
@trap 'make prod-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.prod.yml up --build -V --remove-orphans
prod-down: prod-down:
docker compose -f ./docker/docker-compose.prod.yml down --remove-orphans docker compose -f ./docker/docker-compose.prod.yml down --remove-orphans
prod-scale: prod-scale:
@trap 'make prod-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.prod.yml up --build -V --scale immich-server=3 --scale immich-microservices=3 --remove-orphans docker compose -f ./docker/docker-compose.prod.yml up --build -V --scale immich-server=3 --scale immich-microservices=3 --remove-orphans
.PHONY: open-api .PHONY: open-api
open-api: prepare-volumes open-api:
cd ./open-api && bash ./bin/generate-open-api.sh cd ./open-api && bash ./bin/generate-open-api.sh
open-api-dart: prepare-volumes open-api-dart:
cd ./open-api && bash ./bin/generate-open-api.sh dart cd ./open-api && bash ./bin/generate-open-api.sh dart
open-api-typescript: prepare-volumes open-api-typescript:
cd ./open-api && bash ./bin/generate-open-api.sh typescript cd ./open-api && bash ./bin/generate-open-api.sh typescript
sql: prepare-volumes sql:
pnpm --filter immich run sync:sql npm --prefix server run sync:sql
attach-server: attach-server:
docker exec -it docker_immich-server_1 sh docker exec -it docker_immich-server_1 sh
@@ -51,66 +42,31 @@ attach-server:
renovate: renovate:
LOG_LEVEL=debug npx renovate --platform=local --repository-cache=reset LOG_LEVEL=debug npx renovate --platform=local --repository-cache=reset
# Directories that need to be created for volumes or build output
VOLUME_DIRS = \
./.pnpm-store \
./web/.svelte-kit \
./web/node_modules \
./web/coverage \
./e2e/node_modules \
./docs/node_modules \
./server/node_modules \
./server/dist \
./open-api/typescript-sdk/node_modules \
./.github/node_modules \
./node_modules \
./cli/node_modules
# create empty directories and chown to current user
prepare-volumes:
@for dir in $(VOLUME_DIRS); do \
mkdir -p $$dir; \
done
@if [ -n "$(VOLUME_DIRS)" ]; then \
chown -R $$(id -u):$$(id -g) $(VOLUME_DIRS); \
fi
MODULES = e2e server web cli sdk docs .github MODULES = e2e server web cli sdk docs .github
# directory to package name mapping function
# cli = @immich/cli
# docs = documentation
# e2e = immich-e2e
# open-api/typescript-sdk = @immich/sdk
# server = immich
# web = immich-web
map-package = $(subst sdk,@immich/sdk,$(subst cli,@immich/cli,$(subst docs,documentation,$(subst e2e,immich-e2e,$(subst server,immich,$(subst web,immich-web,$1))))))
audit-%: audit-%:
pnpm --filter $(call map-package,$*) audit fix npm --prefix $(subst sdk,open-api/typescript-sdk,$*) audit fix
install-%: install-%:
pnpm --filter $(call map-package,$*) install $(if $(FROZEN),--frozen-lockfile) $(if $(OFFLINE),--offline) npm --prefix $(subst sdk,open-api/typescript-sdk,$*) i
build-cli: build-sdk build-cli: build-sdk
build-web: build-sdk build-web: build-sdk
build-%: install-% build-%: install-%
pnpm --filter $(call map-package,$*) run build npm --prefix $(subst sdk,open-api/typescript-sdk,$*) run build
format-%: format-%:
pnpm --filter $(call map-package,$*) run format:fix npm --prefix $* run format:fix
lint-%: lint-%:
pnpm --filter $(call map-package,$*) run lint:fix npm --prefix $* run lint:fix
lint-web:
pnpm --filter $(call map-package,$*) run lint:p
check-%: check-%:
pnpm --filter $(call map-package,$*) run check npm --prefix $* run check
check-web: check-web:
pnpm --filter immich-web run check:typescript npm --prefix web run check:typescript
pnpm --filter immich-web run check:svelte npm --prefix web run check:svelte
test-%: test-%:
pnpm --filter $(call map-package,$*) run test npm --prefix $* run test
test-e2e: test-e2e:
docker compose -f ./e2e/docker-compose.yml build docker compose -f ./e2e/docker-compose.yml build
pnpm --filter immich-e2e run test npm --prefix e2e run test
pnpm --filter immich-e2e run test:web npm --prefix e2e run test:web
test-medium: test-medium:
docker run \ docker run \
--rm \ --rm \
@@ -120,38 +76,23 @@ test-medium:
-v ./server/tsconfig.json:/usr/src/app/tsconfig.json \ -v ./server/tsconfig.json:/usr/src/app/tsconfig.json \
-e NODE_ENV=development \ -e NODE_ENV=development \
immich-server:latest \ immich-server:latest \
-c "pnpm test:medium -- --run" -c "npm ci && npm run test:medium -- --run"
test-medium-dev: test-medium-dev:
docker exec -it immich_server /bin/sh -c "pnpm run test:medium" docker exec -it immich_server /bin/sh -c "npm run test:medium"
install-all: build-all: $(foreach M,$(filter-out e2e .github,$(MODULES)),build-$M) ;
pnpm -r --filter '!documentation' install install-all: $(foreach M,$(MODULES),install-$M) ;
check-all: $(foreach M,$(filter-out sdk cli docs .github,$(MODULES)),check-$M) ;
build-all: $(foreach M,$(filter-out e2e docs .github,$(MODULES)),build-$M) ; lint-all: $(foreach M,$(filter-out sdk docs .github,$(MODULES)),lint-$M) ;
format-all: $(foreach M,$(filter-out sdk,$(MODULES)),format-$M) ;
check-all: audit-all: $(foreach M,$(MODULES),audit-$M) ;
pnpm -r --filter '!documentation' run "/^(check|check\:svelte|check\:typescript)$/" hygiene-all: lint-all format-all check-all sql audit-all;
lint-all: test-all: $(foreach M,$(filter-out sdk docs .github,$(MODULES)),test-$M) ;
pnpm -r --filter '!documentation' run lint:fix
format-all:
pnpm -r --filter '!documentation' run format:fix
audit-all:
pnpm -r --filter '!documentation' audit fix
hygiene-all: audit-all
pnpm -r --filter '!documentation' run "/(format:fix|check|check:svelte|check:typescript|sql)/"
test-all:
pnpm -r --filter '!documentation' run "/^test/"
clean: clean:
find . -name "node_modules" -type d -prune -exec rm -rf {} + find . -name "node_modules" -type d -prune -exec rm -rf '{}' +
find . -name "dist" -type d -prune -exec rm -rf '{}' + find . -name "dist" -type d -prune -exec rm -rf '{}' +
find . -name "build" -type d -prune -exec rm -rf '{}' + find . -name "build" -type d -prune -exec rm -rf '{}' +
find . -name ".svelte-kit" -type d -prune -exec rm -rf '{}' + find . -name "svelte-kit" -type d -prune -exec rm -rf '{}' +
find . -name "coverage" -type d -prune -exec rm -rf '{}' + docker compose -f ./docker/docker-compose.dev.yml rm -v -f || true
find . -name ".pnpm-store" -type d -prune -exec rm -rf '{}' + docker compose -f ./e2e/docker-compose.yml rm -v -f || true
command -v docker >/dev/null 2>&1 && docker compose -f ./docker/docker-compose.dev.yml rm -v -f || true
command -v docker >/dev/null 2>&1 && docker compose -f ./e2e/docker-compose.yml rm -v -f || true
setup-server-dev: install-server
setup-web-dev: install-sdk build-sdk install-web

View File

@@ -1 +1 @@
22.18.0 22.16.0

View File

@@ -1,14 +1,19 @@
FROM node:22.16.0-alpine3.20@sha256:2289fb1fba0f4633b08ec47b94a89c7e20b829fc5679f9b7b298eaa2f1ed8b7e AS core FROM node:22.16.0-alpine3.20@sha256:2289fb1fba0f4633b08ec47b94a89c7e20b829fc5679f9b7b298eaa2f1ed8b7e AS core
WORKDIR /usr/src/open-api/typescript-sdk
COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./
RUN npm ci
COPY open-api/typescript-sdk/ ./
RUN npm run build
WORKDIR /usr/src/app WORKDIR /usr/src/app
COPY package* pnpm* .pnpmfile.cjs ./
COPY ./cli ./cli/ COPY cli/package.json cli/package-lock.json ./
COPY ./open-api/typescript-sdk ./open-api/typescript-sdk/ RUN npm ci
RUN corepack enable pnpm && \
pnpm install --filter @immich/sdk --filter @immich/cli --frozen-lockfile && \ COPY cli .
pnpm --filter @immich/sdk build && \ RUN npm run build
pnpm --filter @immich/cli build
WORKDIR /import WORKDIR /import
ENTRYPOINT ["node", "/usr/src/app/cli/dist"] ENTRYPOINT ["node", "/usr/src/app/dist"]

View File

@@ -1,2 +0,0 @@
#!/usr/bin/env node
import '../dist/index.js';

4517
cli/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,11 @@
{ {
"name": "@immich/cli", "name": "@immich/cli",
"version": "2.2.84", "version": "2.2.72",
"description": "Command Line Interface (CLI) for Immich", "description": "Command Line Interface (CLI) for Immich",
"type": "module", "type": "module",
"exports": "./dist/index.js", "exports": "./dist/index.js",
"bin": { "bin": {
"immich": "./bin/immich" "immich": "dist/index.js"
}, },
"license": "GNU Affero General Public License version 3", "license": "GNU Affero General Public License version 3",
"keywords": [ "keywords": [
@@ -21,22 +21,22 @@
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/micromatch": "^4.0.9", "@types/micromatch": "^4.0.9",
"@types/mock-fs": "^4.13.1", "@types/mock-fs": "^4.13.1",
"@types/node": "^22.17.1", "@types/node": "^22.15.31",
"@vitest/coverage-v8": "^3.0.0", "@vitest/coverage-v8": "^3.0.0",
"byte-size": "^9.0.0", "byte-size": "^9.0.0",
"cli-progress": "^3.12.0", "cli-progress": "^3.12.0",
"commander": "^12.0.0", "commander": "^12.0.0",
"eslint": "^9.14.0", "eslint": "^9.14.0",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.0.0",
"eslint-plugin-prettier": "^5.1.3", "eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-unicorn": "^60.0.0", "eslint-plugin-unicorn": "^59.0.0",
"globals": "^16.0.0", "globals": "^16.0.0",
"mock-fs": "^5.2.0", "mock-fs": "^5.2.0",
"prettier": "^3.2.5", "prettier": "^3.2.5",
"prettier-plugin-organize-imports": "^4.0.0", "prettier-plugin-organize-imports": "^4.0.0",
"typescript": "^5.3.3", "typescript": "^5.3.3",
"typescript-eslint": "^8.28.0", "typescript-eslint": "^8.28.0",
"vite": "^7.0.0", "vite": "^6.0.0",
"vite-tsconfig-paths": "^5.0.0", "vite-tsconfig-paths": "^5.0.0",
"vitest": "^3.0.0", "vitest": "^3.0.0",
"vitest-fetch-mock": "^0.4.0", "vitest-fetch-mock": "^0.4.0",
@@ -69,6 +69,6 @@
"micromatch": "^4.0.8" "micromatch": "^4.0.8"
}, },
"volta": { "volta": {
"node": "22.18.0" "node": "22.16.0"
} }
} }

View File

@@ -2,37 +2,37 @@
# Manual edits may be lost in future updates. # Manual edits may be lost in future updates.
provider "registry.opentofu.org/cloudflare/cloudflare" { provider "registry.opentofu.org/cloudflare/cloudflare" {
version = "4.52.1" version = "4.52.0"
constraints = "4.52.1" constraints = "4.52.0"
hashes = [ hashes = [
"h1:2lHvafwGbLdmc9lYkuJFw3nsInaQjRpjX/JfIRKmq/M=", "h1:2BEJyXJtYC4B4nda/WCYUmuJYDaYk88F8t1pwPzr0iQ=",
"h1:596JomwjrtUrOSreq9NNCS+rj70+jOV+0pfja5MXiTI=", "h1:4IASk5SESeWKQ7JU0+M7KApuF5mZyklvwMXPBabim3c=",
"h1:7mBOA5TVAIt3qAwPXKCtE0RSYeqij9v30mnksuBbpEg=", "h1:5ImZxxALSnWfH/4EXw/wFirSmk5Tr0ACmcysy51AafE=",
"h1:ELVgzh4kHKBCYdL+2A8JjWS0E1snLUN3Mmz3Vo6qSfw=", "h1:6TJ3dxLSin4ZKBJLsZDn95H2ZYnGm8S7GGHvvXuuMQU=",
"h1:FGGM5yLFf72g3kSXM3LAN64Gf/AkXr5WCmhixgnP+l4=", "h1:IzTUjg9kQ4N3qizP9CjYLeHwjsuGgtxwXvfUQWyOLcA=",
"h1:JupkJbQALcIVoMhHImrLeLDsQR1ET7VJLGC7ONxjqGU=", "h1:NTaOQfYINA0YTG/V1/9+SYtgX1it63+cBugj4WK4FWc=",
"h1:KsaE4JNq+1uV1nJsuTcYar/8lyY6zKS5UBEpfYg3wvc=", "h1:PXH48LuJn329sCfMXprdMDk51EZaWFyajVvS03qhQLs=",
"h1:NHZ5RJIzQDLhie/ykl3uI6UPfNQR9Lu5Ti7JPR6X904=", "h1:Pi5M+GeoMSN2eJ6QnIeXjBf19O+rby/74CfB2ocpv20=",
"h1:NfAuMbn6LQPLDtJhbzO1MX9JMIGLMa8K6CpekvtsuX8=", "h1:ShXZ2ZjBvm3thfoPPzPT8+OhyismnydQVkUAfI8X12w=",
"h1:e+vNKokamDsp/kJvFr2pRudzwEz2r49iZ/oSggw+1LY=", "h1:WQ9hu0Wge2msBbODfottCSKgu8oKUrw4Opz+fDPVVHk=",
"h1:jnb4VdfNZ79I3yj7Q8x+JmOT+FxbfjjRfrF0dL0yCW8=", "h1:Z5yXML2DE0uH9UU+M0ut9JMQAORcwVZz1CxBHzeBmao=",
"h1:kmF//O539d7NuHU7qIxDj7Wz4eJmLKFiI5glwQivldU=", "h1:jqI2qKknpleS3JDSplyGYHMu0u9K/tor1ZOjFwDgEMk=",
"h1:s6XriaKwOgV4jvKAGPXkrxhhOQxpNU5dceZwi9Z/1k8=", "h1:kgfutDh14Q5nw4eg6qGFamFxIiY8Ae0FPKRBLDOzpcI=",
"h1:wt3WBEBAeSGTlC9OlnTlAALxRiK4SQgLy0KgBIS7qzs=", "h1:zCAO7GZmfYhWb+i6TfqlqhMeDyPZWGio2IzEzAh3YTs=",
"zh:2fb95e1d3229b9b6c704e1a413c7481c60f139780d9641f657b6eb9b633b90f2", "zh:19be1a91c982b902c42aba47766860dfa5dc151eed1e95fd39ca642229381ef0",
"zh:379c7680983383862236e9e6e720c3114195c40526172188e88d0ffcf50dfe2e", "zh:1de451c4d1ecf7efbe67b6dace3426ba810711afdd644b0f1b870364c8ae91f8",
"zh:55533beb6cfc02d22ffda8cba8027bc2c841bb172cd637ed0d28323d41395f8f", "zh:352b4a2120173298622e669258744554339d959ac3a95607b117a48ee4a83238",
"zh:5abd70760e4eb1f37a1c307cbd2989ea7c9ba0afb93818c67c1d363a31f75703", "zh:3c6f1346d9154afbd2d558fabb4b0150fc8d559aa961254144fe1bc17fe6032f",
"zh:699f1c8cd66129176fe659ebf0e6337632a8967a28d2630b6ae5948665c0c2ae", "zh:4c4c92d53fb535b1e0eff26f222bbd627b97d3b4c891ec9c321268676d06152f",
"zh:69c15acd73c451e89de6477059cda2f3ec200b48ae4b9ff3646c4d389fd3205e", "zh:53276f68006c9ceb7cdb10a6ccf91a5c1eadd1407a28edb5741e84e88d7e29e8",
"zh:6e02b687de21b844f8266dff99e93e7c61fc8eb688f4bbb23803caceb251839e", "zh:7925a97773948171a63d4f65bb81ee92fd6d07a447e36012977313293a5435c9",
"zh:7a51d17b87ed87b7bebf2ad9fc7c3a74f16a1b44eee92c779c08eb89258c0496", "zh:7dfb0a4496cfe032437386d0a2cd9229a1956e9c30bd920923c141b0f0440060",
"zh:88ad84436837b0f55302f22748505972634e87400d6902260fd6b7ba1610f937",
"zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f", "zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f",
"zh:8d46c3d9f4f7ad20ac6ef01daa63f4e30a2d16dcb1bb5c7c7ee3dc6be38e9ca1", "zh:8d4aa79f0a414bb4163d771063c70cd991c8fac6c766e685bac2ee12903c5bd6",
"zh:913d64e72a4929dae1d4793e2004f4f9a58b138ea337d9d94fa35cafbf06550a", "zh:a67540c13565616a7e7e51ee9366e88b0dc60046e1d75c72680e150bd02725bb",
"zh:c8d93cf86e2e49f6cec665cfe78b82c144cce15a8b2e30f343385fadd1251849", "zh:a936383a4767f5393f38f622e92bf2d0c03fe04b69c284951f27345766c7b31b",
"zh:cc4f69397d9bc34a528a5609a024c3a48f54f21616c0008792dd417297add955", "zh:d4887d73c466ff036eecf50ad6404ba38fd82ea4855296b1846d244b0f13c380",
"zh:df99cdb8b064aad35ffea77e645cf6541d0b1b2ebc51b6d26c42031de60ab69e", "zh:e9093c8bd5b6cd99c81666e315197791781b8f93afa14fc2e0f732d1bb2a44b7",
"zh:efd3b3f1ec59a37f635aa1d4efcf178734c2fcf8ddb0d56ea690bec342da8672",
] ]
} }

View File

@@ -5,7 +5,7 @@ terraform {
required_providers { required_providers {
cloudflare = { cloudflare = {
source = "cloudflare/cloudflare" source = "cloudflare/cloudflare"
version = "4.52.1" version = "4.52.0"
} }
} }
} }

View File

@@ -2,37 +2,37 @@
# Manual edits may be lost in future updates. # Manual edits may be lost in future updates.
provider "registry.opentofu.org/cloudflare/cloudflare" { provider "registry.opentofu.org/cloudflare/cloudflare" {
version = "4.52.1" version = "4.52.0"
constraints = "4.52.1" constraints = "4.52.0"
hashes = [ hashes = [
"h1:2lHvafwGbLdmc9lYkuJFw3nsInaQjRpjX/JfIRKmq/M=", "h1:2BEJyXJtYC4B4nda/WCYUmuJYDaYk88F8t1pwPzr0iQ=",
"h1:596JomwjrtUrOSreq9NNCS+rj70+jOV+0pfja5MXiTI=", "h1:4IASk5SESeWKQ7JU0+M7KApuF5mZyklvwMXPBabim3c=",
"h1:7mBOA5TVAIt3qAwPXKCtE0RSYeqij9v30mnksuBbpEg=", "h1:5ImZxxALSnWfH/4EXw/wFirSmk5Tr0ACmcysy51AafE=",
"h1:ELVgzh4kHKBCYdL+2A8JjWS0E1snLUN3Mmz3Vo6qSfw=", "h1:6TJ3dxLSin4ZKBJLsZDn95H2ZYnGm8S7GGHvvXuuMQU=",
"h1:FGGM5yLFf72g3kSXM3LAN64Gf/AkXr5WCmhixgnP+l4=", "h1:IzTUjg9kQ4N3qizP9CjYLeHwjsuGgtxwXvfUQWyOLcA=",
"h1:JupkJbQALcIVoMhHImrLeLDsQR1ET7VJLGC7ONxjqGU=", "h1:NTaOQfYINA0YTG/V1/9+SYtgX1it63+cBugj4WK4FWc=",
"h1:KsaE4JNq+1uV1nJsuTcYar/8lyY6zKS5UBEpfYg3wvc=", "h1:PXH48LuJn329sCfMXprdMDk51EZaWFyajVvS03qhQLs=",
"h1:NHZ5RJIzQDLhie/ykl3uI6UPfNQR9Lu5Ti7JPR6X904=", "h1:Pi5M+GeoMSN2eJ6QnIeXjBf19O+rby/74CfB2ocpv20=",
"h1:NfAuMbn6LQPLDtJhbzO1MX9JMIGLMa8K6CpekvtsuX8=", "h1:ShXZ2ZjBvm3thfoPPzPT8+OhyismnydQVkUAfI8X12w=",
"h1:e+vNKokamDsp/kJvFr2pRudzwEz2r49iZ/oSggw+1LY=", "h1:WQ9hu0Wge2msBbODfottCSKgu8oKUrw4Opz+fDPVVHk=",
"h1:jnb4VdfNZ79I3yj7Q8x+JmOT+FxbfjjRfrF0dL0yCW8=", "h1:Z5yXML2DE0uH9UU+M0ut9JMQAORcwVZz1CxBHzeBmao=",
"h1:kmF//O539d7NuHU7qIxDj7Wz4eJmLKFiI5glwQivldU=", "h1:jqI2qKknpleS3JDSplyGYHMu0u9K/tor1ZOjFwDgEMk=",
"h1:s6XriaKwOgV4jvKAGPXkrxhhOQxpNU5dceZwi9Z/1k8=", "h1:kgfutDh14Q5nw4eg6qGFamFxIiY8Ae0FPKRBLDOzpcI=",
"h1:wt3WBEBAeSGTlC9OlnTlAALxRiK4SQgLy0KgBIS7qzs=", "h1:zCAO7GZmfYhWb+i6TfqlqhMeDyPZWGio2IzEzAh3YTs=",
"zh:2fb95e1d3229b9b6c704e1a413c7481c60f139780d9641f657b6eb9b633b90f2", "zh:19be1a91c982b902c42aba47766860dfa5dc151eed1e95fd39ca642229381ef0",
"zh:379c7680983383862236e9e6e720c3114195c40526172188e88d0ffcf50dfe2e", "zh:1de451c4d1ecf7efbe67b6dace3426ba810711afdd644b0f1b870364c8ae91f8",
"zh:55533beb6cfc02d22ffda8cba8027bc2c841bb172cd637ed0d28323d41395f8f", "zh:352b4a2120173298622e669258744554339d959ac3a95607b117a48ee4a83238",
"zh:5abd70760e4eb1f37a1c307cbd2989ea7c9ba0afb93818c67c1d363a31f75703", "zh:3c6f1346d9154afbd2d558fabb4b0150fc8d559aa961254144fe1bc17fe6032f",
"zh:699f1c8cd66129176fe659ebf0e6337632a8967a28d2630b6ae5948665c0c2ae", "zh:4c4c92d53fb535b1e0eff26f222bbd627b97d3b4c891ec9c321268676d06152f",
"zh:69c15acd73c451e89de6477059cda2f3ec200b48ae4b9ff3646c4d389fd3205e", "zh:53276f68006c9ceb7cdb10a6ccf91a5c1eadd1407a28edb5741e84e88d7e29e8",
"zh:6e02b687de21b844f8266dff99e93e7c61fc8eb688f4bbb23803caceb251839e", "zh:7925a97773948171a63d4f65bb81ee92fd6d07a447e36012977313293a5435c9",
"zh:7a51d17b87ed87b7bebf2ad9fc7c3a74f16a1b44eee92c779c08eb89258c0496", "zh:7dfb0a4496cfe032437386d0a2cd9229a1956e9c30bd920923c141b0f0440060",
"zh:88ad84436837b0f55302f22748505972634e87400d6902260fd6b7ba1610f937",
"zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f", "zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f",
"zh:8d46c3d9f4f7ad20ac6ef01daa63f4e30a2d16dcb1bb5c7c7ee3dc6be38e9ca1", "zh:8d4aa79f0a414bb4163d771063c70cd991c8fac6c766e685bac2ee12903c5bd6",
"zh:913d64e72a4929dae1d4793e2004f4f9a58b138ea337d9d94fa35cafbf06550a", "zh:a67540c13565616a7e7e51ee9366e88b0dc60046e1d75c72680e150bd02725bb",
"zh:c8d93cf86e2e49f6cec665cfe78b82c144cce15a8b2e30f343385fadd1251849", "zh:a936383a4767f5393f38f622e92bf2d0c03fe04b69c284951f27345766c7b31b",
"zh:cc4f69397d9bc34a528a5609a024c3a48f54f21616c0008792dd417297add955", "zh:d4887d73c466ff036eecf50ad6404ba38fd82ea4855296b1846d244b0f13c380",
"zh:df99cdb8b064aad35ffea77e645cf6541d0b1b2ebc51b6d26c42031de60ab69e", "zh:e9093c8bd5b6cd99c81666e315197791781b8f93afa14fc2e0f732d1bb2a44b7",
"zh:efd3b3f1ec59a37f635aa1d4efcf178734c2fcf8ddb0d56ea690bec342da8672",
] ]
} }

View File

@@ -5,7 +5,7 @@ terraform {
required_providers { required_providers {
cloudflare = { cloudflare = {
source = "cloudflare/cloudflare" source = "cloudflare/cloudflare"
version = "4.52.1" version = "4.52.0"
} }
} }
} }

View File

@@ -16,33 +16,23 @@ name: immich-dev
services: services:
immich-server: immich-server:
container_name: immich_server container_name: immich_server
command: ['immich-dev'] command: ['/usr/src/app/bin/immich-dev']
image: immich-server-dev:latest image: immich-server-dev:latest
# extends: # extends:
# file: hwaccel.transcoding.yml # file: hwaccel.transcoding.yml
# service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding # service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding
user: '${UID:-1000}:${GID:-1000}'
build: build:
context: ../ context: ../
dockerfile: server/Dockerfile dockerfile: server/Dockerfile
target: dev target: dev
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- ..:/usr/src/app - ../server:/usr/src/app
- ${UPLOAD_LOCATION}/photos:/data - ../open-api:/usr/src/open-api
- ${UPLOAD_LOCATION}/photos/upload:/data/upload - ${UPLOAD_LOCATION}/photos:/usr/src/app/upload
- ${UPLOAD_LOCATION}/photos/upload:/usr/src/app/upload/upload
- /usr/src/app/node_modules
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
- pnpm-store:/usr/src/app/.pnpm-store
- server-node_modules:/usr/src/app/server/node_modules
- web-node_modules:/usr/src/app/web/node_modules
- github-node_modules:/usr/src/app/.github/node_modules
- cli-node_modules:/usr/src/app/cli/node_modules
- docs-node_modules:/usr/src/app/docs/node_modules
- e2e-node_modules:/usr/src/app/e2e/node_modules
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
- app-node_modules:/usr/src/app/node_modules
- sveltekit:/usr/src/app/web/.svelte-kit
- coverage:/usr/src/app/web/coverage
env_file: env_file:
- .env - .env
environment: environment:
@@ -68,12 +58,8 @@ services:
- 9231:9231 - 9231:9231
- 2283:2283 - 2283:2283
depends_on: depends_on:
redis: - redis
condition: service_started - database
database:
condition: service_started
init:
condition: service_completed_successfully
healthcheck: healthcheck:
disable: false disable: false
@@ -82,40 +68,27 @@ services:
image: immich-web-dev:latest image: immich-web-dev:latest
# Needed for rootless docker setup, see https://github.com/moby/moby/issues/45919 # Needed for rootless docker setup, see https://github.com/moby/moby/issues/45919
# user: 0:0 # user: 0:0
user: '${UID:-1000}:${GID:-1000}'
build: build:
context: ../ context: ../web
dockerfile: server/Dockerfile command: ['/usr/src/app/bin/immich-web']
target: dev
command: ['immich-web']
env_file: env_file:
- .env - .env
ports: ports:
- 3000:3000 - 3000:3000
- 24678:24678 - 24678:24678
volumes: volumes:
- ..:/usr/src/app - ../web:/usr/src/app
- pnpm-store:/usr/src/app/.pnpm-store - ../i18n:/usr/src/i18n
- server-node_modules:/usr/src/app/server/node_modules - ../open-api/:/usr/src/open-api/
- web-node_modules:/usr/src/app/web/node_modules # - ../../ui:/usr/ui
- github-node_modules:/usr/src/app/.github/node_modules - /usr/src/app/node_modules
- cli-node_modules:/usr/src/app/cli/node_modules
- docs-node_modules:/usr/src/app/docs/node_modules
- e2e-node_modules:/usr/src/app/e2e/node_modules
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
- app-node_modules:/usr/src/app/node_modules
- sveltekit:/usr/src/app/web/.svelte-kit
- coverage:/usr/src/app/web/coverage
ulimits: ulimits:
nofile: nofile:
soft: 1048576 soft: 1048576
hard: 1048576 hard: 1048576
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
immich-server: - immich-server
condition: service_started
init:
condition: service_completed_successfully
immich-machine-learning: immich-machine-learning:
container_name: immich_machine_learning container_name: immich_machine_learning
@@ -143,13 +116,13 @@ services:
redis: redis:
container_name: immich_redis container_name: immich_redis
image: docker.io/valkey/valkey:8-bookworm@sha256:a137a2b60aca1a75130022d6bb96af423fefae4eb55faf395732db3544803280 image: docker.io/valkey/valkey:8-bookworm@sha256:fec42f399876eb6faf9e008570597741c87ff7662a54185593e74b09ce83d177
healthcheck: healthcheck:
test: redis-cli ping || exit 1 test: redis-cli ping || exit 1
database: database:
container_name: immich_postgres container_name: immich_postgres
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:32324a2f41df5de9efe1af166b7008c3f55646f8d0e00d9550c16c9822366b4a image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
env_file: env_file:
- .env - .env
environment: environment:
@@ -161,7 +134,6 @@ services:
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data - ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
ports: ports:
- 5432:5432 - 5432:5432
shm_size: 128mb
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics # set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
# immich-prometheus: # immich-prometheus:
# container_name: immich_prometheus # container_name: immich_prometheus
@@ -183,37 +155,7 @@ services:
# volumes: # volumes:
# - grafana-data:/var/lib/grafana # - grafana-data:/var/lib/grafana
init:
container_name: init
image: busybox
env_file:
- .env
user: 0:0
command: sh -c 'for path in /usr/src/app/.pnpm-store /usr/src/app/server/node_modules /usr/src/app/server/dist /usr/src/app/.github/node_modules /usr/src/app/cli/node_modules /usr/src/app/docs/node_modules /usr/src/app/e2e/node_modules /usr/src/app/open-api/typescript-sdk/node_modules /usr/src/app/web/.svelte-kit /usr/src/app/web/coverage /usr/src/app/node_modules /usr/src/app/web/node_modules; do [ -e "$$path" ] && chown -R ${UID:-1000}:${GID:-1000} "$$path" || true; done'
volumes:
- pnpm-store:/usr/src/app/.pnpm-store
- server-node_modules:/usr/src/app/server/node_modules
- web-node_modules:/usr/src/app/web/node_modules
- github-node_modules:/usr/src/app/.github/node_modules
- cli-node_modules:/usr/src/app/cli/node_modules
- docs-node_modules:/usr/src/app/docs/node_modules
- e2e-node_modules:/usr/src/app/e2e/node_modules
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
- app-node_modules:/usr/src/app/node_modules
- sveltekit:/usr/src/app/web/.svelte-kit
- coverage:/usr/src/app/web/coverage
volumes: volumes:
model-cache: model-cache:
prometheus-data: prometheus-data:
grafana-data: grafana-data:
pnpm-store:
server-node_modules:
web-node_modules:
github-node_modules:
cli-node_modules:
docs-node_modules:
e2e-node_modules:
sdk-node_modules:
app-node_modules:
sveltekit:
coverage:

View File

@@ -20,7 +20,7 @@ services:
context: ../ context: ../
dockerfile: server/Dockerfile dockerfile: server/Dockerfile
volumes: volumes:
- ${UPLOAD_LOCATION}/photos:/data - ${UPLOAD_LOCATION}/photos:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
env_file: env_file:
- .env - .env
@@ -56,14 +56,14 @@ services:
redis: redis:
container_name: immich_redis container_name: immich_redis
image: docker.io/valkey/valkey:8-bookworm@sha256:a137a2b60aca1a75130022d6bb96af423fefae4eb55faf395732db3544803280 image: docker.io/valkey/valkey:8-bookworm@sha256:fec42f399876eb6faf9e008570597741c87ff7662a54185593e74b09ce83d177
healthcheck: healthcheck:
test: redis-cli ping || exit 1 test: redis-cli ping || exit 1
restart: always restart: always
database: database:
container_name: immich_postgres container_name: immich_postgres
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:32324a2f41df5de9efe1af166b7008c3f55646f8d0e00d9550c16c9822366b4a image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
env_file: env_file:
- .env - .env
environment: environment:
@@ -75,7 +75,6 @@ services:
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data - ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
ports: ports:
- 5432:5432 - 5432:5432
shm_size: 128mb
restart: always restart: always
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics # set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
@@ -83,7 +82,7 @@ services:
container_name: immich_prometheus container_name: immich_prometheus
ports: ports:
- 9090:9090 - 9090:9090
image: prom/prometheus@sha256:63805ebb8d2b3920190daf1cb14a60871b16fd38bed42b857a3182bc621f4996 image: prom/prometheus@sha256:9abc6cf6aea7710d163dbb28d8eeb7dc5baef01e38fa4cd146a406dd9f07f70d
volumes: volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml - ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus - prometheus-data:/prometheus
@@ -95,7 +94,7 @@ services:
command: ['./run.sh', '-disable-reporting'] command: ['./run.sh', '-disable-reporting']
ports: ports:
- 3000:3000 - 3000:3000
image: grafana/grafana:12.1.1-ubuntu@sha256:d1da838234ff2de93e0065ee1bf0e66d38f948dcc5d718c25fa6237e14b4424a image: grafana/grafana:12.0.1-ubuntu@sha256:65575bb9c761335e2ff30e364f21d38632e3b2e75f5f81d83cc92f44b9bbc055
volumes: volumes:
- grafana-data:/var/lib/grafana - grafana-data:/var/lib/grafana

View File

@@ -18,7 +18,7 @@ services:
# service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding # service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding
volumes: volumes:
# Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file # Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file
- ${UPLOAD_LOCATION}:/data - ${UPLOAD_LOCATION}:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
env_file: env_file:
- .env - .env
@@ -49,14 +49,14 @@ services:
redis: redis:
container_name: immich_redis container_name: immich_redis
image: docker.io/valkey/valkey:8-bookworm@sha256:a137a2b60aca1a75130022d6bb96af423fefae4eb55faf395732db3544803280 image: docker.io/valkey/valkey:8-bookworm@sha256:fec42f399876eb6faf9e008570597741c87ff7662a54185593e74b09ce83d177
healthcheck: healthcheck:
test: redis-cli ping || exit 1 test: redis-cli ping || exit 1
restart: always restart: always
database: database:
container_name: immich_postgres container_name: immich_postgres
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:32324a2f41df5de9efe1af166b7008c3f55646f8d0e00d9550c16c9822366b4a image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
environment: environment:
POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME} POSTGRES_USER: ${DB_USERNAME}
@@ -67,7 +67,6 @@ services:
volumes: volumes:
# Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file # Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data - ${DB_DATA_LOCATION}:/var/lib/postgresql/data
shm_size: 128mb
restart: always restart: always
volumes: volumes:

4
docs/.gitignore vendored
View File

@@ -18,6 +18,4 @@
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
yarn.lock yarn.lock
/static/openapi.json

View File

@@ -1 +1 @@
22.18.0 22.16.0

View File

@@ -5,13 +5,13 @@ This website is built using [Docusaurus](https://docusaurus.io/), a modern stati
### Installation ### Installation
``` ```
$ pnpm install $ npm install
``` ```
### Local Development ### Local Development
``` ```
$ pnpm run start $ npm run start
``` ```
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
@@ -19,7 +19,7 @@ This command starts a local development server and opens up a browser window. Mo
### Build ### Build
``` ```
$ pnpm run build $ npm run build
``` ```
This command generates static content into the `build` directory and can be served using any static contents hosting service. This command generates static content into the `build` directory and can be served using any static contents hosting service.
@@ -29,13 +29,13 @@ This command generates static content into the `build` directory and can be serv
Using SSH: Using SSH:
``` ```
$ USE_SSH=true pnpm run deploy $ USE_SSH=true npm run deploy
``` ```
Not using SSH: Not using SSH:
``` ```
$ GIT_USER=<Your GitHub username> pnpm run deploy $ GIT_USER=<Your GitHub username> npm run deploy
``` ```
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.

View File

@@ -1,31 +1,5 @@
# FAQ # FAQ
## Commercial Guidelines
### Are you open to commercial partnerships and collaborations?
We are working to commercialize Immich and we'd love for you to help us by making Immich better. FUTO is dedicated to developing sustainable models for developing open source software for our customers. We want our customers to be delighted by the products our engineers deliver, and we want our engineers to be paid when they succeed.
If you wish to use Immich in a commercial product not owned by FUTO, we have the following requirements:
- Plugin Integrations: Integrations for other platforms are typically approved, provided proper notification is given.
- Reseller Partnerships: Must adhere to the guidelines outlined below regarding trademark usage, and proper representation.
- Strategic Collaborations: We welcome discussions about mutually beneficial partnerships that enhance the value proposition for both organizations.
### What are your guidelines for resellers and trademark usage?
For organizations seeking to resell Immich, we have established the following guidelines to protect our brand integrity and ensure proper representation.
- We request that resellers do not display our trademarks on their websites or marketing materials. If such usage is discovered, we will contact you to request removal.
- Do not misrepresent your reseller site or services as being officially affiliated with or endorsed by Immich or our development team.
- For small resellers who wish to contribute financially to Immich's development, we recommend directing your customers to purchase licenses directy from us rather than attempting to broker revenue-sharing arrangements. We ask that you refrain from misrepresenting reseller activities as directly supporting our development work.
When in doubt or if you have an edge case scenario, we encourage you to contact us directly via email to discuss the use of our trademark. We can provide clear guidance on what is acceptable and what is not. You can reach out at: questions@immich.app
## User ## User
### How can I reset the admin password? ### How can I reset the admin password?
@@ -206,7 +180,7 @@ services:
... ...
volumes: volumes:
# Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file # Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file
- ${UPLOAD_LOCATION}:/data - ${UPLOAD_LOCATION}:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
+ - originals:/usr/src/app/originals + - originals:/usr/src/app/originals
... ...
@@ -516,7 +490,7 @@ You can also scan the Postgres database file structure for errors:
<details> <details>
<summary>Scan for file structure errors</summary> <summary>Scan for file structure errors</summary>
```bash ```bash
docker exec -it immich_postgres pg_amcheck --username=<DB_USERNAME> --heapallindexed --parent-check --rootdescend --progress --all --install-missing docker exec -it immich_postgres pg_amcheck --username=postgres --heapallindexed --parent-check --rootdescend --progress --all --install-missing
``` ```
A normal result will end something like this and return with an exit code of `0`: A normal result will end something like this and return with an exit code of `0`:

View File

@@ -57,7 +57,7 @@ Then please follow the steps in the following section for restoring the database
<TabItem value="Linux system" label="Linux system" default> <TabItem value="Linux system" label="Linux system" default>
```bash title='Backup' ```bash title='Backup'
docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=<DB_USERNAME> | gzip > "/path/to/backup/dump.sql.gz" docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=postgres | gzip > "/path/to/backup/dump.sql.gz"
``` ```
```bash title='Restore' ```bash title='Restore'
@@ -79,7 +79,7 @@ docker compose up -d # Start remainder of Immich apps
<TabItem value="Windows system (PowerShell)" label="Windows system (PowerShell)"> <TabItem value="Windows system (PowerShell)" label="Windows system (PowerShell)">
```powershell title='Backup' ```powershell title='Backup'
[System.IO.File]::WriteAllLines("C:\absolute\path\to\backup\dump.sql", (docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=<DB_USERNAME>)) [System.IO.File]::WriteAllLines("C:\absolute\path\to\backup\dump.sql", (docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=postgres))
``` ```
```powershell title='Restore' ```powershell title='Restore'
@@ -150,10 +150,12 @@ for more info read the [release notes](https://github.com/immich-app/immich/rele
- Preview images (small thumbnails and large previews) for each asset and thumbnails for recognized faces. - Preview images (small thumbnails and large previews) for each asset and thumbnails for recognized faces.
- Stored in `UPLOAD_LOCATION/thumbs/<userID>`. - Stored in `UPLOAD_LOCATION/thumbs/<userID>`.
- **Encoded Assets:** - **Encoded Assets:**
- Videos that have been re-encoded from the original for wider compatibility. The original is not removed. - Videos that have been re-encoded from the original for wider compatibility. The original is not removed.
- Stored in `UPLOAD_LOCATION/encoded-video/<userID>`. - Stored in `UPLOAD_LOCATION/encoded-video/<userID>`.
- **Postgres** - **Postgres**
- The Immich database containing all the information to allow the system to function properly. - 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. **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 `DB_DATA_LOCATION`. - Stored in `DB_DATA_LOCATION`.
@@ -199,6 +201,7 @@ When you turn off the storage template engine, it will leave the assets in `UPLO
- Temporarily located in `UPLOAD_LOCATION/upload/<userID>`. - Temporarily located in `UPLOAD_LOCATION/upload/<userID>`.
- Transferred to `UPLOAD_LOCATION/library/<userID>` upon successful upload. - Transferred to `UPLOAD_LOCATION/library/<userID>` upon successful upload.
- **Postgres** - **Postgres**
- The Immich database containing all the information to allow the system to function properly. - 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. **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 `DB_DATA_LOCATION`. - Stored in `DB_DATA_LOCATION`.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -46,12 +46,6 @@ services:
When a new asset is uploaded it kicks off a series of jobs, which include metadata extraction, thumbnail generation, machine learning tasks, and storage template migration, if enabled. To view the status of a job navigate to the Administration -> Jobs page. When a new asset is uploaded it kicks off a series of jobs, which include metadata extraction, thumbnail generation, machine learning tasks, and storage template migration, if enabled. To view the status of a job navigate to the Administration -> Jobs page.
Additionally, some jobs run on a schedule, which is every night at midnight. This schedule, with the exception of [External Libraries](/docs/features/libraries) scanning, cannot be changed.
<img src={require('./img/admin-jobs.webp').default} width="60%" title="Admin jobs" /> <img src={require('./img/admin-jobs.webp').default} width="60%" title="Admin jobs" />
Additionally, some jobs (such as memories generation) run on a schedule, which is every night at midnight by default. To change when they run or enable/disable a job navigate to System Settings -> [Nightly Tasks Settings](https://my.immich.app/admin/system-settings?isOpen=nightly-tasks).
<img src={require('./img/admin-nightly-tasks.webp').default} width="60%" title="Admin nightly tasks" />
:::note
Some jobs ([External Libraries](/docs/features/libraries) scanning, Database Dump) are configured in their own sections in System Settings.
:::

View File

@@ -10,7 +10,7 @@ Unable to set `app.immich:///oauth-callback` as a valid redirect URI? See [Mobil
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: 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://integrations.goauthentik.io/media/immich/) - [Authentik](https://goauthentik.io/integrations/sources/oauth/#openid-connect)
- [Authelia](https://www.authelia.com/integration/openid-connect/immich/) - [Authelia](https://www.authelia.com/integration/openid-connect/immich/)
- [Okta](https://www.okta.com/openid-connect/) - [Okta](https://www.okta.com/openid-connect/)
- [Google](https://developers.google.com/identity/openid-connect/openid-connect) - [Google](https://developers.google.com/identity/openid-connect/openid-connect)
@@ -20,6 +20,7 @@ Immich supports 3rd party authentication via [OpenID Connect][oidc] (OIDC), an i
Before enabling OAuth in Immich, a new client application needs to be configured in the 3rd-party authentication server. While the specifics of this setup vary from provider to provider, the general approach should be the same. Before enabling OAuth in Immich, a new client application needs to be configured in the 3rd-party authentication server. While the specifics of this setup vary from provider to provider, the general approach should be the same.
1. Create a new (Client) Application 1. Create a new (Client) Application
1. The **Provider** type should be `OpenID Connect` or `OAuth2` 1. The **Provider** type should be `OpenID Connect` or `OAuth2`
2. The **Client type** should be `Confidential` 2. The **Client type** should be `Confidential`
3. The **Application** type should be `Web` 3. The **Application** type should be `Web`
@@ -28,6 +29,7 @@ Before enabling OAuth in Immich, a new client application needs to be configured
2. Configure Redirect URIs/Origins 2. Configure Redirect URIs/Origins
The **Sign-in redirect URIs** should include: The **Sign-in redirect URIs** should include:
- `app.immich:///oauth-callback` - 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/auth/login` - for logging in with OAuth from the Web Client
- `http://DOMAIN:PORT/user-settings` - for manually linking OAuth in the Web Client - `http://DOMAIN:PORT/user-settings` - for manually linking OAuth in the Web Client
@@ -35,17 +37,21 @@ Before enabling OAuth in Immich, a new client application needs to be configured
Redirect URIs should contain all the domains you will be using to access Immich. Some examples include: Redirect URIs should contain all the domains you will be using to access Immich. Some examples include:
Mobile Mobile
- `app.immich:///oauth-callback` (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 Localhost
- `http://localhost:2283/auth/login` - `http://localhost:2283/auth/login`
- `http://localhost:2283/user-settings` - `http://localhost:2283/user-settings`
Local IP Local IP
- `http://192.168.0.200:2283/auth/login` - `http://192.168.0.200:2283/auth/login`
- `http://192.168.0.200:2283/user-settings` - `http://192.168.0.200:2283/user-settings`
Hostname Hostname
- `https://immich.example.com/auth/login` - `https://immich.example.com/auth/login`
- `https://immich.example.com/user-settings` - `https://immich.example.com/user-settings`
@@ -62,9 +68,8 @@ Once you have a new OAuth client application configured, Immich can be configure
| Scope | string | openid email profile | Full list of scopes to send with the request (space delimited) | | Scope | string | openid email profile | Full list of scopes to send with the request (space delimited) |
| Signing Algorithm | string | RS256 | The algorithm used to sign the id token (examples: RS256, HS256) | | Signing Algorithm | string | RS256 | The algorithm used to sign the id token (examples: RS256, HS256) |
| Storage Label Claim | string | preferred_username | Claim mapping for the user's storage label**¹** | | Storage Label Claim | string | preferred_username | Claim mapping for the user's storage label**¹** |
| Role Claim | string | immich_role | Claim mapping for the user's role. (should return "user" or "admin")**¹** |
| Storage Quota Claim | string | immich_quota | Claim mapping for the user's storage**¹** | | Storage Quota Claim | string | immich_quota | Claim mapping for the user's storage**¹** |
| Default Storage Quota (GiB) | number | 0 | Default quota for user without storage quota claim (empty for unlimited quota) | | Default Storage Quota (GiB) | number | 0 | Default quota for user without storage quota claim (Enter 0 for unlimited quota) |
| Button Text | string | Login with OAuth | Text for the OAuth button on the web | | Button Text | string | Login with OAuth | Text for the OAuth button on the web |
| Auto Register | boolean | true | When true, will automatically register a user the first time they sign in | | Auto Register | boolean | true | When true, will automatically register a user the first time they sign in |
| [Auto Launch](#auto-launch) | boolean | false | When true, will skip the login page and automatically start the OAuth login process | | [Auto Launch](#auto-launch) | boolean | false | When true, will skip the login page and automatically start the OAuth login process |
@@ -88,7 +93,7 @@ The `.well-known/openid-configuration` part of the url is optional and will be a
## Auto Launch ## Auto Launch
When Auto Launch is enabled, the login page will automatically redirect the user to the OAuth authorization url, to login with OAuth. To access the login screen again, use the browser's back button, or navigate directly to `/auth/login?autoLaunch=0`. When Auto Launch is enabled, the login page will automatically redirect the user to the OAuth authorization url, to login with OAuth. To access the login screen again, use the browser's back button, or navigate directly to `/auth/login?autoLaunch=0`.
Auto Launch can also be enabled on a per-request basis by navigating to `/auth/login?autoLaunch=1`, this can be useful in situations where Immich is called from e.g. Nextcloud using the _External sites_ app and the _oidc_ app so as to enable users to directly interact with a logged-in instance of Immich. Auto Launch can also be enabled on a per-request basis by navigating to `/auth/login?authLaunch=1`, this can be useful in situations where Immich is called from e.g. Nextcloud using the _External sites_ app and the _oidc_ app so as to enable users to directly interact with a logged-in instance of Immich.
## Mobile Redirect URI ## Mobile Redirect URI
@@ -106,89 +111,6 @@ Immich has a route (`/api/oauth/mobile-redirect`) that is already configured to
## Example Configuration ## Example Configuration
<details>
<summary>Authelia Example</summary>
### Authelia Example
Here's an example of OAuth configured for Authelia:
This assumes there exist an attribute `immichquota` in the user schema, which is used to set the user's storage quota in Immich.
The configuration concerning the quota is optional.
```yaml
authentication_backend:
ldap:
# The LDAP server configuration goes here.
# See: https://www.authelia.com/c/ldap
attributes:
extra:
immichquota: # The attribute name from LDAP
name: 'immich_quota'
multi_valued: false
value_type: 'integer'
identity_providers:
oidc:
## The other portions of the mandatory OpenID Connect 1.0 configuration go here.
## See: https://www.authelia.com/c/oidc
claims_policies:
immich_policy:
custom_claims:
immich_quota:
attribute: 'immich_quota'
scopes:
immich_scope:
claims:
- 'immich_quota'
clients:
- client_id: 'immich'
client_name: 'Immich'
# https://www.authelia.com/integration/openid-connect/frequently-asked-questions/#how-do-i-generate-a-client-identifier-or-client-secret
client_secret: $pbkdf2-sha512$310000$c8p78n7pUMln0jzvd4aK4Q$JNRBzwAo0ek5qKn50cFzzvE9RXV88h1wJn5KGiHrD0YKtZaR/nCb2CJPOsKaPK0hjf.9yHxzQGZziziccp6Yng'
public: false
require_pkce: false
redirect_uris:
- 'https://example.immich.app/auth/login'
- 'https://example.immich.app/user-settings'
- 'app.immich:///oauth-callback'
scopes:
- 'openid'
- 'profile'
- 'email'
- 'immich_scope'
claims_policy: 'immich_policy'
response_types:
- 'code'
grant_types:
- 'authorization_code'
id_token_signed_response_alg: 'RS256'
userinfo_signed_response_alg: 'RS256'
token_endpoint_auth_method: 'client_secret_post'
```
Configuration of OAuth in Immich System Settings
| Setting | Value |
| ---------------------------------- | ------------------------------------------------------------------- |
| Issuer URL | `https://example.immich.app/.well-known/openid-configuration` |
| Client ID | immich |
| Client Secret | 0v89FXkQOWO\***\*\*\*\*\***\*\*\***\*\*\*\*\***mprbvXD549HH6s1iw... |
| Token Endpoint Auth Method | client_secret_post |
| Scope | openid email profile immich_scope |
| ID Token Signed Response Algorithm | RS256 |
| Userinfo Signed Response Algorithm | RS256 |
| Storage Label Claim | uid |
| Storage Quota Claim | immich_quota |
| Default Storage Quota (GiB) | 0 (empty for unlimited quota) |
| Button Text | Sign in with Authelia (optional) |
| Auto Register | Enabled (optional) |
| Auto Launch | Enabled (optional) |
| Mobile Redirect URI Override | Disable |
| Mobile Redirect URI | |
</details>
<details> <details>
<summary>Authentik Example</summary> <summary>Authentik Example</summary>
@@ -211,7 +133,7 @@ Configuration of OAuth in Immich System Settings
| Signing Algorithm | RS256 | | Signing Algorithm | RS256 |
| Storage Label Claim | preferred_username | | Storage Label Claim | preferred_username |
| Storage Quota Claim | immich_quota | | Storage Quota Claim | immich_quota |
| Default Storage Quota (GiB) | 0 (empty for unlimited quota) | | Default Storage Quota (GiB) | 0 (0 for unlimited quota) |
| Button Text | Sign in with Authentik (optional) | | Button Text | Sign in with Authentik (optional) |
| Auto Register | Enabled (optional) | | Auto Register | Enabled (optional) |
| Auto Launch | Enabled (optional) | | Auto Launch | Enabled (optional) |
@@ -242,7 +164,7 @@ Configuration of OAuth in Immich System Settings
| Signing Algorithm | RS256 | | Signing Algorithm | RS256 |
| Storage Label Claim | preferred_username | | Storage Label Claim | preferred_username |
| Storage Quota Claim | immich_quota | | Storage Quota Claim | immich_quota |
| Default Storage Quota (GiB) | 0 (empty for unlimited quota) | | Default Storage Quota (GiB) | 0 (0 for unlimited quota) |
| Button Text | Sign in with Google (optional) | | Button Text | Sign in with Google (optional) |
| Auto Register | Enabled (optional) | | Auto Register | Enabled (optional) |
| Auto Launch | Enabled | | Auto Launch | Enabled |

View File

@@ -2,6 +2,10 @@
Users can deploy a custom reverse proxy that forwards requests to Immich. This way, the reverse proxy can handle TLS termination, load balancing, or other advanced features. All reverse proxies between Immich and the user must forward all headers and set the `Host`, `X-Real-IP`, `X-Forwarded-Proto` and `X-Forwarded-For` headers to their appropriate values. Additionally, your reverse proxy should allow for big enough uploads. By following these practices, you ensure that all custom reverse proxies are fully compatible with Immich. Users can deploy a custom reverse proxy that forwards requests to Immich. This way, the reverse proxy can handle TLS termination, load balancing, or other advanced features. All reverse proxies between Immich and the user must forward all headers and set the `Host`, `X-Real-IP`, `X-Forwarded-Proto` and `X-Forwarded-For` headers to their appropriate values. Additionally, your reverse proxy should allow for big enough uploads. By following these practices, you ensure that all custom reverse proxies are fully compatible with Immich.
:::note
The Repair page can take a long time to load. To avoid server timeouts or errors, we recommend specifying a timeout of at least 10 minutes on your proxy server.
:::
:::caution :::caution
Immich does not support being served on a sub-path such as `location /immich {`. It has to be served on the root path of a (sub)domain. Immich does not support being served on a sub-path such as `location /immich {`. It has to be served on the root path of a (sub)domain.
::: :::

View File

@@ -2,17 +2,16 @@
The `immich-server` docker image comes preinstalled with an administrative CLI (`immich-admin`) that supports the following commands: The `immich-server` docker image comes preinstalled with an administrative CLI (`immich-admin`) that supports the following commands:
| Command | Description | | Command | Description |
| ------------------------ | ------------------------------------------------------------- | | ------------------------ | ------------------------------------- |
| `help` | Display help | | `help` | Display help |
| `reset-admin-password` | Reset the password for the admin user | | `reset-admin-password` | Reset the password for the admin user |
| `disable-password-login` | Disable password login | | `disable-password-login` | Disable password login |
| `enable-password-login` | Enable password login | | `enable-password-login` | Enable password login |
| `enable-oauth-login` | Enable OAuth login | | `enable-oauth-login` | Enable OAuth login |
| `disable-oauth-login` | Disable OAuth login | | `disable-oauth-login` | Disable OAuth login |
| `list-users` | List Immich users | | `list-users` | List Immich users |
| `version` | Print Immich version | | `version` | Print Immich version |
| `change-media-location` | Change database file paths to align with a new media location |
## How to run a command ## How to run a command
@@ -89,21 +88,3 @@ Print Immich Version
immich-admin version immich-admin version
v1.129.0 v1.129.0
``` ```
Change media location
```
immich-admin change-media-location
? Enter the previous value of IMMICH_MEDIA_LOCATION: /data
? Enter the new value of IMMICH_MEDIA_LOCATION: /my-data
...
Previous value: /data
Current value: /my-data
Changing database paths from "/data/*" to "/my-data/*"
? Do you want to proceed? [Y/n] y
Database file paths updated successfully! 🎉
...
```

View File

@@ -5,7 +5,7 @@ After making any changes in the `server/src/schema`, a database migration need t
1. Run the command 1. Run the command
```bash ```bash
pnpm run migrations:generate <migration-name> npm run migrations:generate <migration-name>
``` ```
2. Check if the migration file makes sense. 2. Check if the migration file makes sense.

View File

@@ -7,7 +7,7 @@ sidebar_position: 3
Dev Containers provide a consistent, reproducible development environment using Docker containers. With a single click, you can get started with an Immich development environment on Mac, Linux, Windows, or in the cloud using GitHub Codespaces. Dev Containers provide a consistent, reproducible development environment using Docker containers. With a single click, you can get started with an Immich development environment on Mac, Linux, Windows, or in the cloud using GitHub Codespaces.
Get started fast! [![Open in VSCode Containers](https://img.shields.io/static/v1?label=VSCode%20DevContainer&message=Immich&color=blue)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/immich-app/immich/)
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/immich-app/immich/) [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/immich-app/immich/)
@@ -71,7 +71,7 @@ cd immich
The immich dev containers read environment variables from your shell environment, not from `.env` files. This allows them to work in cloud environments without pre-configuration. The immich dev containers read environment variables from your shell environment, not from `.env` files. This allows them to work in cloud environments without pre-configuration.
:::important Configuration :::important Required Configuration
When running locally, and if you want to create (or use an existing) DB and/or photo storage folder, you must set the `UPLOAD_LOCATION` variable in your shell environment before launching the Dev Container. This determines where uploaded files are stored and also where the DB stores it data. When running locally, and if you want to create (or use an existing) DB and/or photo storage folder, you must set the `UPLOAD_LOCATION` variable in your shell environment before launching the Dev Container. This determines where uploaded files are stored and also where the DB stores it data.
```bash ```bash
@@ -88,10 +88,6 @@ source ~/.bashrc
### Step 3: Launch the Dev Container ### Step 3: Launch the Dev Container
:::tip
Immich development makes extensive use of specialized [base images](https://github.com/immich-app/base-images) for its docker-compose based development. For this reason, you won't be able to use VSCode's **_Clone Repository in a Container Volume_** command.
:::
#### Using VS Code UI: #### Using VS Code UI:
1. Open the cloned repository in VS Code 1. Open the cloned repository in VS Code
@@ -203,11 +199,13 @@ To use your SSH key for commit signing, see the [GitHub guide on SSH commit sign
When the Dev Container starts, it automatically: When the Dev Container starts, it automatically:
1. **Runs post-create script** (`container-server-post-create.sh`): 1. **Runs post-create script** (`container-server-post-create.sh`):
- Adjusts file permissions for the `node` user - Adjusts file permissions for the `node` user
- Installs dependencies: `pnpm install` in all packages - Installs dependencies: `npm install` in all packages
- Builds TypeScript SDK: `pnpm run build` in `open-api/typescript-sdk` - Builds TypeScript SDK: `npm run build` in `open-api/typescript-sdk`
2. **Starts development servers** via VS Code tasks: 2. **Starts development servers** via VS Code tasks:
- `Immich API Server (Nest)` - API server with hot-reloading on port 2283 - `Immich API Server (Nest)` - API server with hot-reloading on port 2283
- `Immich Web Server (Vite)` - Web frontend with hot-reloading on port 3000 - `Immich Web Server (Vite)` - Web frontend with hot-reloading on port 3000
- Both servers watch for file changes and recompile automatically - Both servers watch for file changes and recompile automatically
@@ -243,7 +241,7 @@ To connect the mobile app to your Dev Container:
- **Server code** (`/server`): Changes trigger automatic restart - **Server code** (`/server`): Changes trigger automatic restart
- **Web code** (`/web`): Changes trigger hot module replacement - **Web code** (`/web`): Changes trigger hot module replacement
- **Database migrations**: Run `pnpm run sync:sql` in the server directory - **Database migrations**: Run `npm run sync:sql` in the server directory
- **API changes**: Regenerate TypeScript SDK with `make open-api` - **API changes**: Regenerate TypeScript SDK with `make open-api`
## Testing ## Testing
@@ -273,19 +271,19 @@ make test-medium-dev # End-to-end tests
```bash ```bash
# Server tests # Server tests
cd /workspaces/immich/server cd /workspaces/immich/server
pnpm test # Run all tests npm test # Run all tests
pnpm run test:watch # Watch mode npm run test:watch # Watch mode
pnpm run test:cov # Coverage report npm run test:cov # Coverage report
# Web tests # Web tests
cd /workspaces/immich/web cd /workspaces/immich/web
pnpm test # Run all tests npm test # Run all tests
pnpm run test:watch # Watch mode npm run test:watch # Watch mode
# E2E tests # E2E tests
cd /workspaces/immich/e2e cd /workspaces/immich/e2e
pnpm run test # Run API tests npm run test # Run API tests
pnpm run test:web # Run web UI tests npm run test:web # Run web UI tests
``` ```
### Code Quality Commands ### Code Quality Commands
@@ -337,12 +335,14 @@ make install-all # Install all dependencies
The Dev Container is pre-configured for debugging: The Dev Container is pre-configured for debugging:
1. **API Server Debugging**: 1. **API Server Debugging**:
- Set breakpoints in VS Code - Set breakpoints in VS Code
- Press `F5` or use "Run and Debug" panel - Press `F5` or use "Run and Debug" panel
- Select "Attach to Server" configuration - Select "Attach to Server" configuration
- Debug port: 9231 - Debug port: 9231
2. **Worker Debugging**: 2. **Worker Debugging**:
- Use "Attach to Workers" configuration - Use "Attach to Workers" configuration
- Debug port: 9230 - Debug port: 9230
@@ -428,6 +428,7 @@ While the Dev Container focuses on server and web development, you can connect m
``` ```
2. **Configure mobile app**: 2. **Configure mobile app**:
- Server URL: `http://YOUR_IP:2283/api` - Server URL: `http://YOUR_IP:2283/api`
- Ensure firewall allows port 2283 - Ensure firewall allows port 2283

View File

@@ -8,47 +8,34 @@ When contributing code through a pull request, please check the following:
## Web Checks ## Web Checks
- [ ] `pnpm run lint` (linting via ESLint) - [ ] `npm run lint` (linting via ESLint)
- [ ] `pnpm run format` (formatting via Prettier) - [ ] `npm run format` (formatting via Prettier)
- [ ] `pnpm run check:svelte` (Type checking via SvelteKit) - [ ] `npm run check:svelte` (Type checking via SvelteKit)
- [ ] `pnpm run check:typescript` (check typescript) - [ ] `npm run check:typescript` (check typescript)
- [ ] `pnpm test` (unit tests) - [ ] `npm test` (unit tests)
## Documentation ## Documentation
- [ ] `pnpm run format` (formatting via Prettier) - [ ] `npm run format` (formatting via Prettier)
- [ ] Update the `_redirects` file if you have renamed a page or removed it from the documentation. - [ ] Update the `_redirects` file if you have renamed a page or removed it from the documentation.
:::tip AIO :::tip AIO
Run all web checks with `pnpm run check:all` Run all web checks with `npm run check:all`
::: :::
## Server Checks ## Server Checks
- [ ] `pnpm run lint` (linting via ESLint) - [ ] `npm run lint` (linting via ESLint)
- [ ] `pnpm run format` (formatting via Prettier) - [ ] `npm run format` (formatting via Prettier)
- [ ] `pnpm run check` (Type checking via `tsc`) - [ ] `npm run check` (Type checking via `tsc`)
- [ ] `pnpm test` (unit tests) - [ ] `npm test` (unit tests)
:::tip AIO :::tip AIO
Run all server checks with `pnpm run check:all` Run all server checks with `npm run check:all`
::: :::
:::info Auto Fix :::info Auto Fix
You can use `pnpm run __:fix` to potentially correct some issues automatically for `pnpm run format` and `lint`. You can use `npm run __:fix` to potentially correct some issues automatically for `npm run format` and `lint`.
:::
## Mobile Checks
The following commands must be executed from within the mobile app directory of the codebase.
- [ ] `make build` (auto-generate files using build_runner)
- [ ] `make analyze` (static analysis via Dart Analyzer and DCM)
- [ ] `make format` (formatting via Dart Formatter)
- [ ] `make test` (unit tests)
:::info Auto Fix
You can use `dart fix --apply` and `dcm fix lib` to potentially correct some issues automatically for `make analyze`.
::: :::
## OpenAPI ## OpenAPI

View File

@@ -54,20 +54,20 @@ You can access the web from `http://your-machine-ip:3000` or `http://localhost:3
If you only want to do web development connected to an existing, remote backend, follow these steps: If you only want to do web development connected to an existing, remote backend, follow these steps:
1. Build the Immich SDK - `cd open-api/typescript-sdk && pnpm i && pnpm run build && cd -` 1. Build the Immich SDK - `cd open-api/typescript-sdk && npm i && npm run build && cd -`
2. Enter the web directory - `cd web/` 2. Enter the web directory - `cd web/`
3. Install web dependencies - `pnpm i` 3. Install web dependencies - `npm i`
4. Start the web development server 4. Start the web development server
```bash ```bash
IMMICH_SERVER_URL=https://demo.immich.app/ pnpm run dev IMMICH_SERVER_URL=https://demo.immich.app/ npm run dev
``` ```
If you're using PowerShell on Windows you may need to set the env var separately like so: If you're using PowerShell on Windows you may need to set the env var separately like so:
```powershell ```powershell
$env:IMMICH_SERVER_URL = "https://demo.immich.app/" $env:IMMICH_SERVER_URL = "https://demo.immich.app/"
pnpm run dev npm run dev
``` ```
#### `@immich/ui` #### `@immich/ui`
@@ -75,12 +75,12 @@ pnpm run dev
To see local changes to `@immich/ui` in Immich, do the following: To see local changes to `@immich/ui` in Immich, do the following:
1. Install `@immich/ui` as a sibling to `immich/`, for example `/home/user/immich` and `/home/user/ui` 1. Install `@immich/ui` as a sibling to `immich/`, for example `/home/user/immich` and `/home/user/ui`
2. Build the `@immich/ui` project via `pnpm run build` 2. Build the `@immich/ui` project via `npm run build`
3. Uncomment the corresponding volume in web service of the `docker/docker-compose.dev.yaml` file (`../../ui:/usr/ui`) 3. Uncomment the corresponding volume in web service of the `docker/docker-compose.dev.yaml` file (`../../ui:/usr/ui`)
4. Uncomment the corresponding alias in the `web/vite.config.js` file (`'@immich/ui': path.resolve(\_\_dirname, '../../ui')`) 4. Uncomment the corresponding alias in the `web/vite.config.js` file (`'@immich/ui': path.resolve(\_\_dirname, '../../ui')`)
5. Uncomment the import statement in `web/src/app.css` file `@import '/usr/ui/dist/theme/default.css';` and comment out `@import '@immich/ui/theme/default.css';` 5. Uncomment the import statement in `web/src/app.css` file `@import '/usr/ui/dist/theme/default.css';` and comment out `@import '@immich/ui/theme/default.css';`
6. Start up the stack via `make dev` 6. Start up the stack via `make dev`
7. After making changes in `@immich/ui`, rebuild it (`pnpm run build`) 7. After making changes in `@immich/ui`, rebuild it (`npm run build`)
### Mobile app ### Mobile app

View File

@@ -4,8 +4,8 @@
### Unit tests ### Unit tests
Unit are run by calling `pnpm run test` from the `server/` directory. Unit are run by calling `npm run test` from the `server/` directory.
You need to run `pnpm install` (in `server/`) before _once_. You need to run `npm install` (in `server/`) before _once_.
### End to end tests ### End to end tests
@@ -17,14 +17,14 @@ make e2e
Before you can run the tests, you need to run the following commands _once_: Before you can run the tests, you need to run the following commands _once_:
- `pnpm install` (in `e2e/`) - `npm install` (in `e2e/`)
- `make open-api` (in the project root `/`) - `make open-api` (in the project root `/`)
Once the test environment is running, the e2e tests can be run via: Once the test environment is running, the e2e tests can be run via:
```bash ```bash
cd e2e/ cd e2e/
pnpm test npm test
``` ```
The tests check various things including: The tests check various things including:

View File

@@ -2,7 +2,7 @@
Folder view provides an additional view besides the timeline that is similar to a file explorer. It allows you to navigate through the folders and files in the library. This feature is handy for a highly curated and customized external library or a nicely configured storage template. Folder view provides an additional view besides the timeline that is similar to a file explorer. It allows you to navigate through the folders and files in the library. This feature is handy for a highly curated and customized external library or a nicely configured storage template.
You can enable this feature under [`Account Settings > Features > Folders`](https://my.immich.app/user-settings?isOpen=feature+folders) You can enable this feature under [`Account Settings > Features > Folder View`](https://my.immich.app/user-settings?isOpen=feature+folders)
## Enable folder view ## Enable folder view

View File

@@ -56,9 +56,9 @@ Internally, Immich uses the [glob](https://www.npmjs.com/package/glob) package t
### Automatic watching (EXPERIMENTAL) ### Automatic watching (EXPERIMENTAL)
This feature 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. 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](#set-custom-scan-interval) to pull in your changes. 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.
#### Troubleshooting #### Troubleshooting
@@ -72,9 +72,7 @@ In rare cases, the library watcher can hang, preventing Immich from starting up.
### Nightly job ### Nightly job
There is an automatic scan job that is scheduled to run once a day. Its schedule is configurable, see [Set Custom Scan Interval](#set-custom-scan-interval). There is an automatic scan job that is scheduled to run once a day. This job also cleans up any libraries stuck in deletion. It is possible to trigger the cleanup by clicking "Scan all libraries" in the library management page.
This job also cleans up any libraries stuck in deletion. It is possible to trigger the cleanup by clicking "Scan all libraries" in the library management page.
## Usage ## Usage
@@ -93,7 +91,7 @@ The `immich-server` container will need access to the gallery. Modify your docke
```diff title="docker-compose.yml" ```diff title="docker-compose.yml"
immich-server: immich-server:
volumes: volumes:
- ${UPLOAD_LOCATION}:/data - ${UPLOAD_LOCATION}:/usr/src/app/upload
+ - /mnt/nas/christmas-trip:/mnt/media/christmas-trip:ro + - /mnt/nas/christmas-trip:/mnt/media/christmas-trip:ro
+ - /home/user/old-pics:/mnt/media/old-pics:ro + - /home/user/old-pics:/mnt/media/old-pics:ro
+ - /mnt/media/videos:/mnt/media/videos:ro + - /mnt/media/videos:/mnt/media/videos:ro
@@ -114,7 +112,7 @@ _Remember to run `docker compose up -d` to register the changes. Make sure you c
These actions must be performed by the Immich administrator. These actions must be performed by the Immich administrator.
- Click on your avatar in the upper right corner - Click on your avatar on the upper right corner
- Click on Administration -> External Libraries - Click on Administration -> External Libraries
- Click on Create an external library… - Click on Create an external library…
- Select which user owns the library, this can not be changed later - Select which user owns the library, this can not be changed later
@@ -161,7 +159,9 @@ Within seconds, the assets from the old-pics and videos folders should show up i
Folder view provides an additional view besides the timeline that is similar to a file explorer. It allows you to navigate through the folders and files in the library. This feature is handy for a highly curated and customized external library or a nicely configured storage template. Folder view provides an additional view besides the timeline that is similar to a file explorer. It allows you to navigate through the folders and files in the library. This feature is handy for a highly curated and customized external library or a nicely configured storage template.
You can enable this feature under [`Account Settings > Features > Folders`](https://my.immich.app/user-settings?isOpen=feature+folders) You can enable this feature under [`Account Settings > Features > Folder View`](https://my.immich.app/user-settings?isOpen=feature+folders)
The UI is currently only available for the web; mobile will come in a subsequent release.
<img src={require('./img/folder-view-1.webp').default} width="100%" title='Folder-view' /> <img src={require('./img/folder-view-1.webp').default} width="100%" title='Folder-view' />
@@ -171,7 +171,7 @@ You can enable this feature under [`Account Settings > Features > Folders`](http
Only an admin can do this. Only an admin can do this.
::: :::
You can define a custom interval for the trigger external library rescan under Administration -> Settings -> External Library. You can define a custom interval for the trigger external library rescan under Administration -> Settings -> Library.
You can set the scanning interval using the preset or cron format. For more information you can refer to [Crontab Guru](https://crontab.guru/). You can set the scanning interval using the preset or cron format. For more information you can refer to [Crontab Guru](https://crontab.guru/).
<img src={require('./img/library-custom-scan-interval.webp').default} width="75%" title='Set custom scan interval for external library' /> <img src={require('./img/library-custom-scan-interval.webp').default} width="75%" title='Set custom scan interval for external library' />

View File

@@ -88,9 +88,9 @@ 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. 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 ignore list by To overcome this limitation, the files must be removed from the blacklist by
App settings -> Advanced -> Duplicate Assets -> Clear App settings -> Advanced -> Duplicate Assets -> Clear
:::info :::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 ignore list again at the end of the synchronization. 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.
::: :::

View File

@@ -16,7 +16,7 @@ For the full list, refer to the [Immich source code](https://github.com/immich-a
| `HEIC` | `.heic` | :white_check_mark: | | | `HEIC` | `.heic` | :white_check_mark: | |
| `HEIF` | `.heif` | :white_check_mark: | | | `HEIF` | `.heif` | :white_check_mark: | |
| `JPEG 2000` | `.jp2` | :white_check_mark: | | | `JPEG 2000` | `.jp2` | :white_check_mark: | |
| `JPEG` | `.jpeg` `.jpg` `.jpe` `.insp` | :white_check_mark: | | | `JPEG` | `.webp` `.jpg` `.jpe` `.insp` | :white_check_mark: | |
| `JPEG XL` | `.jxl` | :white_check_mark: | | | `JPEG XL` | `.jxl` | :white_check_mark: | |
| `PNG` | `.png` | :white_check_mark: | | | `PNG` | `.png` | :white_check_mark: | |
| `PSD` | `.psd` | :white_check_mark: | Adobe Photoshop | | `PSD` | `.psd` | :white_check_mark: | Adobe Photoshop |

View File

@@ -27,11 +27,11 @@ After defining the locations of these files, we will edit the `docker-compose.ym
services: services:
immich-server: immich-server:
volumes: volumes:
- ${UPLOAD_LOCATION}:/data - ${UPLOAD_LOCATION}:/usr/src/app/upload
+ - ${THUMB_LOCATION}:/data/thumbs + - ${THUMB_LOCATION}:/usr/src/app/upload/thumbs
+ - ${ENCODED_VIDEO_LOCATION}:/data/encoded-video + - ${ENCODED_VIDEO_LOCATION}:/usr/src/app/upload/encoded-video
+ - ${PROFILE_LOCATION}:/data/profile + - ${PROFILE_LOCATION}:/usr/src/app/upload/profile
+ - ${BACKUP_LOCATION}:/data/backups + - ${BACKUP_LOCATION}:/usr/src/app/upload/backups
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
``` ```
@@ -44,7 +44,7 @@ docker compose up -d
:::note :::note
Because of the underlying properties of docker bind mounts, it is not recommended to mount the `upload/` and `library/` folders as separate bind mounts if they are on the same device. Because of the underlying properties of docker bind mounts, it is not recommended to mount the `upload/` and `library/` folders as separate bind mounts if they are on the same device.
For this reason, we mount the HDD or the network storage (NAS) to `/data` and then mount the folders we want to access under that folder. For this reason, we mount the HDD or the network storage (NAS) to `/usr/src/app/upload` and then mount the folders we want to access under that folder.
The `thumbs/` folder contains both the small thumbnails displayed in the timeline and the larger previews shown when clicking into an image. These cannot be separated. The `thumbs/` folder contains both the small thumbnails displayed in the timeline and the larger previews shown when clicking into an image. These cannot be separated.

View File

@@ -12,131 +12,105 @@ Run `docker exec -it immich_postgres psql --dbname=<DB_DATABASE_NAME> --username
## Assets ## Assets
### Name
:::note :::note
The `"originalFileName"` column is the name of the file at time of upload, including the extension. The `"originalFileName"` column is the name of the file at time of upload, including the extension.
::: :::
```sql title="Find by original filename" ```sql title="Find by original filename"
SELECT * FROM "asset" WHERE "originalFileName" = 'PXL_20230903_232542848.jpg'; SELECT * FROM "assets" WHERE "originalFileName" = 'PXL_20230903_232542848.jpg';
SELECT * FROM "asset" WHERE "originalFileName" LIKE 'PXL_%'; -- all files starting with PXL_ SELECT * FROM "assets" WHERE "originalFileName" LIKE 'PXL_%'; -- all files starting with PXL_
SELECT * FROM "asset" WHERE "originalFileName" LIKE '%_2023_%'; -- all files with _2023_ in the middle SELECT * FROM "assets" WHERE "originalFileName" LIKE '%_2023_%'; -- all files with _2023_ in the middle
``` ```
```sql title="Find by path" ```sql title="Find by path"
SELECT * FROM "asset" WHERE "originalPath" = 'upload/library/admin/2023/2023-09-03/PXL_2023.jpg'; SELECT * FROM "assets" WHERE "originalPath" = 'upload/library/admin/2023/2023-09-03/PXL_2023.jpg';
SELECT * FROM "asset" WHERE "originalPath" LIKE 'upload/library/admin/2023/%'; SELECT * FROM "assets" WHERE "originalPath" LIKE 'upload/library/admin/2023/%';
``` ```
### ID
```sql title="Find by ID" ```sql title="Find by ID"
SELECT * FROM "asset" WHERE "id" = '9f94e60f-65b6-47b7-ae44-a4df7b57f0e9'; SELECT * FROM "assets" WHERE "id" = '9f94e60f-65b6-47b7-ae44-a4df7b57f0e9';
``` ```
```sql title="Find by partial ID" ```sql title="Find by partial ID"
SELECT * FROM "asset" WHERE "id"::text LIKE '%ab431d3a%'; SELECT * FROM "assets" WHERE "id"::text LIKE '%ab431d3a%';
``` ```
### Checksum
:::note :::note
You can calculate the checksum for a particular file by using the command `sha1sum <filename>`. You can calculate the checksum for a particular file by using the command `sha1sum <filename>`.
::: :::
```sql title="Find by checksum (SHA-1)" ```sql title="Find by checksum (SHA-1)"
SELECT encode("checksum", 'hex') FROM "asset"; SELECT encode("checksum", 'hex') FROM "assets";
SELECT * FROM "asset" WHERE "checksum" = decode('69de19c87658c4c15d9cacb9967b8e033bf74dd1', 'hex'); SELECT * FROM "assets" WHERE "checksum" = decode('69de19c87658c4c15d9cacb9967b8e033bf74dd1', 'hex');
SELECT * FROM "asset" WHERE "checksum" = '\x69de19c87658c4c15d9cacb9967b8e033bf74dd1'; -- alternate notation SELECT * FROM "assets" WHERE "checksum" = '\x69de19c87658c4c15d9cacb9967b8e033bf74dd1'; -- alternate notation
``` ```
```sql title="Find duplicate assets with identical checksum (SHA-1) (excluding trashed files)" ```sql title="Find duplicate assets with identical checksum (SHA-1) (excluding trashed files)"
SELECT T1."checksum", array_agg(T2."id") ids FROM "asset" T1 SELECT T1."checksum", array_agg(T2."id") ids FROM "assets" T1
INNER JOIN "asset" T2 ON T1."checksum" = T2."checksum" AND T1."id" != T2."id" AND T2."deletedAt" IS NULL 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"; WHERE T1."deletedAt" IS NULL GROUP BY T1."checksum";
``` ```
### Metadata
```sql title="Live photos" ```sql title="Live photos"
SELECT * FROM "asset" WHERE "livePhotoVideoId" IS NOT NULL; SELECT * FROM "assets" WHERE "livePhotoVideoId" IS NOT NULL;
``` ```
```sql title="By description" ```sql title="By description"
SELECT "asset".*, "asset_exif"."description" FROM "asset_exif" SELECT "assets".*, "exif"."description" FROM "exif"
JOIN "asset" ON "asset"."id" = "asset_exif"."assetId" JOIN "assets" ON "assets"."id" = "exif"."assetId"
WHERE TRIM("asset_exif"."description") <> ''; -- all files with a description WHERE TRIM("exif"."description") <> ''; -- all files with a description
SELECT "asset".*, "asset_exif"."description" FROM "asset_exif" SELECT "assets".*, "exif"."description" FROM "exif"
JOIN "asset" ON "asset"."id" = "asset_exif"."assetId" JOIN "assets" ON "assets"."id" = "exif"."assetId"
WHERE "asset_exif"."description" ILIKE '%string to match%'; -- search by string WHERE "exif"."description" ILIKE '%string to match%'; -- search by string
``` ```
```sql title="Without metadata" ```sql title="Without metadata"
SELECT "asset".* FROM "asset_exif" SELECT "assets".* FROM "exif"
LEFT JOIN "asset" ON "asset"."id" = "asset_exif"."assetId" LEFT JOIN "assets" ON "assets"."id" = "exif"."assetId"
WHERE "asset_exif"."assetId" IS NULL; WHERE "exif"."assetId" IS NULL;
``` ```
```sql title="size < 100,000 bytes, smallest to largest" ```sql title="size < 100,000 bytes, smallest to largest"
SELECT * FROM "asset" SELECT * FROM "assets"
JOIN "asset_exif" ON "asset"."id" = "asset_exif"."assetId" JOIN "exif" ON "assets"."id" = "exif"."assetId"
WHERE "asset_exif"."fileSizeInByte" < 100000 WHERE "exif"."fileSizeInByte" < 100000
ORDER BY "asset_exif"."fileSizeInByte" ASC; ORDER BY "exif"."fileSizeInByte" ASC;
``` ```
### Type ```sql title="Without thumbnails"
SELECT * FROM "assets" WHERE "assets"."previewPath" IS NULL OR "assets"."thumbnailPath" IS NULL;
```
```sql title="By type" ```sql title="By type"
SELECT * FROM "asset" WHERE "asset"."type" = 'VIDEO'; SELECT * FROM "assets" WHERE "assets"."type" = 'VIDEO';
SELECT * FROM "asset" WHERE "asset"."type" = 'IMAGE'; SELECT * FROM "assets" WHERE "assets"."type" = 'IMAGE';
``` ```
```sql title="Count by type" ```sql title="Count by type"
SELECT "asset"."type", COUNT(*) FROM "asset" GROUP BY "asset"."type"; SELECT "assets"."type", COUNT(*) FROM "assets" GROUP BY "assets"."type";
``` ```
```sql title="Count by type (per user)" ```sql title="Count by type (per user)"
SELECT "user"."email", "asset"."type", COUNT(*) FROM "asset" SELECT "users"."email", "assets"."type", COUNT(*) FROM "assets"
JOIN "user" ON "asset"."ownerId" = "user"."id" JOIN "users" ON "assets"."ownerId" = "users"."id"
GROUP BY "asset"."type", "user"."email" ORDER BY "user"."email"; GROUP BY "assets"."type", "users"."email" ORDER BY "users"."email";
``` ```
## Tags ```sql title="Failed file movements"
SELECT * FROM "move_history";
```sql title="Count by tag"
SELECT "t"."value" AS "tag_name", COUNT(*) AS "number_assets" FROM "tag" "t"
JOIN "tag_asset" "ta" ON "t"."id" = "ta"."tagsId" JOIN "asset" "a" ON "ta"."assetsId" = "a"."id"
WHERE "a"."visibility" != 'hidden'
GROUP BY "t"."value" ORDER BY "number_assets" DESC;
```
```sql title="Count by tag (per user)"
SELECT "t"."value" AS "tag_name", "u"."email" as "user_email", COUNT(*) AS "number_assets" FROM "tag" "t"
JOIN "tag_asset" "ta" ON "t"."id" = "ta"."tagsId" JOIN "asset" "a" ON "ta"."assetsId" = "a"."id" JOIN "user" "u" ON "a"."ownerId" = "u"."id"
WHERE "a"."visibility" != 'hidden'
GROUP BY "t"."value", "u"."email" ORDER BY "number_assets" DESC;
``` ```
## Users ## Users
```sql title="List all users" ```sql title="List all users"
SELECT * FROM "user"; SELECT * FROM "users";
``` ```
```sql title="Get owner info from asset ID" ```sql title="Get owner info from asset ID"
SELECT "user".* FROM "user" JOIN "asset" ON "user"."id" = "asset"."ownerId" WHERE "asset"."id" = 'fa310b01-2f26-4b7a-9042-d578226e021f'; SELECT "users".* FROM "users" JOIN "assets" ON "users"."id" = "assets"."ownerId" WHERE "assets"."id" = 'fa310b01-2f26-4b7a-9042-d578226e021f';
``` ```
## Persons ## System Config
```sql title="Delete person and unset it for the faces it was associated with"
DELETE FROM "person" WHERE "name" = 'PersonNameHere';
```
## System
### Config
```sql title="Custom settings" ```sql title="Custom settings"
SELECT "key", "value" FROM "system_metadata" WHERE "key" = 'system-config'; SELECT "key", "value" FROM "system_metadata" WHERE "key" = 'system-config';
@@ -144,14 +118,10 @@ SELECT "key", "value" FROM "system_metadata" WHERE "key" = 'system-config';
(Only used when not using the [config file](/docs/install/config-file)) (Only used when not using the [config file](/docs/install/config-file))
### File properties ## Persons
```sql title="Without thumbnails" ```sql title="Delete person and unset it for the faces it was associated with"
SELECT * FROM "asset" WHERE "asset"."previewPath" IS NULL OR "asset"."thumbnailPath" IS NULL; DELETE FROM "person" WHERE "name" = 'PersonNameHere';
```
```sql title="Failed file movements"
SELECT * FROM "move_history";
``` ```
## Postgres internal ## Postgres internal

View File

@@ -12,7 +12,7 @@ If you want Immich to be able to delete the images in the external library or ad
```diff ```diff
immich-server: immich-server:
volumes: volumes:
- ${UPLOAD_LOCATION}:/data - ${UPLOAD_LOCATION}:/usr/src/app/upload
+ - /home/user/photos1:/home/user/photos1:ro + - /home/user/photos1:/home/user/photos1:ro
+ - /mnt/photos2:/mnt/photos2:ro # you can delete this line if you only have one mount point, or you can add more lines if you have more than two + - /mnt/photos2:/mnt/photos2:ro # you can delete this line if you only have one mount point, or you can add more lines if you have more than two
``` ```
@@ -41,7 +41,7 @@ In the Immich web UI:
- Click Add path - Click Add path
<img src={require('./img/add-path-button.webp').default} width="50%" title="Add Path button" /> <img src={require('./img/add-path-button.webp').default} width="50%" title="Add Path button" />
- Enter **/home/user/photos1** as the path and click Add - Enter **/usr/src/app/external** as the path and click Add
<img src={require('./img/add-path-field.webp').default} width="50%" title="Add Path field" /> <img src={require('./img/add-path-field.webp').default} width="50%" title="Add Path field" />
- Save the new path - Save the new path

View File

@@ -52,9 +52,9 @@ REMOTE_BACKUP_PATH="/path/to/remote/backup/directory"
### Local ### Local
# Backup Immich database # Backup Immich database
docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=<DB_USERNAME> > "$UPLOAD_LOCATION"/database-backup/immich-database.sql docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=postgres > "$UPLOAD_LOCATION"/database-backup/immich-database.sql
# For deduplicating backup programs such as Borg or Restic, compressing the content can increase backup size by making it harder to deduplicate. If you are using a different program or still prefer to compress, you can use the following command instead: # For deduplicating backup programs such as Borg or Restic, compressing the content can increase backup size by making it harder to deduplicate. If you are using a different program or still prefer to compress, you can use the following command instead:
# docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=<DB_USERNAME> | /usr/bin/gzip --rsyncable > "$UPLOAD_LOCATION"/database-backup/immich-database.sql.gz # docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=postgres | /usr/bin/gzip --rsyncable > "$UPLOAD_LOCATION"/database-backup/immich-database.sql.gz
### Append to local Borg repository ### Append to local Borg repository
borg create "$BACKUP_PATH/immich-borg::{now}" "$UPLOAD_LOCATION" --exclude "$UPLOAD_LOCATION"/thumbs/ --exclude "$UPLOAD_LOCATION"/encoded-video/ borg create "$BACKUP_PATH/immich-borg::{now}" "$UPLOAD_LOCATION" --exclude "$UPLOAD_LOCATION"/thumbs/ --exclude "$UPLOAD_LOCATION"/encoded-video/

View File

@@ -34,7 +34,7 @@ These environment variables are used by the `docker-compose.yml` file and do **N
| `TZ` | Timezone | <sup>\*1</sup> | server | microservices | | `TZ` | Timezone | <sup>\*1</sup> | server | microservices |
| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, 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_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>⚠️ | `/data` | server | 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 | | `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 | | | `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | |
| `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | | | `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | |
@@ -49,6 +49,9 @@ These environment variables are used by the `docker-compose.yml` file and do **N
\*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. \*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 needs to be set if the Immich deployment method is changing.
## Workers ## Workers
| Variable | Description | Default | Containers | | Variable | Description | Default | Containers |
@@ -69,25 +72,22 @@ Information on the current workers can be found [here](/docs/administration/jobs
## Database ## Database
| Variable | Description | Default | Containers | | Variable | Description | Default | Containers |
| :---------------------------------- | :------------------------------------------------------------------------------------- | :--------: | :----------------------------- | | :---------------------------------- | :--------------------------------------------------------------------------- | :--------: | :----------------------------- |
| `DB_URL` | Database URL | | server | | `DB_URL` | Database URL | | server |
| `DB_HOSTNAME` | Database host | `database` | server | | `DB_HOSTNAME` | Database host | `database` | server |
| `DB_PORT` | Database port | `5432` | server | | `DB_PORT` | Database port | `5432` | server |
| `DB_USERNAME` | Database user | `postgres` | server, database<sup>\*1</sup> | | `DB_USERNAME` | Database user | `postgres` | server, database<sup>\*1</sup> |
| `DB_PASSWORD` | Database password | `postgres` | server, database<sup>\*1</sup> | | `DB_PASSWORD` | Database password | `postgres` | server, database<sup>\*1</sup> |
| `DB_DATABASE_NAME` | Database name | `immich` | server, database<sup>\*1</sup> | | `DB_DATABASE_NAME` | Database name | `immich` | server, database<sup>\*1</sup> |
| `DB_SSL_MODE` | Database SSL mode | | server | | `DB_SSL_MODE` | Database SSL mode | | server |
| `DB_VECTOR_EXTENSION`<sup>\*2</sup> | Database vector extension (one of [`vectorchord`, `pgvector`, `pgvecto.rs`]) | | server | | `DB_VECTOR_EXTENSION`<sup>\*2</sup> | Database vector extension (one of [`vectorchord`, `pgvector`, `pgvecto.rs`]) | | server |
| `DB_SKIP_MIGRATIONS` | Whether to skip running migrations on startup (one of [`true`, `false`]) | `false` | server | | `DB_SKIP_MIGRATIONS` | Whether to skip running migrations on startup (one of [`true`, `false`]) | `false` | server |
| `DB_STORAGE_TYPE` | Optimize concurrent IO on SSDs or sequential IO on HDDs ([`SSD`, `HDD`])<sup>\*3</sup> | `SSD` | server |
\*1: The values of `DB_USERNAME`, `DB_PASSWORD`, and `DB_DATABASE_NAME` are passed to the Postgres container as the variables `POSTGRES_USER`, `POSTGRES_PASSWORD`, and `POSTGRES_DB` in `docker-compose.yml`. \*1: The values of `DB_USERNAME`, `DB_PASSWORD`, and `DB_DATABASE_NAME` are passed to the Postgres container as the variables `POSTGRES_USER`, `POSTGRES_PASSWORD`, and `POSTGRES_DB` in `docker-compose.yml`.
\*2: If not provided, the appropriate extension to use is auto-detected at startup by introspecting the database. When multiple extensions are installed, the order of preference is VectorChord, pgvecto.rs, pgvector. \*2: If not provided, the appropriate extension to use is auto-detected at startup by introspecting the database. When multiple extensions are installed, the order of preference is VectorChord, pgvecto.rs, pgvector.
\*3: Uses either [`postgresql.ssd.conf`](https://github.com/immich-app/base-images/blob/main/postgres/postgresql.ssd.conf) or [`postgresql.hdd.conf`](https://github.com/immich-app/base-images/blob/main/postgres/postgresql.hdd.conf) which mainly controls the Postgres `effective_io_concurrency` setting to allow for concurrenct IO on SSDs and sequential IO on HDDs.
:::info :::info
All `DB_` variables must be provided to all Immich workers, including `api` and `microservices`. All `DB_` variables must be provided to all Immich workers, including `api` and `microservices`.
@@ -199,11 +199,12 @@ Additional machine learning parameters can be tuned from the admin UI.
| `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_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 | | `IMMICH_TELEMETRY_EXCLUDE` | Do not collect these telemetries. List of `host`, `api`, `io`, `repo`, `job` | | server | api, microservices |
## Secrets ## Docker Secrets
The following variables support reading from files, either via [Systemd Credentials][systemd-creds] or [Docker secrets][docker-secrets] for additional security. The following variables support the use of [Docker secrets][docker-secrets] for additional security.
To use any of these, either set `CREDENTIALS_DIRECTORY` to a directory that contains files whose name is the regular variable” name, and whose content is the secret. If using Docker Secrets, setting `CREDENTIALS_DIRECTORY=/run/secrets` will cause all secrets present to be used. Alternatively, replace the regular variable with the equivalent `_FILE` environment variable as below. The value of the `_FILE` variable should be set to the path of a file containing the variable value. To use any of these, replace the regular environment variable with the equivalent `_FILE` environment variable. The value of
the `_FILE` variable should be set to the path of a file containing the variable value.
| Regular Variable | Equivalent Docker Secrets '\_FILE' Variable | | Regular Variable | Equivalent Docker Secrets '\_FILE' Variable |
| :----------------- | :------------------------------------------ | | :----------------- | :------------------------------------------ |
@@ -225,4 +226,3 @@ to use a Docker secret for the password in the Redis container.
[docker-secrets-docs]: https://github.com/docker-library/docs/tree/master/postgres#docker-secrets [docker-secrets-docs]: https://github.com/docker-library/docs/tree/master/postgres#docker-secrets
[docker-secrets]: https://docs.docker.com/engine/swarm/secrets/ [docker-secrets]: https://docs.docker.com/engine/swarm/secrets/
[ioredis]: https://ioredis.readthedocs.io/en/latest/README/#connect-to-redis [ioredis]: https://ioredis.readthedocs.io/en/latest/README/#connect-to-redis
[systemd-creds]: https://systemd.io/CREDENTIALS/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -2,9 +2,6 @@
sidebar_position: 80 sidebar_position: 80
--- ---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
# TrueNAS [Community] # TrueNAS [Community]
:::note :::note
@@ -12,324 +9,211 @@ This is a community contribution and not officially supported by the Immich team
Community support can be found in the dedicated channel on the [Discord Server](https://discord.immich.app/). Community support can be found in the dedicated channel on the [Discord Server](https://discord.immich.app/).
**Please report app issues to the corresponding [GitHub Repository](https://github.com/truenas/apps/tree/master/trains/community/immich).** **Please report app issues to the corresponding [Github Repository](https://github.com/truenas/apps/tree/master/trains/community/immich).**
:::
:::warning
This guide covers the installation of Immich on TrueNAS Community Edition 24.10.2.2 (Electric Eel) and later.
We recommend keeping TrueNAS Community Edition and Immich relatively up to date with the latest versions to avoid any issues.
If you are using an older version of TrueNAS, we ask that you upgrade to the latest version before installing Immich. Check the [TrueNAS Community Edition Release Notes](https://www.truenas.com/docs/softwarereleases/) for more information on breaking changes, new features, and how to upgrade your system.
::: :::
Immich can easily be installed on TrueNAS Community Edition via the **Community** train application. Immich can easily be installed on TrueNAS Community Edition via the **Community** train application.
Consider reviewing the TrueNAS [Apps resources](https://apps.truenas.com/getting-started/) if you have not previously configured applications on your system. Consider reviewing the TrueNAS [Apps resources](https://apps.truenas.com/getting-started/) if you have not previously configured applications on your system.
TrueNAS Community Edition makes installing and updating Immich easy, but you must use the Immich web portal and mobile app to configure accounts and access libraries.
## First Steps ## First Steps
The Immich app in TrueNAS Community Edition installs, completes the initial configuration, then starts the Immich web portal.
When updates become available, TrueNAS alerts and provides easy updates.
Before installing the Immich app in TrueNAS, review the [Environment Variables](#environment-variables) documentation to see if you want to configure any during installation.
You may also configure environment variables at any time after deploying the application.
### Setting up Storage Datasets ### Setting up Storage Datasets
Before beginning app installation, [create the datasets](https://www.truenas.com/docs/scale/scaletutorials/storage/datasets/datasetsscale/) to use in the **Storage Configuration** section during installation. Before beginning app installation, [create the datasets](https://www.truenas.com/docs/scale/scaletutorials/storage/datasets/datasetsscale/) to use in the **Storage Configuration** section during installation.
Immich requires seven datasets: `library`, `upload`, `thumbs`, `profile`, `video`, `backups`, and `pgData`.
In TrueNAS, Immich requires 2 datasets for the application to function correctly: `data` and `pgData`. You can set the datasets to any names to match your naming conventions or preferences. You can organize these as one parent with seven child datasets, for example `/mnt/tank/immich/library`, `/mnt/tank/immich/upload`, and so on.
You can organize these as one parent with two child datasets, for example `/mnt/tank/immich/data` and `/mnt/tank/immich/pgData`.
<img <img
src={require('./img/truenas/truenas00.webp').default} src={require('./img/truenas12.webp').default}
width="40%" width="30%"
alt="Immich App Widget" alt="Immich App Widget"
className="border rounded-xl" className="border rounded-xl"
/> />
:::info Datasets Permissions :::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 **pgData** dataset must be owned by the user `netdata` (UID 999) for Postgres to start. If the **library** dataset uses ACL it 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 **upload** to **library**, Immich performs `chmod` internally and needs to be allowed to execute the command. [More info.](https://github.com/immich-app/immich/pull/13017)
The `data` dataset must have given the **_modify_** permission to the user who will run Immich.
Since TrueNAS Community Edition 24.10.2.2 and later, Immich can be run as any user and group, the default user being `apps` (UID 568) and the default group being `apps` (GID 568). This user, either `apps` or another user you choose, must have **_modify_** permissions on the **data** dataset.
For an easy setup:
- Create the parent dataset `immich` keeping the default **Generic** preset.
- Select `Dataset Preset` **Apps** instead of **Generic** when creating the `data` dataset. This will automatically give the correct permissions to the dataset. If you want to use another user for Immich, you can keep the **Generic** preset, but you will need to give the **_modify_** permission to that other user.
- For the `pgData` dataset, you can keep the default preset **Generic** as permissions can be set during the installation of the Immich app (See [Storage Configuration](#storage-configuration) section).
:::
:::tip
To improve performance, Immich recommends using SSDs for the database. If you have a pool made of SSDs, you can create the `pgData` dataset on that pool.
Thumbnails can also be stored on the SSDs for faster access. This is an advanced option and not required for Immich to run. More information on how you can use multiple datasets to manage Immich storage in a finer-grained manner can be found in the [Advanced: Multiple Datasets for Immich Storage](#advanced-multiple-datasets-for-immich-storage) section below.
:::
:::warning
If you just created the datasets using the **Apps** preset, you can skip this warning section.
If the **data** dataset uses ACL it must have [ACL mode](https://www.truenas.com/docs/scale/scaletutorials/datasets/permissionsscale/) 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 **upload** to **library** (internal folder created by Immich within the **data** dataset), Immich performs `chmod` internally and must be allowed to execute the command. [More info.](https://github.com/immich-app/immich/pull/13017)
To change or verify the ACL mode, go to the **Datasets** screen, select the **library** dataset, click on the **Edit** button next to **Dataset Details**, then click on the **Advanced Options** tab, scroll down to the **ACL Mode** section, and select `Passthrough` from the dropdown menu. Click **Save** to apply the changes. If the option is greyed out, set the **ACL Type** to `SMB/NFSv4` first, then you can change the **ACL Mode** to `Passthrough`.
::: :::
## Installing the Immich Application ## Installing the Immich Application
To install the **Immich** application, go to **Apps**, click **Discover Apps**, and either begin typing Immich into the search field or scroll down to locate the **Immich** application widget. To install the **Immich** application, go to **Apps**, click **Discover Apps**, either begin typing Immich into the search field or scroll down to locate the **Immich** application widget.
<div style={{ marginBottom: '2rem', border: '1px solid #ccc', padding: '1rem', borderRadius: '8px' }}>
Click on the widget to open the **Immich** application details screen.
<img <img
src={require('./img/truenas/truenas01.webp').default} src={require('./img/truenas01.webp').default}
width="50%" width="50%"
alt="Immich App Widget" alt="Immich App Widget"
className="border rounded-xl" className="border rounded-xl"
/> />
</div> Click on the widget to open the **Immich** application details screen.
<div style={{ marginBottom: '2rem', border: '1px solid #ccc', padding: '1rem', borderRadius: '8px' }}> <br/><br/>
Click **Install** to open the Immich application configuration screen.
<img <img
src={require('./img/truenas/truenas02.webp').default} src={require('./img/truenas02.webp').default}
width="100%" width="100%"
alt="Immich App Details Screen" alt="Immich App Details Screen"
className="border rounded-xl" className="border rounded-xl"
/> />
</div> Click **Install** to open the Immich application configuration screen.
<br/><br/>
Application configuration settings are presented in several sections, each explained below. Application configuration settings are presented in several sections, each explained below.
To find specific fields, click in the **Search Input Fields** search field, scroll down to a particular section, or click on the section heading on the navigation area in the upper-right corner. To find specific fields click in the **Search Input Fields** search field, scroll down to a particular section or click on the section heading on the navigation area in the upper-right corner.
### Application Name and Version ### Application Name and Version
<img <img
src={require('./img/truenas/truenas03.webp').default} src={require('./img/truenas03.webp').default}
width="100%" width="100%"
alt="Install Immich Screen" alt="Install Immich Screen"
className="border rounded-xl mb-4" className="border rounded-xl"
/> />
Keep the default value or enter a name in the **Application Name** field. Accept the default value or enter a name in **Application Name** field.
Change it if youre deploying a second instance. In most cases use the default name, but if adding a second deployment of the application you must change this name.
Immich version within TrueNAS catalog (Different from Immich release version). Accept the default version number in **Version**.
When a new version becomes available, the application has an update badge.
The **Installed Applications** screen shows the option to update applications.
### Immich Configuration ### Immich Configuration
<img <img
src={require('./img/truenas/truenas04.webp').default} src={require('./img/truenas05.webp').default}
width="40%" width="40%"
alt="Configuration Settings" alt="Configuration Settings"
className="border rounded-xl mb-4"
/>
The **Timezone** is set to the system default, which usually matches your local timezone. You can change it to another timezone if you prefer.
**Enable Machine Learning** is enabled by default. It allows Immich to use machine learning features such as face recognition, image search, and smart duplicate detection. Untick this option if you do not want to use these features.
Select the **Machine Learning Image Type** based on the hardware you have. More details here: [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md)
**Database Password** should be set to a custom value using only the characters `A-Za-z0-9`. This password is used to secure the Postgres database.
**Redis Password** should be set to a custom value using only the characters `A-Za-z0-9`. Preferably, use a different password from the database password.
Keep the **Log Level** to the default `Log` value.
Leave **Hugging Face Endpoint** blank. (This is used to download ML models from a different source.)
Set **Database Storage Type** to the type of storage (**HDD** or **SSD**) that the pool where the **pgData** dataset is located uses.
**Additional Environment Variables** can be left blank.
<details>
<summary>Advanced users: Adding Environment Variables</summary>
Environment variables can be set by clicking the **Add** button and filling in the **Name** and **Value** fields.
<img
src={require('./img/truenas/truenas05.webp').default}
width="40%"
alt="Environment Variables"
className="border rounded-xl" className="border rounded-xl"
/> />
These are used to add custom configuration options or to enable specific features. Accept the default value in **Timezone** or change to match your local timezone.
More information on available environment variables can be found in the **[environment variables documentation](/docs/install/environment-variables/)**. **Timezone** is only used by the Immich `exiftool` microservice if it cannot be determined from the image metadata.
:::info Untick **Enable Machine Learning** if you will not use face recognition, image search, and smart duplicate detection.
Some environment variables are not available for the TrueNAS Community Edition app as they can be configured through GUI options in the [Edit Immich screen](#edit-app-settings).
Some examples are: `IMMICH_VERSION`, `UPLOAD_LOCATION`, `DB_DATA_LOCATION`, `TZ`, `IMMICH_LOG_LEVEL`, `DB_PASSWORD`, `REDIS_PASSWORD`. Accept the default option or select the **Machine Learning Image Type** for your hardware based on the [Hardware-Accelerated Machine Learning Supported Backends](/docs/features/ml-hardware-acceleration.md#supported-backends).
:::
</details> Immich's default is `postgres` but you should consider setting the **Database Password** to a custom value using only the characters `A-Za-z0-9`.
### User and Group Configuration The **Redis Password** should be set to a custom value using only the characters `A-Za-z0-9`.
Application in TrueNAS runs as a specific user and group. Immich uses the default user `apps` (UID 568) and the default group `apps` (GID 568). Accept the **Log Level** default of **Log**.
<img Leave **Hugging Face Endpoint** blank. (This is for downloading ML models from a different source.)
src={require('./img/truenas/truenas06.webp').default}
width="40%"
alt="User and Group Configuration"
className="border rounded-xl"
/>
- **User ID**: Keep the default value `apps` (UID 568) or define a different one if needed. Leave **Additional Environment Variables** blank or see [Environment Variables](#environment-variables) to set before installing.
- **Group ID**: Keep the default value `apps` (GID 568) or define a different one if needed.
:::warning
If you change the user or group, make sure that the datasets you created for Immich data storage have the correct permissions set for that user and group as specified in the [Setting up Storage Datasets](#setting-up-storage-datasets) section above.
:::
### Network Configuration ### Network Configuration
<img <img
src={require('./img/truenas/truenas07.webp').default} src={require('./img/truenas06.webp').default}
width="40%" width="40%"
alt="Networking Settings" alt="Networking Settings"
className="border rounded-xl" className="border rounded-xl"
/> />
- **Port Bind Mode**: This lets you expose the port to the host system, allowing you to access Immich from outside the TrueNAS system. Keep the default **_Publish port on the host for external access_** value unless you have a specific reason to change it. Accept the default port `30041` in **WebUI Port** or enter a custom port number.
:::info Allowed Port Numbers
Only numbers within the range 9000-65535 may be used on TrueNAS versions below TrueNAS Community Edition 24.10 Electric Eel.
- **Port Number**: Keep the default port `30041` or enter a custom port number. Regardless of version, to avoid port conflicts, don't use [ports on this list](https://www.truenas.com/docs/solutions/optimizations/security/#truenas-default-ports).
:::
- **Host IPs**: Leave the default blank value.
### Storage Configuration ### Storage Configuration
:::danger Default Settings (Not recommended) Immich requires seven storage datasets.
The default setting for datasets is **ixVolume (dataset created automatically by the system)**. This is not recommended as this results in your data being harder to access manually and can result in data loss if you delete the immich app. It is also harder to manage snapshots and replication tasks. It is recommended to use the **Host Path (Path that already exists on the system)** option instead.
:::
The storage configuration section allows you to set up the storage locations for Immich data. You can select the datasets created in the previous step.
<img <img
src={require('./img/truenas/truenas08.webp').default} src={require('./img/truenas07.webp').default}
width="40%" width="20%"
alt="Configure Storage Volumes" alt="Configure Storage ixVolumes"
className="border rounded-xl" className="border rounded-xl"
/> />
For the Data Storage, select **Host Path (Path that already exists on the system)** and then select the dataset you created for Immich data storage, for example, `data`. :::note Default Setting (Not recommended)
The default setting for datasets is **ixVolume (dataset created automatically by the system)** but this results in your data being harder to access manually and can result in data loss if you delete the immich app. (Not recommended)
The Machine Learning cache can be left with default _Temporary_
For the Postgres Data Storage, select **Host Path (Path that already exists on the system)** and then select the dataset you created for Postgres data storage, for example, `pgData`.
:::info
**Postgres Data Storage**
Once **Host Path** is selected, a checkbox appears with **_Automatic Permissions_**. If you have not set the ownership of the **pgData** dataset to `netdata` (UID 999), tick this box as it will set the user ownership to `netdata` (UID 999) and the group ownership to `docker` (GID 999) automatically. If you have set the ownership of the **pgData** dataset to `netdata` (UID 999), you can leave this box unticked.
::: :::
### Additional Storage (Advanced Users) For each Storage option select **Host Path (Path that already exists on the system)** and then select the matching dataset [created before installing the app](#setting-up-storage-datasets): **Immich Library Storage**: `library`, **Immich Uploads Storage**: `upload`, **Immich Thumbs Storage**: `thumbs`, **Immich Profile Storage**: `profile`, **Immich Video Storage**: `video`, **Immich Backups Storage**: `backups`, **Postgres Data Storage**: `pgData`.
<details> <img
<summary>External Libraries</summary> src={require('./img/truenas08.webp').default}
width="40%"
alt="Configure Storage Host Paths"
className="border rounded-xl"
/>
The image above has example values.
<br/>
### Additional Storage [(External Libraries)](/docs/features/libraries)
:::danger Advanced Users Only :::danger Advanced Users Only
This feature should only be used by advanced users. If this is your first time installing Immich, then DO NOT mount an external library until you have a working setup. This feature should only be used by advanced users. If this is your first time installing Immich, then DO NOT mount an external library until you have a working setup. Also, your mount path MUST be something unique and should NOT be your library or upload location or a Linux directory like `/lib`. The picture below shows a valid example.
::: :::
<img <img
src={require('./img/truenas/truenas09.webp').default} src={require('./img/truenas10.webp').default}
width="40%" width="40%"
alt="Add External Libraries with Additional Storage" alt="Configure Storage Host Paths"
className="border rounded-xl" className="border rounded-xl"
/> />
You may configure [external libraries](/docs/features/libraries) by mounting them using **Additional Storage**. You may configure [External Libraries](/docs/features/libraries) by mounting them using **Additional Storage**.
The **Mount Path** is the location you will need to copy and paste into the External Library settings within Immich.
The **Host Path** is the location on the TrueNAS Community Edition server where your external library is located.
The dataset that contains your external library files must at least give **read** access to the user running Immich (Default: `apps` (UID 568), `apps` (GID 568)). <!-- A section for Labels would go here but I don't know what they do. -->
If you want to be able to delete files or edit metadata in the external library using Immich, you will need to give the **modify** permission to the user running Immich.
- **Mount Path** is the location you will need to copy and paste into the external library settings within Immich.
- **Host Path** is the location on the TrueNAS Community Edition server where your external library is located.
- **Read Only** is a checkbox that you can tick if you want to prevent Immich from modifying the files in the external library. This is useful if you want to use Immich to view and search your external library without modifying it.
:::warning
Each mount path MUST be something unique and should NOT be your library or upload location or a Linux directory like `/lib`.
A general recommendation is to mount any external libraries to a path beginning with `/mnt` or `/media` followed by a unique name, such as `/mnt/external-libraries` or `/media/my-external-libraries`. If you plan to mount multiple external libraries, you can use paths like `/mnt/external-libraries/library1`, `/mnt/external-libraries/library2`, etc.
:::
</details>
<details>
<summary>Multiple Datasets for Immich Storage</summary>
:::danger Advanced Users Only
This feature should only be used by advanced users.
:::
Immich can use multiple datasets for its storage, allowing you to manage your data more granularly, similar to the old storage configuration. This is useful if you want to separate your data into different datasets for performance or organizational reasons. There is a general guide for this [here](/docs/guides/custom-locations), but read on for the TrueNAS guide.
Each additional dataset has to give the permission **_modify_** to the user who will run Immich (Default: `apps` (UID 568), `apps` (GID 568))
As described in the [Setting up Storage Datasets](#setting-up-storage-datasets) section above, you have to create the datasets with the **Apps** preset to ensure the correct permissions are set, or you can set the permissions manually after creating the datasets.
Immich uses 6 folders for its storage: `library`, `upload`, `thumbs`, `profile`, `encoded-video`, and `backups`. You can create a dataset for each of these folders or only for some of them.
To mount these datasets:
1. Add an **Additional Storage** entry for each dataset you want to use.
2. Select **Type** as **Host Path (Path that already exists on the system)**.
3. Enter the **Mount Path** with `/data/<folder-name>`. The `<folder-name>` is the name of the folder you want to mount, for example, `library`, `upload`, `thumbs`, `profile`, `encoded-video`, or `backups`.
:::danger Important
You have to write the full path, including `/data/`, as Immich expects the data to be in that location.
If you do not include this path, Immich will not be able to find the data and will not write the data to the location you specified.
:::
4. Select the **Host Path** as the dataset you created for that folder, for example, `/mnt/tank/immich/library`, `/mnt/tank/immich/upload`, etc.
<img
src={require('./img/truenas/truenas10.webp').default}
width="40%"
alt="Use Multiple Datasets for Immich Storage with Additional Storage"
className="border rounded-xl"
/>
</details>
<!-- A section for Labels could be added, but I don't think it is needed as they are of no use for Immich. -->
### Resources Configuration ### Resources Configuration
<img <img
src={require('./img/truenas/truenas11.webp').default} src={require('./img/truenas09.webp').default}
width="40%" width="40%"
alt="Resource Limits"
className="border rounded-xl" className="border rounded-xl"
/> />
- **CPU**: Depending on your system resources, you can keep the default value of `2` threads or specify a different number. Immich recommends at least `8` threads. Accept the default **CPU** limit of `2` threads or specify the number of threads (CPUs with Multi-/Hyper-threading have 2 threads per core).
- **Memory**: Limit in MB of RAM. Immich recommends at least 6000 MB (6GB). If you selected **Enable Machine Learning** in **Immich Configuration**, you should probably set this above 8000 MB. Specify the **Memory** limit in MB of RAM. Immich recommends at least 6000 MB (6GB). If you selected **Enable Machine Learning** in **Immich Configuration**, you should probably set this above 8000 MB.
Both **CPU** and **Memory** are limits, not reservations. This means that Immich can use up to the specified amount of CPU threads and RAM, but it will not reserve that amount of resources at all times. The system will allocate resources as needed, and Immich will use less than the specified amount most of the time. :::info Older TrueNAS Versions
Before TrueNAS Community Edition version 24.10 Electric Eel:
- Enable **GPU Configuration** options if you have a GPU or CPU with integrated graphics that you will use for [Hardware Transcoding](/docs/features/hardware-transcoding) and/or [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md). The **CPU** value was specified in a different format with a default of `4000m` which is 4 threads.
The process for NVIDIA GPU passthrough requires additional steps. The **Memory** value was specified in a different format with a default of `8Gi` which is 8 GiB of RAM. The value was specified in bytes or a number with a measurement suffix. Examples: `129M`, `123Mi`, `1000000000`
More details here: [GPU Passthrough Docs for TrueNAS Apps](https://apps.truenas.com/managing-apps/installing-apps/#gpu-passthrough) :::
Enable **GPU Configuration** options if you have a GPU that you will use for [Hardware Transcoding](/docs/features/hardware-transcoding) and/or [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md). More info: [GPU Passthrough Docs for TrueNAS Apps](https://apps.truenas.com/managing-apps/installing-apps/#gpu-passthrough)
### Install ### Install
Finally, click **Install**. Finally, click **Install**.
The system opens the **Installed Applications** screen with the Immich app in the **Deploying** state. The system opens the **Installed Applications** screen with the Immich app in the **Deploying** state.
When the installation completes, it changes to **Running**. When the installation completes it changes to **Running**.
<img <img
src={require('./img/truenas/truenas12.webp').default} src={require('./img/truenas04.webp').default}
width="100%" width="100%"
alt="Immich Installed" alt="Immich Installed"
className="border rounded-xl" className="border rounded-xl"
/> />
Click **Web Portal** on the **Application Info** widget, or go to the URL `http://<your-truenas-ip>:30041` in your web browser to open the Immich web interface. This will show you the onboarding process to set up your first user account, which will be an administrator account. Click **Web Portal** on the **Application Info** widget to open the Immich web interface to set up your account and begin uploading photos.
After that, you can start using Immich to upload and manage your photos and videos.
:::tip :::tip
For more information on how to use the application once installed, please refer to the [Post Install](/docs/install/post-install.mdx) guide. For more information on how to use the application once installed, please refer to the [Post Install](/docs/install/post-install.mdx) guide.
@@ -344,6 +228,23 @@ For more information on how to use the application once installed, please refer
- Click **Update** at the very bottom of the page to save changes. - Click **Update** at the very bottom of the page to save changes.
- TrueNAS automatically updates, recreates, and redeploys the Immich container with the updated settings. - TrueNAS automatically updates, recreates, and redeploys the Immich container with the updated settings.
## Environment Variables
You can set [Environment Variables](/docs/install/environment-variables) by clicking **Add** on the **Additional Environment Variables** option and filling in the **Name** and **Value**.
<img
src={require('./img/truenas11.webp').default}
width="40%"
alt="Environment Variables"
className="border rounded-xl"
/>
:::info
Some Environment Variables are not available for the TrueNAS Community Edition app. This is mainly because they can be configured through GUI options in the [Edit Immich screen](#edit-app-settings).
Some examples are: `IMMICH_VERSION`, `UPLOAD_LOCATION`, `DB_DATA_LOCATION`, `TZ`, `IMMICH_LOG_LEVEL`, `DB_PASSWORD`, `REDIS_PASSWORD`.
:::
## Updating the App ## Updating the App
:::danger :::danger
@@ -360,116 +261,3 @@ To update the app to the latest version:
- You may view the Changelog. - You may view the Changelog.
- Click **Upgrade** to begin the process and open a counter dialog that shows the upgrade progress. - Click **Upgrade** to begin the process and open a counter dialog that shows the upgrade progress.
- When complete, the update badge and buttons disappear and the application Update state on the Installed screen changes from Update Available to Up to date. - When complete, the update badge and buttons disappear and the application Update state on the Installed screen changes from Update Available to Up to date.
## Migration
:::danger
Perform a backup of your Immich data before proceeding with the migration steps below. This is crucial to prevent any data loss if something goes wrong during the migration process.
The migration should also be performed when the Immich app is not running to ensure no data is being written while you are copying the data.
:::
### Migration from Old Storage Configuration
There are two ways to migrate from the old storage configuration to the new one, depending on whether you want to keep the old multiple datasets or if you want to move to a double dataset configuration with a single dataset for Immich data storage and a single dataset for Postgres data storage.
:::note Old TrueNAS Versions Permissions
If you were using an older version of TrueNAS (before 24.10.2.2), the datasets, except the one for **pgData** had only to be owned by the `root` user (UID 0). You might have to add the **modify** permission to the `apps` user (UID 568) or the user you want to run Immich as, to all of them, except **pgData**. The steps to add or change ACL permissions are described in the [TrueNAS documentation](https://www.truenas.com/docs/scale/scaletutorials/datasets/permissionsscale/).
:::
<Tabs groupId="truenas-migration-tabs">
<TabItem value="migrate-new-dataset" label="Migrate data to a new dataset (recommended)" default>
To migrate from the old storage configuration to the new one, you will need to create a new dataset for the Immich data storage and copy the data from the old datasets to the new ones. The steps are as follows:
1. **Stop the Immich app** from the TrueNAS web interface to ensure no data is being written while you are copying the data.
2. **Create a new dataset** for the Immich data storage, for example, `data`. As described in the [Setting up Storage Datasets](#setting-up-storage-datasets) section above, create the dataset with the **Apps** preset to ensure the correct permissions are set.
3. **Copy the data** from the old datasets to the new dataset. We advise using the `rsync` command to copy the data, as it will preserve the permissions and ownership of the files. The following commands are examples:
```bash
rsync -av /mnt/tank/immich/library/ /mnt/tank/immich/data/library/
rsync -av /mnt/tank/immich/upload/ /mnt/tank/immich/data/upload/
rsync -av /mnt/tank/immich/thumbs/ /mnt/tank/immich/data/thumbs/
rsync -av /mnt/tank/immich/profile/ /mnt/tank/immich/data/profile/
rsync -av /mnt/tank/immich/video/ /mnt/tank/immich/data/encoded-video/
rsync -av /mnt/tank/immich/backups/ /mnt/tank/immich/data/backups/
```
Make sure to replace `/mnt/tank/immich/` with the correct path to your old datasets and `/mnt/tank/immich/data/` with the correct path to your new dataset.
:::tip
If you were using **ixVolume (dataset created automatically by the system)** for Immich data storage, the path to the data should be `/mnt/.ix-apps/app_mounts/immich/`. You have to use this path instead of `/mnt/tank/immich/` in the `rsync` command above, for example:
```bash
rsync -av /mnt/.ix-apps/app_mounts/immich/library/ /mnt/tank/immich/data/library/
```
If you were also using an ixVolume for Postgres data storage, you also should, first create the pgData dataset, as described in the [Setting up Storage Datasets](#setting-up-storage-datasets) section above, and then you can use the following command to copy the Postgres data:
```bash
rsync -av /mnt/.ix-apps/app_mounts/immich/pgData/ /mnt/tank/immich/pgData/
```
:::
:::warning
Make sure that for each folder, the `.immich` file is copied as well, as it contains important metadata for Immich. If for some reason the `.immich` file is not copied, you can copy it manually with the `rsync` command, for example:
```bash
rsync -av /mnt/tank/immich/library/.immich /mnt/tank/immich/data/library/
```
Replace `library` with the name of the folder where you are copying the file.
:::
4. **Update the permissions** as the permissions of the data that have been copied has been preserved, to ensure that the `apps` user (UID 568) has the correct permissions on all the copied data. If you just created the dataset with the **Apps** preset, from the TrueNAS web interface, go to the **Datasets** screen, select the **data** dataset, click on the **Edit** button next to **Permissions**, tick the "Apply permissions recursively" checkbox, and click **Save**. This will apply the correct permissions to all the copied data.
5. **Update the Immich app** to use the new dataset:
- Go to the **Installed Applications** screen and select Immich from the list of installed applications.
- Click **Edit** on the **Application Info** widget.
- In the **Storage Configuration** section, untick the **Use Old Storage Configuration (Deprecated)** checkbox.
- For the **Data Storage**, select **Host Path (Path that already exists on the system)** and then select the new dataset you created for Immich data storage, for example, `data`.
- For the **Postgres Data Storage**, verify that it is still set to the dataset you created for Postgres data storage, for example, `pgData`.
- Click **Update** at the bottom of the page to save changes.
6. **Start the Immich app** from the TrueNAS web interface.
This will recreate the Immich container with the new storage configuration and start the app.
If everything went well, you should now be able to access Immich with the new storage configuration. You can verify that the data has been copied correctly by checking the Immich web interface and ensuring that all your photos and videos are still available. You may delete the old datasets, if you no longer need them, using the TrueNAS web interface.
If you were using **ixVolume (dataset created automatically by the system)** or folders for Immich data storage, you can delete the old datasets using the following commands:
```bash
rm -r /mnt/.ix-apps/app_mounts/immich/library
rm -r /mnt/.ix-apps/app_mounts/immich/uploads
rm -r /mnt/.ix-apps/app_mounts/immich/thumbs
rm -r /mnt/.ix-apps/app_mounts/immich/profile
rm -r /mnt/.ix-apps/app_mounts/immich/video
rm -r /mnt/.ix-apps/app_mounts/immich/backups
```
</TabItem>
<TabItem value="migrate-old-dataset" label="Keep the existing datasets">
To migrate from the old storage configuration to the new one without creating new datasets.
1. **Stop the Immich app** from the TrueNAS web interface to ensure no data is being written while you are updating the app.
2. **Update the datasets permissions**: Ensure that the datasets used for Immich data storage (`library`, `upload`, `thumbs`, `profile`, `video`, `backups`) have the correct permissions set for the user who will run Immich. The user should have ***modify*** permissions on these datasets. The default user for Immich is `apps` (UID 568) and the default group is `apps` (GID 568). If you are using a different user, make sure to set the permissions accordingly. You can do this from the TrueNAS web interface by going to the **Datasets** screen, selecting each dataset, clicking on the **Edit** button next to **Permissions**, and adding the user with ***modify*** permissions.
3. **Update the Immich app** to use the existing datasets:
- Go to the **Installed Applications** screen and select Immich from the list of installed applications.
- Click **Edit** on the **Application Info** widget.
- In the **Storage Configuration** section, untick the **Use Old Storage Configuration (Deprecated)** checkbox.
- For the **Data Storage**, you can keep the **ixVolume (dataset created automatically by the system)** as no data will be directly written to it. We recommend selecting **Host Path (Path that already exists on the system)** and then select a **new** dataset you created for Immich data storage, for example, `data`.
- For the **Postgres Data Storage**, keep **Host Path (Path that already exists on the system)** and then select the existing dataset you used for Postgres data storage, for example, `pgData`.
- Following the instructions in the [Multiple Datasets for Immich Storage](#additional-storage-advanced-users) section, you can add, **for each old dataset**, a new Additional Storage with the following settings:
- **Type**: `Host Path (Path that already exists on the system)`
- **Mount Path**: `/data/<folder-name>` (e.g. `/data/library`)
- **Host Path**: `/mnt/<your-pool-name>/<dataset-name>` (e.g. `/mnt/tank/immich/library`)
:::danger Ensure using the correct paths names
Make sure to replace `<folder-name>` with the actual name of the folder used by Immich: `library`, `upload`, `thumbs`, `profile`, `encoded-video`, and `backups`. Also, replace `<your-pool-name>` and `<dataset-name>` with the actual names of your pool and dataset.
:::
- **Read Only**: Keep it unticked as Immich needs to write to these datasets.
- Click **Update** at the bottom of the page to save changes.
4. **Start the Immich app** from the TrueNAS web interface. This will recreate the Immich container with the new storage configuration and start the app. If everything went well, you should now be able to access Immich with the new storage configuration. You can verify that the data is still available by checking the Immich web interface and ensuring that all your photos and videos are still accessible.
</TabItem>
</Tabs>

View File

@@ -75,6 +75,7 @@ alt="Select Plugins > Compose.Manager > Add New Stack > Label it Immich"
5. Click "**Save Changes**", you will be prompted to edit stack UI labels, just leave this blank and click "**Ok**" 5. Click "**Save Changes**", you will be prompted to edit stack UI labels, just leave this blank and click "**Ok**"
6. Select the cog ⚙️ next to Immich, click "**Edit Stack**", then click "**Env File**" 6. Select the cog ⚙️ next to Immich, click "**Edit Stack**", then click "**Env File**"
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: 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` - `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/postgresql/data`). This uses the `appdata` share. Do also create the `postgresql` folder, by running `mkdir /mnt/user/{share_location}/postgresql/data`. 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. - `DB_DATA_LOCATION`: Change this to use an Unraid share (preferably a cache pool, e.g. `/mnt/user/appdata/postgresql/data`). This uses the `appdata` share. Do also create the `postgresql` folder, by running `mkdir /mnt/user/{share_location}/postgresql/data`. 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.

View File

@@ -27,102 +27,3 @@ docker image prune
[watchtower]: https://containrrr.dev/watchtower/ [watchtower]: https://containrrr.dev/watchtower/
[breaking]: https://github.com/immich-app/immich/discussions?discussions_q=label%3Achangelog%3Abreaking-change+sort%3Adate_created [breaking]: https://github.com/immich-app/immich/discussions?discussions_q=label%3Achangelog%3Abreaking-change+sort%3Adate_created
[releases]: https://github.com/immich-app/immich/releases [releases]: https://github.com/immich-app/immich/releases
## Migrating to VectorChord
:::info
If you deploy Immich using Docker Compose, see `ghcr.io/immich-app/postgres` in the `docker-compose.yml` file and have not explicitly set the `DB_VECTOR_EXTENSION` environmental variable, your Immich database is already using VectorChord and this section does not apply to you.
:::
:::important
If you do not deploy Immich using Docker Compose and see a deprecation warning for pgvecto.rs on server startup, you should refer to the maintainers of the Immich distribution for guidance (if using a turnkey solution) or adapt the instructions for your specific setup.
:::
Immich has migrated off of the deprecated pgvecto.rs database extension to its successor, [VectorChord](https://github.com/tensorchord/VectorChord), which comes with performance improvements in almost every aspect. This section will guide you on how to make this change in a Docker Compose setup.
Before making any changes, please [back up your database](/docs/administration/backup-and-restore). While every effort has been made to make this migration as smooth as possible, theres always a chance that something can go wrong.
After making a backup, please modify your `docker-compose.yml` file with the following information.
```diff
[...]
database:
container_name: immich_postgres
- image: docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
+ image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_DB: ${DB_DATABASE_NAME}
POSTGRES_INITDB_ARGS: '--data-checksums'
+ # Uncomment the DB_STORAGE_TYPE: 'HDD' var if your database isn't stored on SSDs
+ # DB_STORAGE_TYPE: 'HDD'
volumes:
# Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
- healthcheck:
- test: >-
- pg_isready --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" || exit 1;
- Chksum="$$(psql --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" --tuples-only --no-align
- --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')";
- echo "checksum failure count is $$Chksum";
- [ "$$Chksum" = '0' ] || exit 1
- 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
+ shm_size: 128mb
restart: always
[...]
```
:::important
If you deviated from the defaults of pg14 or pgvectors0.2.0, you must adjust the pg major version and pgvecto.rs version. If you are still using the default `docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0` image, you can just follow the changes above. For example, if the previous image is `docker.io/tensorchord/pgvecto-rs:pg16-v0.3.0`, the new image should be `ghcr.io/immich-app/postgres:16-vectorchord0.3.0-pgvectors0.3.0` instead of the image specified in the diff.
:::
After making these changes, you can start Immich as normal. Immich will make some changes to the DB during startup, which can take seconds to minutes to finish, depending on hardware and library size. In particular, its normal for the server logs to be seemingly stuck at `Reindexing clip_index` and `Reindexing face_index`for some time if you have over 100k assets in Immich and/or Immich is on a relatively weak server. If you see these logs and there are no errors, just give it time.
:::danger
After switching to VectorChord, you should not downgrade Immich below 1.133.0.
:::
Please dont hesitate to contact us on [GitHub](https://github.com/immich-app/immich/discussions) or [Discord](https://discord.immich.app/) if you encounter migration issues.
### VectorChord FAQ
#### I have a separate PostgreSQL instance shared with multiple services. How can I switch to VectorChord?
Please see the [standalone PostgreSQL documentation](/docs/administration/postgres-standalone#migrating-to-vectorchord) for migration instructions. The migration path will be different depending on whether youre currently using pgvecto.rs or pgvector, as well as whether Immich has superuser DB permissions.
#### Why are so many lines removed from the `docker-compose.yml` file? Does this mean the health check is removed?
These lines are now incorporated into the image itself along with some additional tuning.
#### What does this change mean for my existing DB backups?
The new DB image includes pgvector and pgvecto.rs in addition to VectorChord, so you can use this image to restore from existing backups that used either of these extensions. However, backups made after switching to VectorChord require an image containing VectorChord to restore successfully.
#### Do I still need pgvecto.rs installed after migrating to VectorChord?
pgvecto.rs only needs to be available during the migration, or if you need to restore from a backup that used pgvecto.rs. For a leaner DB and a smaller image, you can optionally switch to an image variant that doesnt have pgvecto.rs installed after youve performed the migration and started Immich: `ghcr.io/immich-app/postgres:14-vectorchord0.4.3`, changing the PostgreSQL version as appropriate.
#### Why does it matter whether my database is on an SSD or an HDD?
These storage mediums have different performance characteristics. As a result, the optimal settings for an SSD are not the same as those for an HDD. Either configuration is compatible with SSD and HDD, but using the right configuration will make Immich snappier. As a general tip, we recommend users store the database on an SSD whenever possible.
#### Can I use the new database image as a general PostgreSQL image outside of Immich?
Its a standard PostgreSQL container image that additionally contains the VectorChord, pgvector, and (optionally) pgvecto.rs extensions. If you were using the previous pgvecto.rs image for other purposes, you can similarly do so with this image.
#### If pgvecto.rs and pgvector still work, why should I switch to VectorChord?
VectorChord is faster, more stable, uses less RAM, and (with the settings Immich uses) offers higher-quality results than pgvector and pgvecto.rs. This translates to better search and facial recognition experiences. In addition, pgvecto.rs support will be dropped in the future, so changing it sooner will avoid disruption.

20954
docs/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -7,8 +7,7 @@
"format": "prettier --check .", "format": "prettier --check .",
"format:fix": "prettier --write .", "format:fix": "prettier --write .",
"start": "docusaurus start --port 3005", "start": "docusaurus start --port 3005",
"copy:openapi": "jq -c < ../open-api/immich-openapi-specs.json > ./static/openapi.json || exit 0", "build": "docusaurus build",
"build": "npm run copy:openapi && docusaurus build",
"swizzle": "docusaurus swizzle", "swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy", "deploy": "docusaurus deploy",
"clear": "docusaurus clear", "clear": "docusaurus clear",
@@ -17,9 +16,8 @@
"write-heading-ids": "docusaurus write-heading-ids" "write-heading-ids": "docusaurus write-heading-ids"
}, },
"dependencies": { "dependencies": {
"@docusaurus/core": "~3.8.0", "@docusaurus/core": "~3.7.0",
"@docusaurus/preset-classic": "~3.8.0", "@docusaurus/preset-classic": "~3.7.0",
"@docusaurus/theme-common": "~3.8.0",
"@mdi/js": "^7.3.67", "@mdi/js": "^7.3.67",
"@mdi/react": "^1.6.1", "@mdi/react": "^1.6.1",
"@mdx-js/react": "^3.0.0", "@mdx-js/react": "^3.0.0",
@@ -28,7 +26,6 @@
"clsx": "^2.0.0", "clsx": "^2.0.0",
"docusaurus-lunr-search": "^3.3.2", "docusaurus-lunr-search": "^3.3.2",
"docusaurus-preset-openapi": "^0.7.5", "docusaurus-preset-openapi": "^0.7.5",
"lunr": "^2.3.9",
"postcss": "^8.4.25", "postcss": "^8.4.25",
"prism-react-renderer": "^2.3.1", "prism-react-renderer": "^2.3.1",
"raw-loader": "^4.0.2", "raw-loader": "^4.0.2",
@@ -38,7 +35,7 @@
"url": "^0.11.0" "url": "^0.11.0"
}, },
"devDependencies": { "devDependencies": {
"@docusaurus/module-type-aliases": "~3.8.0", "@docusaurus/module-type-aliases": "~3.7.0",
"@docusaurus/tsconfig": "^3.7.0", "@docusaurus/tsconfig": "^3.7.0",
"@docusaurus/types": "^3.7.0", "@docusaurus/types": "^3.7.0",
"prettier": "^3.2.4", "prettier": "^3.2.4",
@@ -60,6 +57,6 @@
"node": ">=20" "node": ">=20"
}, },
"volta": { "volta": {
"node": "22.18.0" "node": "22.16.0"
} }
} }

View File

@@ -58,12 +58,6 @@ const guides: CommunityGuidesProps[] = [
description: 'Access Immich with an end-to-end encrypted connection.', description: 'Access Immich with an end-to-end encrypted connection.',
url: 'https://meshnet.nordvpn.com/how-to/remote-files-media-access/immich-remote-access', url: 'https://meshnet.nordvpn.com/how-to/remote-files-media-access/immich-remote-access',
}, },
{
title: 'Trust Self Signed Certificates with Immich - OAuth Setup',
description:
'Set up Certificate Authority trust with Immich, and your private OAuth2/OpenID service, while using a private CA for HTTPS commication.',
url: 'https://github.com/immich-app/immich/discussions/18614',
},
]; ];
function CommunityGuide({ title, description, url }: CommunityGuidesProps): JSX.Element { function CommunityGuide({ title, description, url }: CommunityGuidesProps): JSX.Element {

View File

@@ -100,11 +100,6 @@ const projects: CommunityProjectProps[] = [
description: 'Automatically optimize files uploaded to Immich in order to save storage space', description: 'Automatically optimize files uploaded to Immich in order to save storage space',
url: 'https://github.com/miguelangel-nubla/immich-upload-optimizer', url: 'https://github.com/miguelangel-nubla/immich-upload-optimizer',
}, },
{
title: 'Immich Machine Learning Load Balancer',
description: 'Speed up your machine learning by load balancing your requests to multiple computers',
url: 'https://github.com/apetersson/immich_ml_balancer',
},
]; ];
function CommunityProject({ title, description, url }: CommunityProjectProps): JSX.Element { function CommunityProject({ title, description, url }: CommunityProjectProps): JSX.Element {

View File

@@ -16,9 +16,6 @@ import {
mdiCloudKeyOutline, mdiCloudKeyOutline,
mdiRegex, mdiRegex,
mdiCodeJson, mdiCodeJson,
mdiClockOutline,
mdiAccountOutline,
mdiRestart,
} from '@mdi/js'; } from '@mdi/js';
import Layout from '@theme/Layout'; import Layout from '@theme/Layout';
import React from 'react'; import React from 'react';
@@ -29,42 +26,6 @@ const withLanguage = (date: Date) => (language: string) => date.toLocaleDateStri
type Item = Omit<TimelineItem, 'done' | 'getDateLabel'> & { date: Date }; type Item = Omit<TimelineItem, 'done' | 'getDateLabel'> & { date: Date };
const items: Item[] = [ const items: Item[] = [
{
icon: mdiClockOutline,
iconColor: 'gray',
title: 'setTimeout is cursed',
description:
'The setTimeout method in JavaScript is cursed when used with small values because the implementation may or may not actually wait the specified time.',
link: {
url: 'https://github.com/immich-app/immich/pull/20655',
text: '#20655',
},
date: new Date(2025, 7, 4),
},
{
icon: mdiAccountOutline,
iconColor: '#DAB1DA',
title: 'PostgreSQL USER is cursed',
description:
'The USER keyword in PostgreSQL is cursed because you can select from it like a table, which leads to confusion if you have a table name user as well.',
link: {
url: 'https://github.com/immich-app/immich/pull/19891',
text: '#19891',
},
date: new Date(2025, 7, 4),
},
{
icon: mdiRestart,
iconColor: '#8395e3',
title: 'PostgreSQL RESET is cursed',
description:
'PostgreSQL RESET is cursed because it is impossible to RESET a PostgreSQL extension parameter if the extension has been uninstalled.',
link: {
url: 'https://github.com/immich-app/immich/pull/19363',
text: '#19363',
},
date: new Date(2025, 5, 20),
},
{ {
icon: mdiRegex, icon: mdiRegex,
iconColor: 'purple', iconColor: 'purple',

Some files were not shown because too many files have changed in this diff Show More