Compare commits
18 Commits
feat/graph
...
feat/rotat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a4f48204d | ||
|
|
9cd0871178 | ||
|
|
dbbefde98d | ||
|
|
5407a28533 | ||
|
|
f5edc87e4d | ||
|
|
bf16b61d43 | ||
|
|
8c882b54cd | ||
|
|
2d7c333c8c | ||
|
|
7c821dd205 | ||
|
|
703361da1a | ||
|
|
fa5aeaf539 | ||
|
|
5f3a42a132 | ||
|
|
9d85272c2b | ||
|
|
d2575d8f00 | ||
|
|
f0a4c945bd | ||
|
|
a3766b879e | ||
|
|
1a190c33a0 | ||
|
|
17a63e37b2 |
4
.github/workflows/cli.yml
vendored
4
.github/workflows/cli.yml
vendored
@@ -56,10 +56,10 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3.3.0
|
||||
uses: docker/setup-qemu-action@v3.4.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.8.0
|
||||
uses: docker/setup-buildx-action@v3.9.0
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
|
||||
133
.github/workflows/docker.yml
vendored
133
.github/workflows/docker.yml
vendored
@@ -87,7 +87,6 @@ jobs:
|
||||
TAG_NEW=${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }}
|
||||
docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_NEW $REGISTRY_NAME/$REPOSITORY:$TAG_OLD
|
||||
|
||||
|
||||
build_and_push_ml:
|
||||
name: Build and Push ML
|
||||
needs: pre-job
|
||||
@@ -122,10 +121,10 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3.3.0
|
||||
uses: docker/setup-qemu-action@v3.4.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.8.0
|
||||
uses: docker/setup-buildx-action@v3.9.0
|
||||
|
||||
- name: Login to Docker Hub
|
||||
# Only push to Docker Hub when making a release
|
||||
@@ -195,33 +194,38 @@ jobs:
|
||||
|
||||
build_and_push_server:
|
||||
name: Build and Push Server
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ matrix.runner }}
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
||||
env:
|
||||
image: immich-server
|
||||
context: .
|
||||
file: server/Dockerfile
|
||||
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-server
|
||||
DOCKER_REPO: altran1502/immich-server
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- platforms: linux/amd64,linux/arm64
|
||||
device: cpu
|
||||
- platform: linux/amd64
|
||||
runner: ubuntu-latest
|
||||
- platform: linux/arm64
|
||||
runner: ubuntu-24.04-arm
|
||||
steps:
|
||||
- name: Prepare
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3.3.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.8.0
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
# Only push to Docker Hub when making a release
|
||||
if: ${{ github.event_name == 'release' }}
|
||||
uses: docker/login-action@v3
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
@@ -235,16 +239,81 @@ jobs:
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push image
|
||||
id: build
|
||||
uses: docker/build-push-action@v6.13.0
|
||||
with:
|
||||
context: ${{ env.context }}
|
||||
file: ${{ env.file }}
|
||||
platforms: ${{ matrix.platform }}
|
||||
# Skip pushing when PR from a fork
|
||||
push: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
labels: ${{ steps.metadata.outputs.labels }}
|
||||
outputs: type=image,"name=${{ env.GHCR_REPO }},${{ env.DOCKER_REPO }}",push-by-digest=true,name-canonical=true,push=true
|
||||
build-args: |
|
||||
DEVICE=cpu
|
||||
BUILD_ID=${{ github.run_id }}
|
||||
BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.metadata.outputs.tags }}
|
||||
BUILD_SOURCE_REF=${{ github.ref_name }}
|
||||
BUILD_SOURCE_COMMIT=${{ github.sha }}
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p ${{ runner.temp }}/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: digests-${{ env.PLATFORM_PAIR }}
|
||||
path: ${{ runner.temp }}/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
merge_server:
|
||||
name: Merge & Push Server
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ needs.pre-job.outputs.should_run_server == 'true' && !github.event.pull_request.head.repo.fork }}
|
||||
env:
|
||||
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-server
|
||||
DOCKER_REPO: altran1502/immich-server
|
||||
needs:
|
||||
- build_and_push_server
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ${{ runner.temp }}/digests
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Generate docker image tags
|
||||
id: metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
flavor: |
|
||||
# Disable latest tag
|
||||
latest=false
|
||||
images: |
|
||||
name=ghcr.io/${{ github.repository_owner }}/${{env.image}}
|
||||
name=altran1502/${{env.image}},enable=${{ github.event_name == 'release' }}
|
||||
name=${{ env.GHCR_REPO }}
|
||||
name=${{ env.DOCKER_REPO }},enable=${{ github.event_name == 'release' }}
|
||||
tags: |
|
||||
# Tag with branch name
|
||||
type=ref,event=branch,suffix=${{ matrix.suffix }}
|
||||
@@ -254,38 +323,16 @@ jobs:
|
||||
type=ref,event=tag,suffix=${{ matrix.suffix }}
|
||||
type=raw,value=release,enable=${{ github.event_name == 'release' }},suffix=${{ matrix.suffix }}
|
||||
|
||||
- name: Determine build cache output
|
||||
id: cache-target
|
||||
- name: Create manifest list and push
|
||||
working-directory: ${{ runner.temp }}/digests
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
# Essentially just ignore the cache output (PR can't write to registry cache)
|
||||
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "cache-to=type=registry,mode=max,ref=ghcr.io/${{ github.repository_owner }}/immich-build-cache:${{ env.image }}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Build and push image
|
||||
uses: docker/build-push-action@v6.13.0
|
||||
with:
|
||||
context: ${{ env.context }}
|
||||
file: ${{ env.file }}
|
||||
platforms: ${{ matrix.platforms }}
|
||||
# Skip pushing when PR from a fork
|
||||
push: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/immich-build-cache:${{env.image}}
|
||||
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
||||
tags: ${{ steps.metadata.outputs.tags }}
|
||||
labels: ${{ steps.metadata.outputs.labels }}
|
||||
build-args: |
|
||||
DEVICE=${{ matrix.device }}
|
||||
BUILD_ID=${{ github.run_id }}
|
||||
BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.metadata.outputs.tags }}
|
||||
BUILD_SOURCE_REF=${{ github.ref_name }}
|
||||
BUILD_SOURCE_COMMIT=${{ github.sha }}
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf '${{ env.GHCR_REPO }}@sha256:%s ' *) \
|
||||
$(printf '${{ env.DOCKER_REPO }}@sha256:%s ' *)
|
||||
|
||||
success-check-server:
|
||||
name: Docker Build & Push Server Success
|
||||
needs: [build_and_push_server, retag_server]
|
||||
needs: [merge_server, retag_server]
|
||||
runs-on: ubuntu-latest
|
||||
if: always()
|
||||
steps:
|
||||
|
||||
17
.github/workflows/preview-comment.yaml
vendored
Normal file
17
.github/workflows/preview-comment.yaml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: Preview comment
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
|
||||
jobs:
|
||||
comment-status:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.label.name == 'preview' }}
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: mshick/add-pr-comment@v2
|
||||
with:
|
||||
message-id: "preview-status"
|
||||
message: "Deploying preview environment to https://pr-${{ github.event.pull_request.number }}.preview.internal.immich.cloud/"
|
||||
252
cli/package-lock.json
generated
252
cli/package-lock.json
generated
@@ -881,9 +881,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz",
|
||||
"integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==",
|
||||
"version": "9.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.19.0.tgz",
|
||||
"integrity": "sha512-rbq9/g38qjfqFLOVPvwjIvFFdNziEC5S65jmjPw5r6A//QH+W91akh9irMwjDN8zKUTak6W9EsAv4m/7Wnw0UQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -1498,21 +1498,21 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.20.0.tgz",
|
||||
"integrity": "sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==",
|
||||
"version": "8.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.23.0.tgz",
|
||||
"integrity": "sha512-vBz65tJgRrA1Q5gWlRfvoH+w943dq9K1p1yDBY2pc+a1nbBLZp7fB9+Hk8DaALUbzjqlMfgaqlVPT1REJdkt/w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.20.0",
|
||||
"@typescript-eslint/type-utils": "8.20.0",
|
||||
"@typescript-eslint/utils": "8.20.0",
|
||||
"@typescript-eslint/visitor-keys": "8.20.0",
|
||||
"@typescript-eslint/scope-manager": "8.23.0",
|
||||
"@typescript-eslint/type-utils": "8.23.0",
|
||||
"@typescript-eslint/utils": "8.23.0",
|
||||
"@typescript-eslint/visitor-keys": "8.23.0",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.3.1",
|
||||
"natural-compare": "^1.4.0",
|
||||
"ts-api-utils": "^2.0.0"
|
||||
"ts-api-utils": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -1528,16 +1528,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.20.0.tgz",
|
||||
"integrity": "sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==",
|
||||
"version": "8.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.23.0.tgz",
|
||||
"integrity": "sha512-h2lUByouOXFAlMec2mILeELUbME5SZRN/7R9Cw2RD2lRQQY08MWMM+PmVVKKJNK1aIwqTo9t/0CvOxwPbRIE2Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.20.0",
|
||||
"@typescript-eslint/types": "8.20.0",
|
||||
"@typescript-eslint/typescript-estree": "8.20.0",
|
||||
"@typescript-eslint/visitor-keys": "8.20.0",
|
||||
"@typescript-eslint/scope-manager": "8.23.0",
|
||||
"@typescript-eslint/types": "8.23.0",
|
||||
"@typescript-eslint/typescript-estree": "8.23.0",
|
||||
"@typescript-eslint/visitor-keys": "8.23.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1553,14 +1553,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.20.0.tgz",
|
||||
"integrity": "sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==",
|
||||
"version": "8.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.23.0.tgz",
|
||||
"integrity": "sha512-OGqo7+dXHqI7Hfm+WqkZjKjsiRtFUQHPdGMXzk5mYXhJUedO7e/Y7i8AK3MyLMgZR93TX4bIzYrfyVjLC+0VSw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.20.0",
|
||||
"@typescript-eslint/visitor-keys": "8.20.0"
|
||||
"@typescript-eslint/types": "8.23.0",
|
||||
"@typescript-eslint/visitor-keys": "8.23.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -1571,16 +1571,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.20.0.tgz",
|
||||
"integrity": "sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==",
|
||||
"version": "8.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.23.0.tgz",
|
||||
"integrity": "sha512-iIuLdYpQWZKbiH+RkCGc6iu+VwscP5rCtQ1lyQ7TYuKLrcZoeJVpcLiG8DliXVkUxirW/PWlmS+d6yD51L9jvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "8.20.0",
|
||||
"@typescript-eslint/utils": "8.20.0",
|
||||
"@typescript-eslint/typescript-estree": "8.23.0",
|
||||
"@typescript-eslint/utils": "8.23.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^2.0.0"
|
||||
"ts-api-utils": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -1595,9 +1595,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.20.0.tgz",
|
||||
"integrity": "sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==",
|
||||
"version": "8.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.23.0.tgz",
|
||||
"integrity": "sha512-1sK4ILJbCmZOTt9k4vkoulT6/y5CHJ1qUYxqpF1K/DBAd8+ZUL4LlSCxOssuH5m4rUaaN0uS0HlVPvd45zjduQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -1609,20 +1609,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.20.0.tgz",
|
||||
"integrity": "sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==",
|
||||
"version": "8.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.23.0.tgz",
|
||||
"integrity": "sha512-LcqzfipsB8RTvH8FX24W4UUFk1bl+0yTOf9ZA08XngFwMg4Kj8A+9hwz8Cr/ZS4KwHrmo9PJiLZkOt49vPnuvQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.20.0",
|
||||
"@typescript-eslint/visitor-keys": "8.20.0",
|
||||
"@typescript-eslint/types": "8.23.0",
|
||||
"@typescript-eslint/visitor-keys": "8.23.0",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
"minimatch": "^9.0.4",
|
||||
"semver": "^7.6.0",
|
||||
"ts-api-utils": "^2.0.0"
|
||||
"ts-api-utils": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -1636,16 +1636,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.20.0.tgz",
|
||||
"integrity": "sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==",
|
||||
"version": "8.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.23.0.tgz",
|
||||
"integrity": "sha512-uB/+PSo6Exu02b5ZEiVtmY6RVYO7YU5xqgzTIVZwTHvvK3HsL8tZZHFaTLFtRG3CsV4A5mhOv+NZx5BlhXPyIA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@typescript-eslint/scope-manager": "8.20.0",
|
||||
"@typescript-eslint/types": "8.20.0",
|
||||
"@typescript-eslint/typescript-estree": "8.20.0"
|
||||
"@typescript-eslint/scope-manager": "8.23.0",
|
||||
"@typescript-eslint/types": "8.23.0",
|
||||
"@typescript-eslint/typescript-estree": "8.23.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -1660,13 +1660,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.20.0.tgz",
|
||||
"integrity": "sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==",
|
||||
"version": "8.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.23.0.tgz",
|
||||
"integrity": "sha512-oWWhcWDLwDfu++BGTZcmXWqpwtkwb5o7fxUIGksMQQDSdPW9prsSnfIOZMlsj4vBOSrcnjIUZMiIjODgGosFhQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.20.0",
|
||||
"@typescript-eslint/types": "8.23.0",
|
||||
"eslint-visitor-keys": "^4.2.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1691,9 +1691,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/coverage-v8": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.3.tgz",
|
||||
"integrity": "sha512-uVbJ/xhImdNtzPnLyxCZJMTeTIYdgcC2nWtBBBpR1H6z0w8m7D+9/zrDIx2nNxgMg9r+X8+RY2qVpUDeW2b3nw==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.5.tgz",
|
||||
"integrity": "sha512-zOOWIsj5fHh3jjGwQg+P+J1FW3s4jBu1Zqga0qW60yutsBtqEqNEJKWYh7cYn1yGD+1bdPsPdC/eL4eVK56xMg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1714,8 +1714,8 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vitest/browser": "3.0.3",
|
||||
"vitest": "3.0.3"
|
||||
"@vitest/browser": "3.0.5",
|
||||
"vitest": "3.0.5"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vitest/browser": {
|
||||
@@ -1724,14 +1724,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.3.tgz",
|
||||
"integrity": "sha512-SbRCHU4qr91xguu+dH3RUdI5dC86zm8aZWydbp961aIR7G8OYNN6ZiayFuf9WAngRbFOfdrLHCGgXTj3GtoMRQ==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.5.tgz",
|
||||
"integrity": "sha512-nNIOqupgZ4v5jWuQx2DSlHLEs7Q4Oh/7AYwNyE+k0UQzG7tSmjPXShUikn1mpNGzYEN2jJbTvLejwShMitovBA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "3.0.3",
|
||||
"@vitest/utils": "3.0.3",
|
||||
"@vitest/spy": "3.0.5",
|
||||
"@vitest/utils": "3.0.5",
|
||||
"chai": "^5.1.2",
|
||||
"tinyrainbow": "^2.0.0"
|
||||
},
|
||||
@@ -1740,13 +1740,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/mocker": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.3.tgz",
|
||||
"integrity": "sha512-XT2XBc4AN9UdaxJAeIlcSZ0ILi/GzmG5G8XSly4gaiqIvPV3HMTSIDZWJVX6QRJ0PX1m+W8Cy0K9ByXNb/bPIA==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.5.tgz",
|
||||
"integrity": "sha512-CLPNBFBIE7x6aEGbIjaQAX03ZZlBMaWwAjBdMkIf/cAn6xzLTiM3zYqO/WAbieEjsAZir6tO71mzeHZoodThvw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "3.0.3",
|
||||
"@vitest/spy": "3.0.5",
|
||||
"estree-walker": "^3.0.3",
|
||||
"magic-string": "^0.30.17"
|
||||
},
|
||||
@@ -1767,9 +1767,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/pretty-format": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.3.tgz",
|
||||
"integrity": "sha512-gCrM9F7STYdsDoNjGgYXKPq4SkSxwwIU5nkaQvdUxiQ0EcNlez+PdKOVIsUJvh9P9IeIFmjn4IIREWblOBpP2Q==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.5.tgz",
|
||||
"integrity": "sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1780,38 +1780,38 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/runner": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.3.tgz",
|
||||
"integrity": "sha512-Rgi2kOAk5ZxWZlwPguRJFOBmWs6uvvyAAR9k3MvjRvYrG7xYvKChZcmnnpJCS98311CBDMqsW9MzzRFsj2gX3g==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.5.tgz",
|
||||
"integrity": "sha512-BAiZFityFexZQi2yN4OX3OkJC6scwRo8EhRB0Z5HIGGgd2q+Nq29LgHU/+ovCtd0fOfXj5ZI6pwdlUmC5bpi8A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/utils": "3.0.3",
|
||||
"pathe": "^2.0.1"
|
||||
"@vitest/utils": "3.0.5",
|
||||
"pathe": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/snapshot": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.3.tgz",
|
||||
"integrity": "sha512-kNRcHlI4txBGztuJfPEJ68VezlPAXLRT1u5UCx219TU3kOG2DplNxhWLwDf2h6emwmTPogzLnGVwP6epDaJN6Q==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.5.tgz",
|
||||
"integrity": "sha512-GJPZYcd7v8QNUJ7vRvLDmRwl+a1fGg4T/54lZXe+UOGy47F9yUfE18hRCtXL5aHN/AONu29NGzIXSVFh9K0feA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "3.0.3",
|
||||
"@vitest/pretty-format": "3.0.5",
|
||||
"magic-string": "^0.30.17",
|
||||
"pathe": "^2.0.1"
|
||||
"pathe": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/spy": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.3.tgz",
|
||||
"integrity": "sha512-7/dgux8ZBbF7lEIKNnEqQlyRaER9nkAL9eTmdKJkDO3hS8p59ATGwKOCUDHcBLKr7h/oi/6hP+7djQk8049T2A==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.5.tgz",
|
||||
"integrity": "sha512-5fOzHj0WbUNqPK6blI/8VzZdkBlQLnT25knX0r4dbZI9qoZDf3qAdjoMmDcLG5A83W6oUUFJgUd0EYBc2P5xqg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1822,13 +1822,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/utils": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.3.tgz",
|
||||
"integrity": "sha512-f+s8CvyzPtMFY1eZKkIHGhPsQgYo5qCm6O8KZoim9qm1/jT64qBgGpO5tHscNH6BzRHM+edLNOP+3vO8+8pE/A==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.5.tgz",
|
||||
"integrity": "sha512-N9AX0NUoUtVwKwy21JtwzaqR5L5R5A99GAbrHfCCXK1lp593i/3AZAXhSP43wRQuxYsflrdzEfXZFo1reR1Nkg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "3.0.3",
|
||||
"@vitest/pretty-format": "3.0.5",
|
||||
"loupe": "^3.1.2",
|
||||
"tinyrainbow": "^2.0.0"
|
||||
},
|
||||
@@ -2334,9 +2334,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.18.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.18.0.tgz",
|
||||
"integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==",
|
||||
"version": "9.19.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.19.0.tgz",
|
||||
"integrity": "sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2345,7 +2345,7 @@
|
||||
"@eslint/config-array": "^0.19.0",
|
||||
"@eslint/core": "^0.10.0",
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
"@eslint/js": "9.18.0",
|
||||
"@eslint/js": "9.19.0",
|
||||
"@eslint/plugin-kit": "^0.2.5",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
@@ -2407,9 +2407,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-prettier": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.2.tgz",
|
||||
"integrity": "sha512-1yI3/hf35wmlq66C8yOyrujQnel+v5l1Vop5Cl2I6ylyNTT1JbuUUnV3/41PzwTzcyDp/oF0jWE3HXvcH5AQOQ==",
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz",
|
||||
"integrity": "sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2685,9 +2685,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fastq": {
|
||||
"version": "1.18.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz",
|
||||
"integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==",
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz",
|
||||
"integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"reusify": "^1.0.4"
|
||||
@@ -3180,9 +3180,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/loupe": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz",
|
||||
"integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==",
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz",
|
||||
"integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -3285,9 +3285,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mock-fs": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.4.1.tgz",
|
||||
"integrity": "sha512-sz/Q8K1gXXXHR+qr0GZg2ysxCRr323kuN10O7CtQjraJsFDJ4SJ+0I5MzALz7aRp9lHk8Cc/YdsT95h9Ka1aFw==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.5.0.tgz",
|
||||
"integrity": "sha512-d/P1M/RacgM3dB0sJ8rjeRNXxtapkPCUnMGmIN0ixJ16F/E4GUZCvWcSGfWGz8eaXYvn1s9baUwNjI4LOPEjiA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -4166,9 +4166,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ts-api-utils": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz",
|
||||
"integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz",
|
||||
"integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -4288,15 +4288,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "6.0.11",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.0.11.tgz",
|
||||
"integrity": "sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==",
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.1.0.tgz",
|
||||
"integrity": "sha512-RjjMipCKVoR4hVfPY6GQTgveinjNuyLw+qruksLDvA5ktI1150VmcMBKmQaEWJhg/j6Uaf6dNCNA0AfdzUb/hQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.24.2",
|
||||
"postcss": "^8.4.49",
|
||||
"rollup": "^4.23.0"
|
||||
"postcss": "^8.5.1",
|
||||
"rollup": "^4.30.1"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
@@ -4360,16 +4360,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite-node": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.3.tgz",
|
||||
"integrity": "sha512-0sQcwhwAEw/UJGojbhOrnq3HtiZ3tC7BzpAa0lx3QaTX0S3YX70iGcik25UBdB96pmdwjyY2uyKNYruxCDmiEg==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.5.tgz",
|
||||
"integrity": "sha512-02JEJl7SbtwSDJdYS537nU6l+ktdvcREfLksk/NDAqtdKWGqHl+joXzEubHROmS3E6pip+Xgu2tFezMu75jH7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cac": "^6.7.14",
|
||||
"debug": "^4.4.0",
|
||||
"es-module-lexer": "^1.6.0",
|
||||
"pathe": "^2.0.1",
|
||||
"pathe": "^2.0.2",
|
||||
"vite": "^5.0.0 || ^6.0.0"
|
||||
},
|
||||
"bin": {
|
||||
@@ -4403,31 +4403,31 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.3.tgz",
|
||||
"integrity": "sha512-dWdwTFUW9rcnL0LyF2F+IfvNQWB0w9DERySCk8VMG75F8k25C7LsZoh6XfCjPvcR8Nb+Lqi9JKr6vnzH7HSrpQ==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.5.tgz",
|
||||
"integrity": "sha512-4dof+HvqONw9bvsYxtkfUp2uHsTN9bV2CZIi1pWgoFpL1Lld8LA1ka9q/ONSsoScAKG7NVGf2stJTI7XRkXb2Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/expect": "3.0.3",
|
||||
"@vitest/mocker": "3.0.3",
|
||||
"@vitest/pretty-format": "^3.0.3",
|
||||
"@vitest/runner": "3.0.3",
|
||||
"@vitest/snapshot": "3.0.3",
|
||||
"@vitest/spy": "3.0.3",
|
||||
"@vitest/utils": "3.0.3",
|
||||
"@vitest/expect": "3.0.5",
|
||||
"@vitest/mocker": "3.0.5",
|
||||
"@vitest/pretty-format": "^3.0.5",
|
||||
"@vitest/runner": "3.0.5",
|
||||
"@vitest/snapshot": "3.0.5",
|
||||
"@vitest/spy": "3.0.5",
|
||||
"@vitest/utils": "3.0.5",
|
||||
"chai": "^5.1.2",
|
||||
"debug": "^4.4.0",
|
||||
"expect-type": "^1.1.0",
|
||||
"magic-string": "^0.30.17",
|
||||
"pathe": "^2.0.1",
|
||||
"pathe": "^2.0.2",
|
||||
"std-env": "^3.8.0",
|
||||
"tinybench": "^2.9.0",
|
||||
"tinyexec": "^0.3.2",
|
||||
"tinypool": "^1.0.2",
|
||||
"tinyrainbow": "^2.0.0",
|
||||
"vite": "^5.0.0 || ^6.0.0",
|
||||
"vite-node": "3.0.3",
|
||||
"vite-node": "3.0.5",
|
||||
"why-is-node-running": "^2.3.0"
|
||||
},
|
||||
"bin": {
|
||||
@@ -4441,9 +4441,10 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@edge-runtime/vm": "*",
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
||||
"@vitest/browser": "3.0.3",
|
||||
"@vitest/ui": "3.0.3",
|
||||
"@vitest/browser": "3.0.5",
|
||||
"@vitest/ui": "3.0.5",
|
||||
"happy-dom": "*",
|
||||
"jsdom": "*"
|
||||
},
|
||||
@@ -4451,6 +4452,9 @@
|
||||
"@edge-runtime/vm": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/debug": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
},
|
||||
|
||||
@@ -77,9 +77,7 @@ docker start immich_postgres # Start Postgres server
|
||||
sleep 10 # Wait for Postgres server to start up
|
||||
docker exec -it immich_postgres bash # Enter the Docker shell and run the following command
|
||||
# Check the database user if you deviated from the default. If your backup ends in `.gz`, replace `cat` with `gunzip`
|
||||
cat < "/dump.sql" \
|
||||
| sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" \
|
||||
| psql --dbname=postgres --username=<DB_USERNAME> # Restore Backup
|
||||
cat < "/dump.sql" | sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" | psql --dbname=postgres --username=<DB_USERNAME>
|
||||
exit # Exit the Docker shell
|
||||
docker compose up -d # Start remainder of Immich apps
|
||||
```
|
||||
|
||||
790
e2e/package-lock.json
generated
790
e2e/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -298,6 +298,7 @@ describe('/libraries', () => {
|
||||
expect(status).toBe(204);
|
||||
|
||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
||||
|
||||
const { assets } = await utils.searchAssets(admin.accessToken, {
|
||||
originalPath: `${testAssetDirInternal}/temp/directoryA/assetA.png`,
|
||||
|
||||
@@ -602,6 +602,8 @@
|
||||
"enable": "Enable",
|
||||
"enabled": "Enabled",
|
||||
"end_date": "End date",
|
||||
"rotate_left": "Rotate left",
|
||||
"rotate_right": "Rotate right",
|
||||
"error": "Error",
|
||||
"error_loading_image": "Error loading image",
|
||||
"error_title": "Error - Something went wrong",
|
||||
@@ -644,6 +646,7 @@
|
||||
"quota_higher_than_disk_size": "You set a quota higher than the disk size",
|
||||
"repair_unable_to_check_items": "Unable to check {count, select, one {item} other {items}}",
|
||||
"unable_to_add_album_users": "Unable to add users to album",
|
||||
"unable_to_rotate_image": "Unable to rotate image",
|
||||
"unable_to_add_assets_to_shared_link": "Unable to add assets to shared link",
|
||||
"unable_to_add_comment": "Unable to add comment",
|
||||
"unable_to_add_exclusion_pattern": "Unable to add exclusion pattern",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
ARG DEVICE=cpu
|
||||
|
||||
FROM python:3.11-bookworm@sha256:adb581d8ed80edd03efd4dcad66db115b9ce8de8522b01720b9f3e6146f0884c AS builder-cpu
|
||||
FROM python:3.11-bookworm@sha256:14b4620f59a90f163dfa6bd252b68743f9a41d494a9fde935f9d7669d98094bb AS builder-cpu
|
||||
|
||||
FROM builder-cpu AS builder-openvino
|
||||
|
||||
@@ -34,7 +34,7 @@ RUN python3 -m venv /opt/venv
|
||||
COPY poetry.lock pyproject.toml ./
|
||||
RUN poetry install --sync --no-interaction --no-ansi --no-root --with ${DEVICE} --without dev
|
||||
|
||||
FROM python:3.11-slim-bookworm@sha256:6ed5bff4d7d377e2a27d9285553b8c21cfccc4f00881de1b24c9bc8d90016e82 AS prod-cpu
|
||||
FROM python:3.11-slim-bookworm@sha256:42420f737ba91d509fc60d5ed65ed0492678a90c561e1fa08786ae8ba8b52eda AS prod-cpu
|
||||
|
||||
FROM prod-cpu AS prod-openvino
|
||||
|
||||
|
||||
314
machine-learning/poetry.lock
generated
314
machine-learning/poetry.lock
generated
@@ -758,23 +758,23 @@ test = ["pytest (>=6)"]
|
||||
|
||||
[[package]]
|
||||
name = "fastapi"
|
||||
version = "0.115.6"
|
||||
version = "0.115.8"
|
||||
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "fastapi-0.115.6-py3-none-any.whl", hash = "sha256:e9240b29e36fa8f4bb7290316988e90c381e5092e0cbe84e7818cc3713bcf305"},
|
||||
{file = "fastapi-0.115.6.tar.gz", hash = "sha256:9ec46f7addc14ea472958a96aae5b5de65f39721a46aaf5705c480d9a8b76654"},
|
||||
{file = "fastapi-0.115.8-py3-none-any.whl", hash = "sha256:753a96dd7e036b34eeef8babdfcfe3f28ff79648f86551eb36bfc1b0bf4a8cbf"},
|
||||
{file = "fastapi-0.115.8.tar.gz", hash = "sha256:0ce9111231720190473e222cdf0f07f7206ad7e53ea02beb1d2dc36e2f0741e9"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
|
||||
starlette = ">=0.40.0,<0.42.0"
|
||||
starlette = ">=0.40.0,<0.46.0"
|
||||
typing-extensions = ">=4.8.0"
|
||||
|
||||
[package.extras]
|
||||
all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
|
||||
standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"]
|
||||
all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=3.1.5)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
|
||||
standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "filelock"
|
||||
@@ -1331,13 +1331,13 @@ zstd = ["zstandard (>=0.18.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "huggingface-hub"
|
||||
version = "0.27.1"
|
||||
version = "0.28.1"
|
||||
description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub"
|
||||
optional = false
|
||||
python-versions = ">=3.8.0"
|
||||
files = [
|
||||
{file = "huggingface_hub-0.27.1-py3-none-any.whl", hash = "sha256:1c5155ca7d60b60c2e2fc38cbb3ffb7f7c3adf48f824015b219af9061771daec"},
|
||||
{file = "huggingface_hub-0.27.1.tar.gz", hash = "sha256:c004463ca870283909d715d20f066ebd6968c2207dae9393fdffb3c1d4d8f98b"},
|
||||
{file = "huggingface_hub-0.28.1-py3-none-any.whl", hash = "sha256:aa6b9a3ffdae939b72c464dbb0d7f99f56e649b55c3d52406f49e0a5a620c0a7"},
|
||||
{file = "huggingface_hub-0.28.1.tar.gz", hash = "sha256:893471090c98e3b6efbdfdacafe4052b20b84d59866fb6f54c33d9af18c303ae"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1350,13 +1350,13 @@ tqdm = ">=4.42.1"
|
||||
typing-extensions = ">=3.7.4.3"
|
||||
|
||||
[package.extras]
|
||||
all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"]
|
||||
all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"]
|
||||
cli = ["InquirerPy (==0.3.4)"]
|
||||
dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"]
|
||||
dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"]
|
||||
fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"]
|
||||
hf-transfer = ["hf-transfer (>=0.1.4)"]
|
||||
inference = ["aiohttp"]
|
||||
quality = ["libcst (==1.4.0)", "mypy (==1.5.1)", "ruff (>=0.5.0)"]
|
||||
quality = ["libcst (==1.4.0)", "mypy (==1.5.1)", "ruff (>=0.9.0)"]
|
||||
tensorflow = ["graphviz", "pydot", "tensorflow"]
|
||||
tensorflow-testing = ["keras (<3.0)", "tensorflow"]
|
||||
testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"]
|
||||
@@ -1625,13 +1625,13 @@ test = ["pytest (>=7.4)", "pytest-cov (>=4.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "locust"
|
||||
version = "2.32.6"
|
||||
version = "2.32.9"
|
||||
description = "Developer-friendly load testing framework"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "locust-2.32.6-py3-none-any.whl", hash = "sha256:d5c0e4f73134415d250087034431cf3ea42ca695d3dee7f10812287cacb6c4ef"},
|
||||
{file = "locust-2.32.6.tar.gz", hash = "sha256:6600cc308398e724764aacc56ccddf6cfcd0127c4c92dedd5c4979dd37ef5b15"},
|
||||
{file = "locust-2.32.9-py3-none-any.whl", hash = "sha256:d9447c26d2bbaec5a0ace7cadefa1a31820ed392234257b309965a43d5e8d26f"},
|
||||
{file = "locust-2.32.9.tar.gz", hash = "sha256:4c297afa5cdc3de15dfa79279576e5f33c1d69dd70006b51d079dcbd212201cc"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1649,8 +1649,8 @@ psutil = ">=5.9.1"
|
||||
pywin32 = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
pyzmq = ">=25.0.0"
|
||||
requests = [
|
||||
{version = ">=2.32.2", markers = "python_full_version > \"3.11.0\""},
|
||||
{version = ">=2.26.0", markers = "python_full_version <= \"3.11.0\""},
|
||||
{version = ">=2.32.2", markers = "python_full_version > \"3.11.0\""},
|
||||
]
|
||||
setuptools = ">=70.0.0"
|
||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||
@@ -1893,49 +1893,43 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "1.14.1"
|
||||
version = "1.15.0"
|
||||
description = "Optional static typing for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"},
|
||||
{file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"},
|
||||
{file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d"},
|
||||
{file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b"},
|
||||
{file = "mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427"},
|
||||
{file = "mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f"},
|
||||
{file = "mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c"},
|
||||
{file = "mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1"},
|
||||
{file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8"},
|
||||
{file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f"},
|
||||
{file = "mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1"},
|
||||
{file = "mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae"},
|
||||
{file = "mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14"},
|
||||
{file = "mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9"},
|
||||
{file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11"},
|
||||
{file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e"},
|
||||
{file = "mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89"},
|
||||
{file = "mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b"},
|
||||
{file = "mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255"},
|
||||
{file = "mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34"},
|
||||
{file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a"},
|
||||
{file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9"},
|
||||
{file = "mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd"},
|
||||
{file = "mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107"},
|
||||
{file = "mypy-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7084fb8f1128c76cd9cf68fe5971b37072598e7c31b2f9f95586b65c741a9d31"},
|
||||
{file = "mypy-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f845a00b4f420f693f870eaee5f3e2692fa84cc8514496114649cfa8fd5e2c6"},
|
||||
{file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44bf464499f0e3a2d14d58b54674dee25c031703b2ffc35064bd0df2e0fac319"},
|
||||
{file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c99f27732c0b7dc847adb21c9d47ce57eb48fa33a17bc6d7d5c5e9f9e7ae5bac"},
|
||||
{file = "mypy-1.14.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bce23c7377b43602baa0bd22ea3265c49b9ff0b76eb315d6c34721af4cdf1d9b"},
|
||||
{file = "mypy-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:8edc07eeade7ebc771ff9cf6b211b9a7d93687ff892150cb5692e4f4272b0837"},
|
||||
{file = "mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35"},
|
||||
{file = "mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc"},
|
||||
{file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9"},
|
||||
{file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb"},
|
||||
{file = "mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60"},
|
||||
{file = "mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c"},
|
||||
{file = "mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1"},
|
||||
{file = "mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6"},
|
||||
{file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"},
|
||||
{file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"},
|
||||
{file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b"},
|
||||
{file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3"},
|
||||
{file = "mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b"},
|
||||
{file = "mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828"},
|
||||
{file = "mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f"},
|
||||
{file = "mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5"},
|
||||
{file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e"},
|
||||
{file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c"},
|
||||
{file = "mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f"},
|
||||
{file = "mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f"},
|
||||
{file = "mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd"},
|
||||
{file = "mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f"},
|
||||
{file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464"},
|
||||
{file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee"},
|
||||
{file = "mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e"},
|
||||
{file = "mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22"},
|
||||
{file = "mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445"},
|
||||
{file = "mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d"},
|
||||
{file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5"},
|
||||
{file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036"},
|
||||
{file = "mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357"},
|
||||
{file = "mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf"},
|
||||
{file = "mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078"},
|
||||
{file = "mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba"},
|
||||
{file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5"},
|
||||
{file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b"},
|
||||
{file = "mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2"},
|
||||
{file = "mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980"},
|
||||
{file = "mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e"},
|
||||
{file = "mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -2181,94 +2175,98 @@ files = [
|
||||
|
||||
[package.dependencies]
|
||||
numpy = [
|
||||
{version = ">=1.26.0", markers = "python_version >= \"3.12\""},
|
||||
{version = ">=1.23.5", markers = "python_version >= \"3.11\" and python_version < \"3.12\""},
|
||||
{version = ">=1.21.4", markers = "python_version >= \"3.10\" and platform_system == \"Darwin\" and python_version < \"3.11\""},
|
||||
{version = ">=1.21.2", markers = "platform_system != \"Darwin\" and python_version >= \"3.10\" and python_version < \"3.11\""},
|
||||
{version = ">=1.26.0", markers = "python_version >= \"3.12\""},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "orjson"
|
||||
version = "3.10.14"
|
||||
version = "3.10.15"
|
||||
description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "orjson-3.10.14-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:849ea7845a55f09965826e816cdc7689d6cf74fe9223d79d758c714af955bcb6"},
|
||||
{file = "orjson-3.10.14-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5947b139dfa33f72eecc63f17e45230a97e741942955a6c9e650069305eb73d"},
|
||||
{file = "orjson-3.10.14-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cde6d76910d3179dae70f164466692f4ea36da124d6fb1a61399ca589e81d69a"},
|
||||
{file = "orjson-3.10.14-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6dfbaeb7afa77ca608a50e2770a0461177b63a99520d4928e27591b142c74b1"},
|
||||
{file = "orjson-3.10.14-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa45e489ef80f28ff0e5ba0a72812b8cfc7c1ef8b46a694723807d1b07c89ebb"},
|
||||
{file = "orjson-3.10.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f5007abfdbb1d866e2aa8990bd1c465f0f6da71d19e695fc278282be12cffa5"},
|
||||
{file = "orjson-3.10.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1b49e2af011c84c3f2d541bb5cd1e3c7c2df672223e7e3ea608f09cf295e5f8a"},
|
||||
{file = "orjson-3.10.14-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:164ac155109226b3a2606ee6dda899ccfbe6e7e18b5bdc3fbc00f79cc074157d"},
|
||||
{file = "orjson-3.10.14-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6b1225024cf0ef5d15934b5ffe9baf860fe8bc68a796513f5ea4f5056de30bca"},
|
||||
{file = "orjson-3.10.14-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d6546e8073dc382e60fcae4a001a5a1bc46da5eab4a4878acc2d12072d6166d5"},
|
||||
{file = "orjson-3.10.14-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9f1d2942605c894162252d6259b0121bf1cb493071a1ea8cb35d79cb3e6ac5bc"},
|
||||
{file = "orjson-3.10.14-cp310-cp310-win32.whl", hash = "sha256:397083806abd51cf2b3bbbf6c347575374d160331a2d33c5823e22249ad3118b"},
|
||||
{file = "orjson-3.10.14-cp310-cp310-win_amd64.whl", hash = "sha256:fa18f949d3183a8d468367056be989666ac2bef3a72eece0bade9cdb733b3c28"},
|
||||
{file = "orjson-3.10.14-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f506fd666dd1ecd15a832bebc66c4df45c1902fd47526292836c339f7ba665a9"},
|
||||
{file = "orjson-3.10.14-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efe5fd254cfb0eeee13b8ef7ecb20f5d5a56ddda8a587f3852ab2cedfefdb5f6"},
|
||||
{file = "orjson-3.10.14-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ddc8c866d7467f5ee2991397d2ea94bcf60d0048bdd8ca555740b56f9042725"},
|
||||
{file = "orjson-3.10.14-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af8e42ae4363773658b8d578d56dedffb4f05ceeb4d1d4dd3fb504950b45526"},
|
||||
{file = "orjson-3.10.14-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84dd83110503bc10e94322bf3ffab8bc49150176b49b4984dc1cce4c0a993bf9"},
|
||||
{file = "orjson-3.10.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36f5bfc0399cd4811bf10ec7a759c7ab0cd18080956af8ee138097d5b5296a95"},
|
||||
{file = "orjson-3.10.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868943660fb2a1e6b6b965b74430c16a79320b665b28dd4511d15ad5038d37d5"},
|
||||
{file = "orjson-3.10.14-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:33449c67195969b1a677533dee9d76e006001213a24501333624623e13c7cc8e"},
|
||||
{file = "orjson-3.10.14-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e4c9f60f9fb0b5be66e416dcd8c9d94c3eabff3801d875bdb1f8ffc12cf86905"},
|
||||
{file = "orjson-3.10.14-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0de4d6315cfdbd9ec803b945c23b3a68207fd47cbe43626036d97e8e9561a436"},
|
||||
{file = "orjson-3.10.14-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:83adda3db595cb1a7e2237029b3249c85afbe5c747d26b41b802e7482cb3933e"},
|
||||
{file = "orjson-3.10.14-cp311-cp311-win32.whl", hash = "sha256:998019ef74a4997a9d741b1473533cdb8faa31373afc9849b35129b4b8ec048d"},
|
||||
{file = "orjson-3.10.14-cp311-cp311-win_amd64.whl", hash = "sha256:9d034abdd36f0f0f2240f91492684e5043d46f290525d1117712d5b8137784eb"},
|
||||
{file = "orjson-3.10.14-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:2ad4b7e367efba6dc3f119c9a0fcd41908b7ec0399a696f3cdea7ec477441b09"},
|
||||
{file = "orjson-3.10.14-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f496286fc85e93ce0f71cc84fc1c42de2decf1bf494094e188e27a53694777a7"},
|
||||
{file = "orjson-3.10.14-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c7f189bbfcded40e41a6969c1068ba305850ba016665be71a217918931416fbf"},
|
||||
{file = "orjson-3.10.14-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8cc8204f0b75606869c707da331058ddf085de29558b516fc43c73ee5ee2aadb"},
|
||||
{file = "orjson-3.10.14-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:deaa2899dff7f03ab667e2ec25842d233e2a6a9e333efa484dfe666403f3501c"},
|
||||
{file = "orjson-3.10.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1c3ea52642c9714dc6e56de8a451a066f6d2707d273e07fe8a9cc1ba073813d"},
|
||||
{file = "orjson-3.10.14-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9d3f9ed72e7458ded9a1fb1b4d4ed4c4fdbaf82030ce3f9274b4dc1bff7ace2b"},
|
||||
{file = "orjson-3.10.14-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:07520685d408a2aba514c17ccc16199ff2934f9f9e28501e676c557f454a37fe"},
|
||||
{file = "orjson-3.10.14-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:76344269b550ea01488d19a2a369ab572c1ac4449a72e9f6ac0d70eb1cbfb953"},
|
||||
{file = "orjson-3.10.14-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e2979d0f2959990620f7e62da6cd954e4620ee815539bc57a8ae46e2dacf90e3"},
|
||||
{file = "orjson-3.10.14-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:03f61ca3674555adcb1aa717b9fc87ae936aa7a63f6aba90a474a88701278780"},
|
||||
{file = "orjson-3.10.14-cp312-cp312-win32.whl", hash = "sha256:d5075c54edf1d6ad81d4c6523ce54a748ba1208b542e54b97d8a882ecd810fd1"},
|
||||
{file = "orjson-3.10.14-cp312-cp312-win_amd64.whl", hash = "sha256:175cafd322e458603e8ce73510a068d16b6e6f389c13f69bf16de0e843d7d406"},
|
||||
{file = "orjson-3.10.14-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:0905ca08a10f7e0e0c97d11359609300eb1437490a7f32bbaa349de757e2e0c7"},
|
||||
{file = "orjson-3.10.14-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92d13292249f9f2a3e418cbc307a9fbbef043c65f4bd8ba1eb620bc2aaba3d15"},
|
||||
{file = "orjson-3.10.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90937664e776ad316d64251e2fa2ad69265e4443067668e4727074fe39676414"},
|
||||
{file = "orjson-3.10.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9ed3d26c4cb4f6babaf791aa46a029265850e80ec2a566581f5c2ee1a14df4f1"},
|
||||
{file = "orjson-3.10.14-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:56ee546c2bbe9599aba78169f99d1dc33301853e897dbaf642d654248280dc6e"},
|
||||
{file = "orjson-3.10.14-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:901e826cb2f1bdc1fcef3ef59adf0c451e8f7c0b5deb26c1a933fb66fb505eae"},
|
||||
{file = "orjson-3.10.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:26336c0d4b2d44636e1e1e6ed1002f03c6aae4a8a9329561c8883f135e9ff010"},
|
||||
{file = "orjson-3.10.14-cp313-cp313-win32.whl", hash = "sha256:e2bc525e335a8545c4e48f84dd0328bc46158c9aaeb8a1c2276546e94540ea3d"},
|
||||
{file = "orjson-3.10.14-cp313-cp313-win_amd64.whl", hash = "sha256:eca04dfd792cedad53dc9a917da1a522486255360cb4e77619343a20d9f35364"},
|
||||
{file = "orjson-3.10.14-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9a0fba3b8a587a54c18585f077dcab6dd251c170d85cfa4d063d5746cd595a0f"},
|
||||
{file = "orjson-3.10.14-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:175abf3d20e737fec47261d278f95031736a49d7832a09ab684026528c4d96db"},
|
||||
{file = "orjson-3.10.14-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:29ca1a93e035d570e8b791b6c0feddd403c6a5388bfe870bf2aa6bba1b9d9b8e"},
|
||||
{file = "orjson-3.10.14-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f77202c80e8ab5a1d1e9faf642343bee5aaf332061e1ada4e9147dbd9eb00c46"},
|
||||
{file = "orjson-3.10.14-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6e2ec73b7099b6a29b40a62e08a23b936423bd35529f8f55c42e27acccde7954"},
|
||||
{file = "orjson-3.10.14-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2d1679df9f9cd9504f8dff24555c1eaabba8aad7f5914f28dab99e3c2552c9d"},
|
||||
{file = "orjson-3.10.14-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:691ab9a13834310a263664313e4f747ceb93662d14a8bdf20eb97d27ed488f16"},
|
||||
{file = "orjson-3.10.14-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:b11ed82054fce82fb74cea33247d825d05ad6a4015ecfc02af5fbce442fbf361"},
|
||||
{file = "orjson-3.10.14-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:e70a1d62b8288677d48f3bea66c21586a5f999c64ecd3878edb7393e8d1b548d"},
|
||||
{file = "orjson-3.10.14-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:16642f10c1ca5611251bd835de9914a4b03095e28a34c8ba6a5500b5074338bd"},
|
||||
{file = "orjson-3.10.14-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3871bad546aa66c155e3f36f99c459780c2a392d502a64e23fb96d9abf338511"},
|
||||
{file = "orjson-3.10.14-cp38-cp38-win32.whl", hash = "sha256:0293a88815e9bb5c90af4045f81ed364d982f955d12052d989d844d6c4e50945"},
|
||||
{file = "orjson-3.10.14-cp38-cp38-win_amd64.whl", hash = "sha256:6169d3868b190d6b21adc8e61f64e3db30f50559dfbdef34a1cd6c738d409dfc"},
|
||||
{file = "orjson-3.10.14-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:06d4ec218b1ec1467d8d64da4e123b4794c781b536203c309ca0f52819a16c03"},
|
||||
{file = "orjson-3.10.14-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:962c2ec0dcaf22b76dee9831fdf0c4a33d4bf9a257a2bc5d4adc00d5c8ad9034"},
|
||||
{file = "orjson-3.10.14-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:21d3be4132f71ef1360385770474f29ea1538a242eef72ac4934fe142800e37f"},
|
||||
{file = "orjson-3.10.14-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c28ed60597c149a9e3f5ad6dd9cebaee6fb2f0e3f2d159a4a2b9b862d4748860"},
|
||||
{file = "orjson-3.10.14-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e947f70167fe18469f2023644e91ab3d24f9aed69a5e1c78e2c81b9cea553fb"},
|
||||
{file = "orjson-3.10.14-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64410696c97a35af2432dea7bdc4ce32416458159430ef1b4beb79fd30093ad6"},
|
||||
{file = "orjson-3.10.14-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8050a5d81c022561ee29cd2739de5b4445f3c72f39423fde80a63299c1892c52"},
|
||||
{file = "orjson-3.10.14-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b49a28e30d3eca86db3fe6f9b7f4152fcacbb4a467953cd1b42b94b479b77956"},
|
||||
{file = "orjson-3.10.14-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ca041ad20291a65d853a9523744eebc3f5a4b2f7634e99f8fe88320695ddf766"},
|
||||
{file = "orjson-3.10.14-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d313a2998b74bb26e9e371851a173a9b9474764916f1fc7971095699b3c6e964"},
|
||||
{file = "orjson-3.10.14-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7796692136a67b3e301ef9052bde6fe8e7bd5200da766811a3a608ffa62aaff0"},
|
||||
{file = "orjson-3.10.14-cp39-cp39-win32.whl", hash = "sha256:eee4bc767f348fba485ed9dc576ca58b0a9eac237f0e160f7a59bce628ed06b3"},
|
||||
{file = "orjson-3.10.14-cp39-cp39-win_amd64.whl", hash = "sha256:96a1c0ee30fb113b3ae3c748fd75ca74a157ff4c58476c47db4d61518962a011"},
|
||||
{file = "orjson-3.10.14.tar.gz", hash = "sha256:cf31f6f071a6b8e7aa1ead1fa27b935b48d00fbfa6a28ce856cfff2d5dd68eed"},
|
||||
{file = "orjson-3.10.15-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:552c883d03ad185f720d0c09583ebde257e41b9521b74ff40e08b7dec4559c04"},
|
||||
{file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:616e3e8d438d02e4854f70bfdc03a6bcdb697358dbaa6bcd19cbe24d24ece1f8"},
|
||||
{file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c2c79fa308e6edb0ffab0a31fd75a7841bf2a79a20ef08a3c6e3b26814c8ca8"},
|
||||
{file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cb85490aa6bf98abd20607ab5c8324c0acb48d6da7863a51be48505646c814"},
|
||||
{file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763dadac05e4e9d2bc14938a45a2d0560549561287d41c465d3c58aec818b164"},
|
||||
{file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a330b9b4734f09a623f74a7490db713695e13b67c959713b78369f26b3dee6bf"},
|
||||
{file = "orjson-3.10.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a61a4622b7ff861f019974f73d8165be1bd9a0855e1cad18ee167acacabeb061"},
|
||||
{file = "orjson-3.10.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:acd271247691574416b3228db667b84775c497b245fa275c6ab90dc1ffbbd2b3"},
|
||||
{file = "orjson-3.10.15-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4759b109c37f635aa5c5cc93a1b26927bfde24b254bcc0e1149a9fada253d2d"},
|
||||
{file = "orjson-3.10.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9e992fd5cfb8b9f00bfad2fd7a05a4299db2bbe92e6440d9dd2fab27655b3182"},
|
||||
{file = "orjson-3.10.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f95fb363d79366af56c3f26b71df40b9a583b07bbaaf5b317407c4d58497852e"},
|
||||
{file = "orjson-3.10.15-cp310-cp310-win32.whl", hash = "sha256:f9875f5fea7492da8ec2444839dcc439b0ef298978f311103d0b7dfd775898ab"},
|
||||
{file = "orjson-3.10.15-cp310-cp310-win_amd64.whl", hash = "sha256:17085a6aa91e1cd70ca8533989a18b5433e15d29c574582f76f821737c8d5806"},
|
||||
{file = "orjson-3.10.15-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c4cc83960ab79a4031f3119cc4b1a1c627a3dc09df125b27c4201dff2af7eaa6"},
|
||||
{file = "orjson-3.10.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ddbeef2481d895ab8be5185f2432c334d6dec1f5d1933a9c83014d188e102cef"},
|
||||
{file = "orjson-3.10.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9e590a0477b23ecd5b0ac865b1b907b01b3c5535f5e8a8f6ab0e503efb896334"},
|
||||
{file = "orjson-3.10.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6be38bd103d2fd9bdfa31c2720b23b5d47c6796bcb1d1b598e3924441b4298d"},
|
||||
{file = "orjson-3.10.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ff4f6edb1578960ed628a3b998fa54d78d9bb3e2eb2cfc5c2a09732431c678d0"},
|
||||
{file = "orjson-3.10.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0482b21d0462eddd67e7fce10b89e0b6ac56570424662b685a0d6fccf581e13"},
|
||||
{file = "orjson-3.10.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bb5cc3527036ae3d98b65e37b7986a918955f85332c1ee07f9d3f82f3a6899b5"},
|
||||
{file = "orjson-3.10.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d569c1c462912acdd119ccbf719cf7102ea2c67dd03b99edcb1a3048651ac96b"},
|
||||
{file = "orjson-3.10.15-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:1e6d33efab6b71d67f22bf2962895d3dc6f82a6273a965fab762e64fa90dc399"},
|
||||
{file = "orjson-3.10.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c33be3795e299f565681d69852ac8c1bc5c84863c0b0030b2b3468843be90388"},
|
||||
{file = "orjson-3.10.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:eea80037b9fae5339b214f59308ef0589fc06dc870578b7cce6d71eb2096764c"},
|
||||
{file = "orjson-3.10.15-cp311-cp311-win32.whl", hash = "sha256:d5ac11b659fd798228a7adba3e37c010e0152b78b1982897020a8e019a94882e"},
|
||||
{file = "orjson-3.10.15-cp311-cp311-win_amd64.whl", hash = "sha256:cf45e0214c593660339ef63e875f32ddd5aa3b4adc15e662cdb80dc49e194f8e"},
|
||||
{file = "orjson-3.10.15-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9d11c0714fc85bfcf36ada1179400862da3288fc785c30e8297844c867d7505a"},
|
||||
{file = "orjson-3.10.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dba5a1e85d554e3897fa9fe6fbcff2ed32d55008973ec9a2b992bd9a65d2352d"},
|
||||
{file = "orjson-3.10.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7723ad949a0ea502df656948ddd8b392780a5beaa4c3b5f97e525191b102fff0"},
|
||||
{file = "orjson-3.10.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6fd9bc64421e9fe9bd88039e7ce8e58d4fead67ca88e3a4014b143cec7684fd4"},
|
||||
{file = "orjson-3.10.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dadba0e7b6594216c214ef7894c4bd5f08d7c0135f4dd0145600be4fbcc16767"},
|
||||
{file = "orjson-3.10.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48f59114fe318f33bbaee8ebeda696d8ccc94c9e90bc27dbe72153094e26f41"},
|
||||
{file = "orjson-3.10.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:035fb83585e0f15e076759b6fedaf0abb460d1765b6a36f48018a52858443514"},
|
||||
{file = "orjson-3.10.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d13b7fe322d75bf84464b075eafd8e7dd9eae05649aa2a5354cfa32f43c59f17"},
|
||||
{file = "orjson-3.10.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7066b74f9f259849629e0d04db6609db4cf5b973248f455ba5d3bd58a4daaa5b"},
|
||||
{file = "orjson-3.10.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:88dc3f65a026bd3175eb157fea994fca6ac7c4c8579fc5a86fc2114ad05705b7"},
|
||||
{file = "orjson-3.10.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b342567e5465bd99faa559507fe45e33fc76b9fb868a63f1642c6bc0735ad02a"},
|
||||
{file = "orjson-3.10.15-cp312-cp312-win32.whl", hash = "sha256:0a4f27ea5617828e6b58922fdbec67b0aa4bb844e2d363b9244c47fa2180e665"},
|
||||
{file = "orjson-3.10.15-cp312-cp312-win_amd64.whl", hash = "sha256:ef5b87e7aa9545ddadd2309efe6824bd3dd64ac101c15dae0f2f597911d46eaa"},
|
||||
{file = "orjson-3.10.15-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:bae0e6ec2b7ba6895198cd981b7cca95d1487d0147c8ed751e5632ad16f031a6"},
|
||||
{file = "orjson-3.10.15-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f93ce145b2db1252dd86af37d4165b6faa83072b46e3995ecc95d4b2301b725a"},
|
||||
{file = "orjson-3.10.15-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c203f6f969210128af3acae0ef9ea6aab9782939f45f6fe02d05958fe761ef9"},
|
||||
{file = "orjson-3.10.15-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8918719572d662e18b8af66aef699d8c21072e54b6c82a3f8f6404c1f5ccd5e0"},
|
||||
{file = "orjson-3.10.15-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f71eae9651465dff70aa80db92586ad5b92df46a9373ee55252109bb6b703307"},
|
||||
{file = "orjson-3.10.15-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e117eb299a35f2634e25ed120c37c641398826c2f5a3d3cc39f5993b96171b9e"},
|
||||
{file = "orjson-3.10.15-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:13242f12d295e83c2955756a574ddd6741c81e5b99f2bef8ed8d53e47a01e4b7"},
|
||||
{file = "orjson-3.10.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7946922ada8f3e0b7b958cc3eb22cfcf6c0df83d1fe5521b4a100103e3fa84c8"},
|
||||
{file = "orjson-3.10.15-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:b7155eb1623347f0f22c38c9abdd738b287e39b9982e1da227503387b81b34ca"},
|
||||
{file = "orjson-3.10.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:208beedfa807c922da4e81061dafa9c8489c6328934ca2a562efa707e049e561"},
|
||||
{file = "orjson-3.10.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eca81f83b1b8c07449e1d6ff7074e82e3fd6777e588f1a6632127f286a968825"},
|
||||
{file = "orjson-3.10.15-cp313-cp313-win32.whl", hash = "sha256:c03cd6eea1bd3b949d0d007c8d57049aa2b39bd49f58b4b2af571a5d3833d890"},
|
||||
{file = "orjson-3.10.15-cp313-cp313-win_amd64.whl", hash = "sha256:fd56a26a04f6ba5fb2045b0acc487a63162a958ed837648c5781e1fe3316cfbf"},
|
||||
{file = "orjson-3.10.15-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:5e8afd6200e12771467a1a44e5ad780614b86abb4b11862ec54861a82d677746"},
|
||||
{file = "orjson-3.10.15-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da9a18c500f19273e9e104cca8c1f0b40a6470bcccfc33afcc088045d0bf5ea6"},
|
||||
{file = "orjson-3.10.15-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb00b7bfbdf5d34a13180e4805d76b4567025da19a197645ca746fc2fb536586"},
|
||||
{file = "orjson-3.10.15-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33aedc3d903378e257047fee506f11e0833146ca3e57a1a1fb0ddb789876c1e1"},
|
||||
{file = "orjson-3.10.15-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd0099ae6aed5eb1fc84c9eb72b95505a3df4267e6962eb93cdd5af03be71c98"},
|
||||
{file = "orjson-3.10.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c864a80a2d467d7786274fce0e4f93ef2a7ca4ff31f7fc5634225aaa4e9e98c"},
|
||||
{file = "orjson-3.10.15-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c25774c9e88a3e0013d7d1a6c8056926b607a61edd423b50eb5c88fd7f2823ae"},
|
||||
{file = "orjson-3.10.15-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:e78c211d0074e783d824ce7bb85bf459f93a233eb67a5b5003498232ddfb0e8a"},
|
||||
{file = "orjson-3.10.15-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:43e17289ffdbbac8f39243916c893d2ae41a2ea1a9cbb060a56a4d75286351ae"},
|
||||
{file = "orjson-3.10.15-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:781d54657063f361e89714293c095f506c533582ee40a426cb6489c48a637b81"},
|
||||
{file = "orjson-3.10.15-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6875210307d36c94873f553786a808af2788e362bd0cf4c8e66d976791e7b528"},
|
||||
{file = "orjson-3.10.15-cp38-cp38-win32.whl", hash = "sha256:305b38b2b8f8083cc3d618927d7f424349afce5975b316d33075ef0f73576b60"},
|
||||
{file = "orjson-3.10.15-cp38-cp38-win_amd64.whl", hash = "sha256:5dd9ef1639878cc3efffed349543cbf9372bdbd79f478615a1c633fe4e4180d1"},
|
||||
{file = "orjson-3.10.15-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ffe19f3e8d68111e8644d4f4e267a069ca427926855582ff01fc012496d19969"},
|
||||
{file = "orjson-3.10.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d433bf32a363823863a96561a555227c18a522a8217a6f9400f00ddc70139ae2"},
|
||||
{file = "orjson-3.10.15-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:da03392674f59a95d03fa5fb9fe3a160b0511ad84b7a3914699ea5a1b3a38da2"},
|
||||
{file = "orjson-3.10.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3a63bb41559b05360ded9132032239e47983a39b151af1201f07ec9370715c82"},
|
||||
{file = "orjson-3.10.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3766ac4702f8f795ff3fa067968e806b4344af257011858cc3d6d8721588b53f"},
|
||||
{file = "orjson-3.10.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a1c73dcc8fadbd7c55802d9aa093b36878d34a3b3222c41052ce6b0fc65f8e8"},
|
||||
{file = "orjson-3.10.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b299383825eafe642cbab34be762ccff9fd3408d72726a6b2a4506d410a71ab3"},
|
||||
{file = "orjson-3.10.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:abc7abecdbf67a173ef1316036ebbf54ce400ef2300b4e26a7b843bd446c2480"},
|
||||
{file = "orjson-3.10.15-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:3614ea508d522a621384c1d6639016a5a2e4f027f3e4a1c93a51867615d28829"},
|
||||
{file = "orjson-3.10.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:295c70f9dc154307777ba30fe29ff15c1bcc9dfc5c48632f37d20a607e9ba85a"},
|
||||
{file = "orjson-3.10.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:63309e3ff924c62404923c80b9e2048c1f74ba4b615e7584584389ada50ed428"},
|
||||
{file = "orjson-3.10.15-cp39-cp39-win32.whl", hash = "sha256:a2f708c62d026fb5340788ba94a55c23df4e1869fec74be455e0b2f5363b8507"},
|
||||
{file = "orjson-3.10.15-cp39-cp39-win_amd64.whl", hash = "sha256:efcf6c735c3d22ef60c4aa27a5238f1a477df85e9b15f2142f9d669beb2d13fd"},
|
||||
{file = "orjson-3.10.15.tar.gz", hash = "sha256:05ca7fe452a2e9d8d9d706a2984c95b9c2ebc5db417ce0b7a49b91d50642a23e"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2498,13 +2496,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.10.5"
|
||||
version = "2.10.6"
|
||||
description = "Data validation using Python type hints"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pydantic-2.10.5-py3-none-any.whl", hash = "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53"},
|
||||
{file = "pydantic-2.10.5.tar.gz", hash = "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff"},
|
||||
{file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"},
|
||||
{file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -2712,13 +2710,13 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments
|
||||
|
||||
[[package]]
|
||||
name = "pytest-asyncio"
|
||||
version = "0.25.2"
|
||||
version = "0.25.3"
|
||||
description = "Pytest support for asyncio"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "pytest_asyncio-0.25.2-py3-none-any.whl", hash = "sha256:0d0bb693f7b99da304a0634afc0a4b19e49d5e0de2d670f38dc4bfa5727c5075"},
|
||||
{file = "pytest_asyncio-0.25.2.tar.gz", hash = "sha256:3f8ef9a98f45948ea91a0ed3dc4268b5326c0e7bce73892acc654df4262ad45f"},
|
||||
{file = "pytest_asyncio-0.25.3-py3-none-any.whl", hash = "sha256:9e89518e0f9bd08928f97a3482fdc4e244df17529460bc038291ccaf8f85c7c3"},
|
||||
{file = "pytest_asyncio-0.25.3.tar.gz", hash = "sha256:fc1da2cf9f125ada7e710b4ddad05518d4cee187ae9412e9ac9271003497f07a"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -3049,29 +3047,29 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.9.2"
|
||||
version = "0.9.6"
|
||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "ruff-0.9.2-py3-none-linux_armv6l.whl", hash = "sha256:80605a039ba1454d002b32139e4970becf84b5fee3a3c3bf1c2af6f61a784347"},
|
||||
{file = "ruff-0.9.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b9aab82bb20afd5f596527045c01e6ae25a718ff1784cb92947bff1f83068b00"},
|
||||
{file = "ruff-0.9.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fbd337bac1cfa96be615f6efcd4bc4d077edbc127ef30e2b8ba2a27e18c054d4"},
|
||||
{file = "ruff-0.9.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b35259b0cbf8daa22a498018e300b9bb0174c2bbb7bcba593935158a78054d"},
|
||||
{file = "ruff-0.9.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b6a9701d1e371bf41dca22015c3f89769da7576884d2add7317ec1ec8cb9c3c"},
|
||||
{file = "ruff-0.9.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9cc53e68b3c5ae41e8faf83a3b89f4a5d7b2cb666dff4b366bb86ed2a85b481f"},
|
||||
{file = "ruff-0.9.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8efd9da7a1ee314b910da155ca7e8953094a7c10d0c0a39bfde3fcfd2a015684"},
|
||||
{file = "ruff-0.9.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3292c5a22ea9a5f9a185e2d131dc7f98f8534a32fb6d2ee7b9944569239c648d"},
|
||||
{file = "ruff-0.9.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a605fdcf6e8b2d39f9436d343d1f0ff70c365a1e681546de0104bef81ce88df"},
|
||||
{file = "ruff-0.9.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c547f7f256aa366834829a08375c297fa63386cbe5f1459efaf174086b564247"},
|
||||
{file = "ruff-0.9.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d18bba3d3353ed916e882521bc3e0af403949dbada344c20c16ea78f47af965e"},
|
||||
{file = "ruff-0.9.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b338edc4610142355ccf6b87bd356729b62bf1bc152a2fad5b0c7dc04af77bfe"},
|
||||
{file = "ruff-0.9.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:492a5e44ad9b22a0ea98cf72e40305cbdaf27fac0d927f8bc9e1df316dcc96eb"},
|
||||
{file = "ruff-0.9.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:af1e9e9fe7b1f767264d26b1075ac4ad831c7db976911fa362d09b2d0356426a"},
|
||||
{file = "ruff-0.9.2-py3-none-win32.whl", hash = "sha256:71cbe22e178c5da20e1514e1e01029c73dc09288a8028a5d3446e6bba87a5145"},
|
||||
{file = "ruff-0.9.2-py3-none-win_amd64.whl", hash = "sha256:c5e1d6abc798419cf46eed03f54f2e0c3adb1ad4b801119dedf23fcaf69b55b5"},
|
||||
{file = "ruff-0.9.2-py3-none-win_arm64.whl", hash = "sha256:a1b63fa24149918f8b37cef2ee6fff81f24f0d74b6f0bdc37bc3e1f2143e41c6"},
|
||||
{file = "ruff-0.9.2.tar.gz", hash = "sha256:b5eceb334d55fae5f316f783437392642ae18e16dcf4f1858d55d3c2a0f8f5d0"},
|
||||
{file = "ruff-0.9.6-py3-none-linux_armv6l.whl", hash = "sha256:2f218f356dd2d995839f1941322ff021c72a492c470f0b26a34f844c29cdf5ba"},
|
||||
{file = "ruff-0.9.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b908ff4df65dad7b251c9968a2e4560836d8f5487c2f0cc238321ed951ea0504"},
|
||||
{file = "ruff-0.9.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b109c0ad2ececf42e75fa99dc4043ff72a357436bb171900714a9ea581ddef83"},
|
||||
{file = "ruff-0.9.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1de4367cca3dac99bcbd15c161404e849bb0bfd543664db39232648dc00112dc"},
|
||||
{file = "ruff-0.9.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac3ee4d7c2c92ddfdaedf0bf31b2b176fa7aa8950efc454628d477394d35638b"},
|
||||
{file = "ruff-0.9.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dc1edd1775270e6aa2386119aea692039781429f0be1e0949ea5884e011aa8e"},
|
||||
{file = "ruff-0.9.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4a091729086dffa4bd070aa5dab7e39cc6b9d62eb2bef8f3d91172d30d599666"},
|
||||
{file = "ruff-0.9.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1bbc6808bf7b15796cef0815e1dfb796fbd383e7dbd4334709642649625e7c5"},
|
||||
{file = "ruff-0.9.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:589d1d9f25b5754ff230dce914a174a7c951a85a4e9270613a2b74231fdac2f5"},
|
||||
{file = "ruff-0.9.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc61dd5131742e21103fbbdcad683a8813be0e3c204472d520d9a5021ca8b217"},
|
||||
{file = "ruff-0.9.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5e2d9126161d0357e5c8f30b0bd6168d2c3872372f14481136d13de9937f79b6"},
|
||||
{file = "ruff-0.9.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:68660eab1a8e65babb5229a1f97b46e3120923757a68b5413d8561f8a85d4897"},
|
||||
{file = "ruff-0.9.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c4cae6c4cc7b9b4017c71114115db0445b00a16de3bcde0946273e8392856f08"},
|
||||
{file = "ruff-0.9.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:19f505b643228b417c1111a2a536424ddde0db4ef9023b9e04a46ed8a1cb4656"},
|
||||
{file = "ruff-0.9.6-py3-none-win32.whl", hash = "sha256:194d8402bceef1b31164909540a597e0d913c0e4952015a5b40e28c146121b5d"},
|
||||
{file = "ruff-0.9.6-py3-none-win_amd64.whl", hash = "sha256:03482d5c09d90d4ee3f40d97578423698ad895c87314c4de39ed2af945633caa"},
|
||||
{file = "ruff-0.9.6-py3-none-win_arm64.whl", hash = "sha256:0e2bb706a2be7ddfea4a4af918562fdc1bcb16df255e5fa595bbd800ce322a5a"},
|
||||
{file = "ruff-0.9.6.tar.gz", hash = "sha256:81761592f72b620ec8fa1068a6fd00e98a5ebee342a3642efd84454f3031dca9"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -541,7 +541,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 193;
|
||||
CURRENT_PROJECT_VERSION = 194;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -685,7 +685,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 193;
|
||||
CURRENT_PROJECT_VERSION = 194;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -715,7 +715,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 193;
|
||||
CURRENT_PROJECT_VERSION = 194;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -748,7 +748,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 193;
|
||||
CURRENT_PROJECT_VERSION = 194;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
@@ -791,7 +791,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 193;
|
||||
CURRENT_PROJECT_VERSION = 194;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
@@ -831,7 +831,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 193;
|
||||
CURRENT_PROJECT_VERSION = 194;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.126.0</string>
|
||||
<string>1.126.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
@@ -93,7 +93,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>193</string>
|
||||
<string>194</string>
|
||||
<key>FLTEnableImpeller</key>
|
||||
<true/>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
|
||||
@@ -59,9 +59,10 @@ class MapBottomSheet extends HookConsumerWidget {
|
||||
child: DraggableScrollableSheet(
|
||||
controller: sheetController,
|
||||
minChildSize: sheetMinExtent,
|
||||
maxChildSize: 0.5,
|
||||
maxChildSize: 0.8,
|
||||
initialChildSize: sheetMinExtent,
|
||||
snap: true,
|
||||
snapSizes: [sheetMinExtent, 0.5, 0.8],
|
||||
shouldCloseOnMinExtent: false,
|
||||
builder: (ctx, scrollController) => MapAssetGrid(
|
||||
controller: scrollController,
|
||||
@@ -78,18 +79,23 @@ class MapBottomSheet extends HookConsumerWidget {
|
||||
),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: bottomSheetOffset,
|
||||
builder: (ctx, value, child) => Positioned(
|
||||
right: 0,
|
||||
bottom: context.height * (value + 0.02),
|
||||
child: child!,
|
||||
),
|
||||
child: ElevatedButton(
|
||||
onPressed: onZoomToLocation,
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: const CircleBorder(),
|
||||
),
|
||||
child: const Icon(Icons.my_location),
|
||||
),
|
||||
builder: (context, value, child) {
|
||||
return Positioned(
|
||||
right: 0,
|
||||
bottom: context.height * (value + 0.02),
|
||||
child: AnimatedOpacity(
|
||||
opacity: value < 0.8 ? 1 : 0,
|
||||
duration: const Duration(milliseconds: 150),
|
||||
child: ElevatedButton(
|
||||
onPressed: onZoomToLocation,
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: const CircleBorder(),
|
||||
),
|
||||
child: const Icon(Icons.my_location),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
105
mobile/openapi/lib/model/asset_bulk_update_dto.dart
generated
105
mobile/openapi/lib/model/asset_bulk_update_dto.dart
generated
@@ -20,6 +20,7 @@ class AssetBulkUpdateDto {
|
||||
this.isFavorite,
|
||||
this.latitude,
|
||||
this.longitude,
|
||||
this.orientation,
|
||||
this.rating,
|
||||
});
|
||||
|
||||
@@ -67,6 +68,8 @@ class AssetBulkUpdateDto {
|
||||
///
|
||||
num? longitude;
|
||||
|
||||
AssetBulkUpdateDtoOrientationEnum? orientation;
|
||||
|
||||
/// Minimum value: -1
|
||||
/// Maximum value: 5
|
||||
///
|
||||
@@ -86,6 +89,7 @@ class AssetBulkUpdateDto {
|
||||
other.isFavorite == isFavorite &&
|
||||
other.latitude == latitude &&
|
||||
other.longitude == longitude &&
|
||||
other.orientation == orientation &&
|
||||
other.rating == rating;
|
||||
|
||||
@override
|
||||
@@ -98,10 +102,11 @@ class AssetBulkUpdateDto {
|
||||
(isFavorite == null ? 0 : isFavorite!.hashCode) +
|
||||
(latitude == null ? 0 : latitude!.hashCode) +
|
||||
(longitude == null ? 0 : longitude!.hashCode) +
|
||||
(orientation == null ? 0 : orientation!.hashCode) +
|
||||
(rating == null ? 0 : rating!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'AssetBulkUpdateDto[dateTimeOriginal=$dateTimeOriginal, duplicateId=$duplicateId, ids=$ids, isArchived=$isArchived, isFavorite=$isFavorite, latitude=$latitude, longitude=$longitude, rating=$rating]';
|
||||
String toString() => 'AssetBulkUpdateDto[dateTimeOriginal=$dateTimeOriginal, duplicateId=$duplicateId, ids=$ids, isArchived=$isArchived, isFavorite=$isFavorite, latitude=$latitude, longitude=$longitude, orientation=$orientation, rating=$rating]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
@@ -136,6 +141,11 @@ class AssetBulkUpdateDto {
|
||||
} else {
|
||||
// json[r'longitude'] = null;
|
||||
}
|
||||
if (this.orientation != null) {
|
||||
json[r'orientation'] = this.orientation;
|
||||
} else {
|
||||
// json[r'orientation'] = null;
|
||||
}
|
||||
if (this.rating != null) {
|
||||
json[r'rating'] = this.rating;
|
||||
} else {
|
||||
@@ -162,6 +172,7 @@ class AssetBulkUpdateDto {
|
||||
isFavorite: mapValueOfType<bool>(json, r'isFavorite'),
|
||||
latitude: num.parse('${json[r'latitude']}'),
|
||||
longitude: num.parse('${json[r'longitude']}'),
|
||||
orientation: AssetBulkUpdateDtoOrientationEnum.fromJson(json[r'orientation']),
|
||||
rating: num.parse('${json[r'rating']}'),
|
||||
);
|
||||
}
|
||||
@@ -214,3 +225,95 @@ class AssetBulkUpdateDto {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
class AssetBulkUpdateDtoOrientationEnum {
|
||||
/// Instantiate a new enum with the provided [value].
|
||||
const AssetBulkUpdateDtoOrientationEnum._(this.value);
|
||||
|
||||
/// The underlying value of this enum member.
|
||||
final int value;
|
||||
|
||||
@override
|
||||
String toString() => value.toString();
|
||||
|
||||
int toJson() => value;
|
||||
|
||||
static const number1 = AssetBulkUpdateDtoOrientationEnum._(1);
|
||||
static const number2 = AssetBulkUpdateDtoOrientationEnum._(2);
|
||||
static const number3 = AssetBulkUpdateDtoOrientationEnum._(3);
|
||||
static const number4 = AssetBulkUpdateDtoOrientationEnum._(4);
|
||||
static const number5 = AssetBulkUpdateDtoOrientationEnum._(5);
|
||||
static const number6 = AssetBulkUpdateDtoOrientationEnum._(6);
|
||||
static const number7 = AssetBulkUpdateDtoOrientationEnum._(7);
|
||||
static const number8 = AssetBulkUpdateDtoOrientationEnum._(8);
|
||||
|
||||
/// List of all possible values in this [enum][AssetBulkUpdateDtoOrientationEnum].
|
||||
static const values = <AssetBulkUpdateDtoOrientationEnum>[
|
||||
number1,
|
||||
number2,
|
||||
number3,
|
||||
number4,
|
||||
number5,
|
||||
number6,
|
||||
number7,
|
||||
number8,
|
||||
];
|
||||
|
||||
static AssetBulkUpdateDtoOrientationEnum? fromJson(dynamic value) => AssetBulkUpdateDtoOrientationEnumTypeTransformer().decode(value);
|
||||
|
||||
static List<AssetBulkUpdateDtoOrientationEnum> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <AssetBulkUpdateDtoOrientationEnum>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = AssetBulkUpdateDtoOrientationEnum.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
}
|
||||
|
||||
/// Transformation class that can [encode] an instance of [AssetBulkUpdateDtoOrientationEnum] to int,
|
||||
/// and [decode] dynamic data back to [AssetBulkUpdateDtoOrientationEnum].
|
||||
class AssetBulkUpdateDtoOrientationEnumTypeTransformer {
|
||||
factory AssetBulkUpdateDtoOrientationEnumTypeTransformer() => _instance ??= const AssetBulkUpdateDtoOrientationEnumTypeTransformer._();
|
||||
|
||||
const AssetBulkUpdateDtoOrientationEnumTypeTransformer._();
|
||||
|
||||
int encode(AssetBulkUpdateDtoOrientationEnum data) => data.value;
|
||||
|
||||
/// Decodes a [dynamic value][data] to a AssetBulkUpdateDtoOrientationEnum.
|
||||
///
|
||||
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
|
||||
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
|
||||
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
|
||||
///
|
||||
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
|
||||
/// and users are still using an old app with the old code.
|
||||
AssetBulkUpdateDtoOrientationEnum? decode(dynamic data, {bool allowNull = true}) {
|
||||
if (data != null) {
|
||||
switch (data) {
|
||||
case 1: return AssetBulkUpdateDtoOrientationEnum.number1;
|
||||
case 2: return AssetBulkUpdateDtoOrientationEnum.number2;
|
||||
case 3: return AssetBulkUpdateDtoOrientationEnum.number3;
|
||||
case 4: return AssetBulkUpdateDtoOrientationEnum.number4;
|
||||
case 5: return AssetBulkUpdateDtoOrientationEnum.number5;
|
||||
case 6: return AssetBulkUpdateDtoOrientationEnum.number6;
|
||||
case 7: return AssetBulkUpdateDtoOrientationEnum.number7;
|
||||
case 8: return AssetBulkUpdateDtoOrientationEnum.number8;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
throw ArgumentError('Unknown enum value to decode: $data');
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Singleton [AssetBulkUpdateDtoOrientationEnumTypeTransformer] instance.
|
||||
static AssetBulkUpdateDtoOrientationEnumTypeTransformer? _instance;
|
||||
}
|
||||
|
||||
|
||||
|
||||
105
mobile/openapi/lib/model/update_asset_dto.dart
generated
105
mobile/openapi/lib/model/update_asset_dto.dart
generated
@@ -20,6 +20,7 @@ class UpdateAssetDto {
|
||||
this.latitude,
|
||||
this.livePhotoVideoId,
|
||||
this.longitude,
|
||||
this.orientation,
|
||||
this.rating,
|
||||
});
|
||||
|
||||
@@ -73,6 +74,8 @@ class UpdateAssetDto {
|
||||
///
|
||||
num? longitude;
|
||||
|
||||
UpdateAssetDtoOrientationEnum? orientation;
|
||||
|
||||
/// Minimum value: -1
|
||||
/// Maximum value: 5
|
||||
///
|
||||
@@ -92,6 +95,7 @@ class UpdateAssetDto {
|
||||
other.latitude == latitude &&
|
||||
other.livePhotoVideoId == livePhotoVideoId &&
|
||||
other.longitude == longitude &&
|
||||
other.orientation == orientation &&
|
||||
other.rating == rating;
|
||||
|
||||
@override
|
||||
@@ -104,10 +108,11 @@ class UpdateAssetDto {
|
||||
(latitude == null ? 0 : latitude!.hashCode) +
|
||||
(livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode) +
|
||||
(longitude == null ? 0 : longitude!.hashCode) +
|
||||
(orientation == null ? 0 : orientation!.hashCode) +
|
||||
(rating == null ? 0 : rating!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'UpdateAssetDto[dateTimeOriginal=$dateTimeOriginal, description=$description, isArchived=$isArchived, isFavorite=$isFavorite, latitude=$latitude, livePhotoVideoId=$livePhotoVideoId, longitude=$longitude, rating=$rating]';
|
||||
String toString() => 'UpdateAssetDto[dateTimeOriginal=$dateTimeOriginal, description=$description, isArchived=$isArchived, isFavorite=$isFavorite, latitude=$latitude, livePhotoVideoId=$livePhotoVideoId, longitude=$longitude, orientation=$orientation, rating=$rating]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
@@ -146,6 +151,11 @@ class UpdateAssetDto {
|
||||
} else {
|
||||
// json[r'longitude'] = null;
|
||||
}
|
||||
if (this.orientation != null) {
|
||||
json[r'orientation'] = this.orientation;
|
||||
} else {
|
||||
// json[r'orientation'] = null;
|
||||
}
|
||||
if (this.rating != null) {
|
||||
json[r'rating'] = this.rating;
|
||||
} else {
|
||||
@@ -170,6 +180,7 @@ class UpdateAssetDto {
|
||||
latitude: num.parse('${json[r'latitude']}'),
|
||||
livePhotoVideoId: mapValueOfType<String>(json, r'livePhotoVideoId'),
|
||||
longitude: num.parse('${json[r'longitude']}'),
|
||||
orientation: UpdateAssetDtoOrientationEnum.fromJson(json[r'orientation']),
|
||||
rating: num.parse('${json[r'rating']}'),
|
||||
);
|
||||
}
|
||||
@@ -221,3 +232,95 @@ class UpdateAssetDto {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
class UpdateAssetDtoOrientationEnum {
|
||||
/// Instantiate a new enum with the provided [value].
|
||||
const UpdateAssetDtoOrientationEnum._(this.value);
|
||||
|
||||
/// The underlying value of this enum member.
|
||||
final int value;
|
||||
|
||||
@override
|
||||
String toString() => value.toString();
|
||||
|
||||
int toJson() => value;
|
||||
|
||||
static const number1 = UpdateAssetDtoOrientationEnum._(1);
|
||||
static const number2 = UpdateAssetDtoOrientationEnum._(2);
|
||||
static const number3 = UpdateAssetDtoOrientationEnum._(3);
|
||||
static const number4 = UpdateAssetDtoOrientationEnum._(4);
|
||||
static const number5 = UpdateAssetDtoOrientationEnum._(5);
|
||||
static const number6 = UpdateAssetDtoOrientationEnum._(6);
|
||||
static const number7 = UpdateAssetDtoOrientationEnum._(7);
|
||||
static const number8 = UpdateAssetDtoOrientationEnum._(8);
|
||||
|
||||
/// List of all possible values in this [enum][UpdateAssetDtoOrientationEnum].
|
||||
static const values = <UpdateAssetDtoOrientationEnum>[
|
||||
number1,
|
||||
number2,
|
||||
number3,
|
||||
number4,
|
||||
number5,
|
||||
number6,
|
||||
number7,
|
||||
number8,
|
||||
];
|
||||
|
||||
static UpdateAssetDtoOrientationEnum? fromJson(dynamic value) => UpdateAssetDtoOrientationEnumTypeTransformer().decode(value);
|
||||
|
||||
static List<UpdateAssetDtoOrientationEnum> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <UpdateAssetDtoOrientationEnum>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = UpdateAssetDtoOrientationEnum.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
}
|
||||
|
||||
/// Transformation class that can [encode] an instance of [UpdateAssetDtoOrientationEnum] to int,
|
||||
/// and [decode] dynamic data back to [UpdateAssetDtoOrientationEnum].
|
||||
class UpdateAssetDtoOrientationEnumTypeTransformer {
|
||||
factory UpdateAssetDtoOrientationEnumTypeTransformer() => _instance ??= const UpdateAssetDtoOrientationEnumTypeTransformer._();
|
||||
|
||||
const UpdateAssetDtoOrientationEnumTypeTransformer._();
|
||||
|
||||
int encode(UpdateAssetDtoOrientationEnum data) => data.value;
|
||||
|
||||
/// Decodes a [dynamic value][data] to a UpdateAssetDtoOrientationEnum.
|
||||
///
|
||||
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
|
||||
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
|
||||
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
|
||||
///
|
||||
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
|
||||
/// and users are still using an old app with the old code.
|
||||
UpdateAssetDtoOrientationEnum? decode(dynamic data, {bool allowNull = true}) {
|
||||
if (data != null) {
|
||||
switch (data) {
|
||||
case 1: return UpdateAssetDtoOrientationEnum.number1;
|
||||
case 2: return UpdateAssetDtoOrientationEnum.number2;
|
||||
case 3: return UpdateAssetDtoOrientationEnum.number3;
|
||||
case 4: return UpdateAssetDtoOrientationEnum.number4;
|
||||
case 5: return UpdateAssetDtoOrientationEnum.number5;
|
||||
case 6: return UpdateAssetDtoOrientationEnum.number6;
|
||||
case 7: return UpdateAssetDtoOrientationEnum.number7;
|
||||
case 8: return UpdateAssetDtoOrientationEnum.number8;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
throw ArgumentError('Unknown enum value to decode: $data');
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Singleton [UpdateAssetDtoOrientationEnumTypeTransformer] instance.
|
||||
static UpdateAssetDtoOrientationEnumTypeTransformer? _instance;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7963,6 +7963,21 @@
|
||||
"longitude": {
|
||||
"type": "number"
|
||||
},
|
||||
"orientation": {
|
||||
"enum": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8
|
||||
],
|
||||
"maximum": 8,
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
},
|
||||
"rating": {
|
||||
"maximum": 5,
|
||||
"minimum": -1,
|
||||
@@ -12880,6 +12895,21 @@
|
||||
"longitude": {
|
||||
"type": "number"
|
||||
},
|
||||
"orientation": {
|
||||
"enum": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8
|
||||
],
|
||||
"maximum": 8,
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
},
|
||||
"rating": {
|
||||
"maximum": 5,
|
||||
"minimum": -1,
|
||||
|
||||
@@ -391,6 +391,7 @@ export type AssetBulkUpdateDto = {
|
||||
isFavorite?: boolean;
|
||||
latitude?: number;
|
||||
longitude?: number;
|
||||
orientation?: Orientation;
|
||||
rating?: number;
|
||||
};
|
||||
export type AssetBulkUploadCheckItem = {
|
||||
@@ -439,6 +440,7 @@ export type UpdateAssetDto = {
|
||||
latitude?: number;
|
||||
livePhotoVideoId?: string | null;
|
||||
longitude?: number;
|
||||
orientation?: Orientation;
|
||||
rating?: number;
|
||||
};
|
||||
export type AssetMediaReplaceDto = {
|
||||
@@ -3481,6 +3483,16 @@ export enum AssetMediaStatus {
|
||||
Replaced = "replaced",
|
||||
Duplicate = "duplicate"
|
||||
}
|
||||
export enum Orientation {
|
||||
$1 = 1,
|
||||
$2 = 2,
|
||||
$3 = 3,
|
||||
$4 = 4,
|
||||
$5 = 5,
|
||||
$6 = 6,
|
||||
$7 = 7,
|
||||
$8 = 8
|
||||
}
|
||||
export enum Action {
|
||||
Accept = "accept",
|
||||
Reject = "reject"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# dev build
|
||||
FROM ghcr.io/immich-app/base-server-dev:20250204@sha256:8b203f19f4d5cf4619b60ee5f50d6d4b5ea3745747f5e5170d1b7404ebeb0792 AS dev
|
||||
FROM ghcr.io/immich-app/base-server-dev:20250211@sha256:6ae577a6518e1ccca973db16955f4d79b01cac3ae122759ccb1c17bf6c330ba9 AS dev
|
||||
|
||||
RUN apt-get install --no-install-recommends -yqq tini
|
||||
WORKDIR /usr/src/app
|
||||
@@ -42,7 +42,7 @@ RUN npm run build
|
||||
|
||||
|
||||
# prod build
|
||||
FROM ghcr.io/immich-app/base-server-prod:20250204@sha256:2af3da713d5ab3ccca23b216b747557ea6016117e72deac101e35069ccaf9b5e
|
||||
FROM ghcr.io/immich-app/base-server-prod:20250211@sha256:879cfe3d2afd4b7bdb211f694f99b6c0679a8bbd3e96964ad6c878f8471d63ea
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
ENV NODE_ENV=production \
|
||||
|
||||
485
server/package-lock.json
generated
485
server/package-lock.json
generated
@@ -19,7 +19,7 @@
|
||||
"@nestjs/swagger": "^11.0.2",
|
||||
"@nestjs/typeorm": "^11.0.0",
|
||||
"@nestjs/websockets": "^11.0.4",
|
||||
"@opentelemetry/auto-instrumentations-node": "^0.55.0",
|
||||
"@opentelemetry/auto-instrumentations-node": "^0.56.0",
|
||||
"@opentelemetry/context-async-hooks": "^1.24.0",
|
||||
"@opentelemetry/exporter-prometheus": "^0.57.0",
|
||||
"@opentelemetry/sdk-node": "^0.57.0",
|
||||
@@ -1178,9 +1178,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz",
|
||||
"integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==",
|
||||
"version": "9.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.19.0.tgz",
|
||||
"integrity": "sha512-rbq9/g38qjfqFLOVPvwjIvFFdNziEC5S65jmjPw5r6A//QH+W91akh9irMwjDN8zKUTak6W9EsAv4m/7Wnw0UQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -2310,9 +2310,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@nestjs/bull-shared": {
|
||||
"version": "11.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/bull-shared/-/bull-shared-11.0.1.tgz",
|
||||
"integrity": "sha512-FqpmIjhCONaYo+5AjtggPdo2lRIM/fv1VHiEq7YwFZBTNSPW0eOvcT96JDb5q4OuvLvADxgpnwP7rmzZywMMiw==",
|
||||
"version": "11.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/bull-shared/-/bull-shared-11.0.2.tgz",
|
||||
"integrity": "sha512-dFlttJvBqIFD6M8JVFbkrR4Feb39OTAJPJpFVILU50NOJCM4qziRw3dSNG84Q3v+7/M6xUGMFdZRRGvBBKxoSA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "2.8.1"
|
||||
@@ -2323,12 +2323,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/bullmq": {
|
||||
"version": "11.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/bullmq/-/bullmq-11.0.1.tgz",
|
||||
"integrity": "sha512-BntU0Zfiyk4R5hlasUV22n1HuqmWWKvsx3knSR5A9/5vce808pmHOmHrtm4GZDs/8Pw9X8UGY8zdLe4a36S6KQ==",
|
||||
"version": "11.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/bullmq/-/bullmq-11.0.2.tgz",
|
||||
"integrity": "sha512-Lq6lGpKkETsm0RDcUktlzsthFoE3A5QTMp2FwPi1eztKqKD6/90KS1TcnC9CJFzjpUaYnQzIMrlNs55e+/wsHA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nestjs/bull-shared": "^11.0.1",
|
||||
"@nestjs/bull-shared": "^11.0.2",
|
||||
"tslib": "2.8.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -2577,9 +2577,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/common": {
|
||||
"version": "11.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.0.6.tgz",
|
||||
"integrity": "sha512-j+M3WOU6loZPNirIHDiZ1LxXRXVNb62XicgLBqdgyrDBFCJrAZaq0lfERUEPlN0/j4GBFnTSPg+CNsoGTBW1zQ==",
|
||||
"version": "11.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.0.8.tgz",
|
||||
"integrity": "sha512-IB6wEl8RgT/vWzb6p3cmBwTY3R0qfQWvO8lW0PfIv4DTJfUiVqNNEikonGuH/6TX8KvRXNhXHCaQZrUN00Xe6g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"iterare": "1.2.1",
|
||||
@@ -2606,9 +2606,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/core": {
|
||||
"version": "11.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.0.6.tgz",
|
||||
"integrity": "sha512-Xf33bwc3waAJ/faJBW06+Dwq3m15p3wbFOc/CcK8ua5EZna4sMjIjXXAb6bQmEjR1KfTXV5z595UD2vwp6cyHg==",
|
||||
"version": "11.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.0.8.tgz",
|
||||
"integrity": "sha512-GQLLdZnjZOmV4Q+TzQ8YuHvEYOneRhzsDbSJRkKdFFAVuoVh+q1nWZy+bZNeTxdWZFGL2Rve70X5jc4MoSXJqQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2680,9 +2680,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express": {
|
||||
"version": "11.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.0.6.tgz",
|
||||
"integrity": "sha512-fP6vrpqDIBaf1FNfFtBeJm/BwtGtueatI4FHxaBgw93XxKmIOV4G4ZO7ouQKqfgyIxV2mkYr/Fhg7hwRmizIjw==",
|
||||
"version": "11.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.0.8.tgz",
|
||||
"integrity": "sha512-Ru7seOYYglKNGQFzNALE5ilLqkdtX/ge6AJDKLMt+WI7iElZ7lXjT40fE3+HVUiZODunmeKQ7jVxcQyZwLafVA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cors": "2.8.5",
|
||||
@@ -2701,9 +2701,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-socket.io": {
|
||||
"version": "11.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.0.6.tgz",
|
||||
"integrity": "sha512-80r6hp0p+YK/+D5srjPTE/fc1w0aTM6ecqprJr8bFlzTPJbtYtpfHqMbEg0UfRKTsZ0krEgpv8fb0K4dSYzW0w==",
|
||||
"version": "11.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.0.8.tgz",
|
||||
"integrity": "sha512-DUpfRSDgxu+z9czB6ddFdQFawSAIr7jEbNOvpjpjYErvDitUdos57FhTw9IJxIm2EAOHoiCk4g3tN59GfjdwfQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"socket.io": "4.8.1",
|
||||
@@ -2720,9 +2720,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/schedule": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-5.0.0.tgz",
|
||||
"integrity": "sha512-RHqJIOo3AQvdeq0WuIFDqa5N0CkgxgqwmWRla96S6GmFV6qkQD1//EeH4k19MeCu4Ac9PzZ2y/Hu0zK9f//BQg==",
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-5.0.1.tgz",
|
||||
"integrity": "sha512-kFoel84I4RyS2LNPH6yHYTKxB16tb3auAEciFuc788C3ph6nABkUfzX5IE+unjVaRX+3GuruJwurNepMlHXpQg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cron": "3.5.0"
|
||||
@@ -2916,9 +2916,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/testing": {
|
||||
"version": "11.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.0.6.tgz",
|
||||
"integrity": "sha512-RZDWdnOncOQ1vT3630VlRzKee2P21ZJoF1+NAY+nzYUuYuYAaBdjrTZQGwymmiZQcrM+TQaViSjSPUmcJXdKyA==",
|
||||
"version": "11.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.0.8.tgz",
|
||||
"integrity": "sha512-5Reqec4MQSm4nFasKE5Pd799cAx3MmjkweF17Wgj/EJNWhFgVdv6N9OUIWXbU8nc8Pjso1fJmv0KJyN6h51qOA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2957,9 +2957,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/websockets": {
|
||||
"version": "11.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.0.6.tgz",
|
||||
"integrity": "sha512-bsYZnmIXmZTYWzLJ3LbusH72kdppKSQLKUe9cfuUPXuTF+FaAX6TvErP11dJJ0W0S1Iwufr+LERTK211Txs8Eg==",
|
||||
"version": "11.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.0.8.tgz",
|
||||
"integrity": "sha512-wyS512+QWhWhE8NU1DgbAPkCaaOSNK1xBIgRlgpYg5/tKuhu4lc5r8iMdZAQn6xay++ELlOqsSlyuj0J2BixOA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"iterare": "1.2.1",
|
||||
@@ -3186,9 +3186,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/auto-instrumentations-node": {
|
||||
"version": "0.55.3",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.55.3.tgz",
|
||||
"integrity": "sha512-tX5k3ZG8Nk6f1DHAF0K1ClP/OiW2hNuSeCVqDHNMcJ58dZSiad0XO2mwrvSipo77/DPXXUl0j9MxqmUVITdujQ==",
|
||||
"version": "0.56.0",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.56.0.tgz",
|
||||
"integrity": "sha512-d1X3DQY0+VmhNUir/3U3JO6Uh0FOSm8G91zsPzVVKc6NGDwmHP6Dn7PMVH70O6FZ0yErzlHqRx8vkNiAsTWt5A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/instrumentation": "^0.57.0",
|
||||
@@ -3198,7 +3198,7 @@
|
||||
"@opentelemetry/instrumentation-bunyan": "^0.45.0",
|
||||
"@opentelemetry/instrumentation-cassandra-driver": "^0.45.0",
|
||||
"@opentelemetry/instrumentation-connect": "^0.43.0",
|
||||
"@opentelemetry/instrumentation-cucumber": "^0.13.0",
|
||||
"@opentelemetry/instrumentation-cucumber": "^0.14.0",
|
||||
"@opentelemetry/instrumentation-dataloader": "^0.16.0",
|
||||
"@opentelemetry/instrumentation-dns": "^0.43.0",
|
||||
"@opentelemetry/instrumentation-express": "^0.47.0",
|
||||
@@ -3218,10 +3218,10 @@
|
||||
"@opentelemetry/instrumentation-mongodb": "^0.51.0",
|
||||
"@opentelemetry/instrumentation-mongoose": "^0.46.0",
|
||||
"@opentelemetry/instrumentation-mysql": "^0.45.0",
|
||||
"@opentelemetry/instrumentation-mysql2": "^0.45.0",
|
||||
"@opentelemetry/instrumentation-mysql2": "^0.45.1",
|
||||
"@opentelemetry/instrumentation-nestjs-core": "^0.44.0",
|
||||
"@opentelemetry/instrumentation-net": "^0.43.0",
|
||||
"@opentelemetry/instrumentation-pg": "^0.50.0",
|
||||
"@opentelemetry/instrumentation-pg": "^0.51.0",
|
||||
"@opentelemetry/instrumentation-pino": "^0.46.0",
|
||||
"@opentelemetry/instrumentation-redis": "^0.46.0",
|
||||
"@opentelemetry/instrumentation-redis-4": "^0.46.0",
|
||||
@@ -3626,9 +3626,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/instrumentation-cucumber": {
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.13.0.tgz",
|
||||
"integrity": "sha512-ZBswBKONU2g7mhjEKF4vkTXxezq16QdvGaD5W4o01/t5KzvCZGQ6hYPsB34miJIj/hh6UrFLRDAjqb7nur5I3Q==",
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.14.0.tgz",
|
||||
"integrity": "sha512-i/GlurL1IM+CnbmItW8kx59YxAp0wu/YQkzQQRU/YGmUjym5g+/dOVjnk/K46lAU49Nn1XyFd7S3ZNf83PHL2Q==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/instrumentation": "^0.57.0",
|
||||
@@ -3952,9 +3952,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/instrumentation-mysql2": {
|
||||
"version": "0.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.45.0.tgz",
|
||||
"integrity": "sha512-qLslv/EPuLj0IXFvcE3b0EqhWI8LKmrgRPIa4gUd8DllbBpqJAvLNJSv3cC6vWwovpbSI3bagNO/3Q2SuXv2xA==",
|
||||
"version": "0.45.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.45.1.tgz",
|
||||
"integrity": "sha512-9R/vxEc02vlSqyQSmXRTvFMZVht8vgSJokKhiWA3z8Idu0mmdKFKeHiuW5yRGxM/WOi+7DWqQfYM7zw/cJc3sA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/instrumentation": "^0.57.0",
|
||||
@@ -4001,14 +4001,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/instrumentation-pg": {
|
||||
"version": "0.50.0",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.50.0.tgz",
|
||||
"integrity": "sha512-TtLxDdYZmBhFswm8UIsrDjh/HFBeDXd4BLmE8h2MxirNHewLJ0VS9UUddKKEverb5Sm2qFVjqRjcU+8Iw4FJ3w==",
|
||||
"version": "0.51.0",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.51.0.tgz",
|
||||
"integrity": "sha512-/NStIcUWUofc11dL7tSgMk25NqvhtbHDCncgm+yc4iJF8Ste2Q/lwUitjfxqj4qWM280uFmBEtcmtMMjbjRU7Q==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "^1.26.0",
|
||||
"@opentelemetry/instrumentation": "^0.57.0",
|
||||
"@opentelemetry/semantic-conventions": "1.27.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.27.0",
|
||||
"@opentelemetry/sql-common": "^0.40.1",
|
||||
"@types/pg": "8.6.1",
|
||||
"@types/pg-pool": "2.0.6"
|
||||
@@ -4020,15 +4020,6 @@
|
||||
"@opentelemetry/api": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/instrumentation-pg/node_modules/@opentelemetry/semantic-conventions": {
|
||||
"version": "1.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz",
|
||||
"integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/instrumentation-pino": {
|
||||
"version": "0.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.46.0.tgz",
|
||||
@@ -5270,9 +5261,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@swc/core": {
|
||||
"version": "1.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.10.7.tgz",
|
||||
"integrity": "sha512-py91kjI1jV5D5W/Q+PurBdGsdU5TFbrzamP7zSCqLdMcHkKi3rQEM5jkQcZr0MXXSJTaayLxS3MWYTBIkzPDrg==",
|
||||
"version": "1.10.14",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.10.14.tgz",
|
||||
"integrity": "sha512-WSrnE6JRnH20ZYjOOgSS4aOaPv9gxlkI2KRkN24kagbZnPZMnN8bZZyzw1rrLvwgpuRGv17Uz+hflosbR+SP6w==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
@@ -5288,16 +5279,16 @@
|
||||
"url": "https://opencollective.com/swc"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@swc/core-darwin-arm64": "1.10.7",
|
||||
"@swc/core-darwin-x64": "1.10.7",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.10.7",
|
||||
"@swc/core-linux-arm64-gnu": "1.10.7",
|
||||
"@swc/core-linux-arm64-musl": "1.10.7",
|
||||
"@swc/core-linux-x64-gnu": "1.10.7",
|
||||
"@swc/core-linux-x64-musl": "1.10.7",
|
||||
"@swc/core-win32-arm64-msvc": "1.10.7",
|
||||
"@swc/core-win32-ia32-msvc": "1.10.7",
|
||||
"@swc/core-win32-x64-msvc": "1.10.7"
|
||||
"@swc/core-darwin-arm64": "1.10.14",
|
||||
"@swc/core-darwin-x64": "1.10.14",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.10.14",
|
||||
"@swc/core-linux-arm64-gnu": "1.10.14",
|
||||
"@swc/core-linux-arm64-musl": "1.10.14",
|
||||
"@swc/core-linux-x64-gnu": "1.10.14",
|
||||
"@swc/core-linux-x64-musl": "1.10.14",
|
||||
"@swc/core-win32-arm64-msvc": "1.10.14",
|
||||
"@swc/core-win32-ia32-msvc": "1.10.14",
|
||||
"@swc/core-win32-x64-msvc": "1.10.14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@swc/helpers": "*"
|
||||
@@ -5309,9 +5300,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-darwin-arm64": {
|
||||
"version": "1.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.7.tgz",
|
||||
"integrity": "sha512-SI0OFg987P6hcyT0Dbng3YRISPS9uhLX1dzW4qRrfqQdb0i75lPJ2YWe9CN47HBazrIA5COuTzrD2Dc0TcVsSQ==",
|
||||
"version": "1.10.14",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.14.tgz",
|
||||
"integrity": "sha512-Dh4VyrhDDb05tdRmqJ/MucOPMTnrB4pRJol18HVyLlqu1HOT5EzonUniNTCdQbUXjgdv5UVJSTE1lYTzrp+myA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -5326,9 +5317,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-darwin-x64": {
|
||||
"version": "1.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.10.7.tgz",
|
||||
"integrity": "sha512-RFIAmWVicD/l3RzxgHW0R/G1ya/6nyMspE2cAeDcTbjHi0I5qgdhBWd6ieXOaqwEwiCd0Mot1g2VZrLGoBLsjQ==",
|
||||
"version": "1.10.14",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.10.14.tgz",
|
||||
"integrity": "sha512-KpzotL/I0O12RE3tF8NmQErINv0cQe/0mnN/Q50ESFzB5kU6bLgp2HMnnwDTm/XEZZRJCNe0oc9WJ5rKbAJFRQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -5343,9 +5334,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-arm-gnueabihf": {
|
||||
"version": "1.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.7.tgz",
|
||||
"integrity": "sha512-QP8vz7yELWfop5mM5foN6KkLylVO7ZUgWSF2cA0owwIaziactB2hCPZY5QU690coJouk9KmdFsPWDnaCFUP8tg==",
|
||||
"version": "1.10.14",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.14.tgz",
|
||||
"integrity": "sha512-20yRXZjMJVz1wp1TcscKiGTVXistG+saIaxOmxSNQia1Qun3hSWLL+u6+5kXbfYGr7R2N6kqSwtZbIfJI25r9Q==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -5360,9 +5351,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-arm64-gnu": {
|
||||
"version": "1.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.7.tgz",
|
||||
"integrity": "sha512-NgUDBGQcOeLNR+EOpmUvSDIP/F7i/OVOKxst4wOvT5FTxhnkWrW+StJGKj+DcUVSK5eWOYboSXr1y+Hlywwokw==",
|
||||
"version": "1.10.14",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.14.tgz",
|
||||
"integrity": "sha512-Gy7cGrNkiMfPxQyLGxdgXPwyWzNzbHuWycJFcoKBihxZKZIW8hkPBttkGivuLC+0qOgsV2/U+S7tlvAju7FtmQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -5377,9 +5368,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-arm64-musl": {
|
||||
"version": "1.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.7.tgz",
|
||||
"integrity": "sha512-gp5Un3EbeSThBIh6oac5ZArV/CsSmTKj5jNuuUAuEsML3VF9vqPO+25VuxCvsRf/z3py+xOWRaN2HY/rjMeZog==",
|
||||
"version": "1.10.14",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.14.tgz",
|
||||
"integrity": "sha512-+oYVqJvFw62InZ8PIy1rBACJPC2WTe4vbVb9kM1jJj2D7dKLm9acnnYIVIDsM5Wo7Uab8RvPHXVbs19IBurzuw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -5394,9 +5385,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-x64-gnu": {
|
||||
"version": "1.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.7.tgz",
|
||||
"integrity": "sha512-k/OxLLMl/edYqbZyUNg6/bqEHTXJT15l9WGqsl/2QaIGwWGvles8YjruQYQ9d4h/thSXLT9gd8bExU2D0N+bUA==",
|
||||
"version": "1.10.14",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.14.tgz",
|
||||
"integrity": "sha512-OmEbVEKQFLQVHwo4EJl9osmlulURy46k232Opfpn/1ji0t2KcNCci3POsnfMuoZjLkGJv8vGNJdPQxX+CP+wSA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -5411,9 +5402,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-x64-musl": {
|
||||
"version": "1.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.7.tgz",
|
||||
"integrity": "sha512-XeDoURdWt/ybYmXLCEE8aSiTOzEn0o3Dx5l9hgt0IZEmTts7HgHHVeRgzGXbR4yDo0MfRuX5nE1dYpTmCz0uyA==",
|
||||
"version": "1.10.14",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.14.tgz",
|
||||
"integrity": "sha512-OZW+Icm8DMPqHbhdxplkuG8qrNnPk5i7xJOZWYi1y5bTjgGFI4nEzrsmmeHKMdQTaWwsFrm3uK1rlyQ48MmXmg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -5428,9 +5419,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-win32-arm64-msvc": {
|
||||
"version": "1.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.7.tgz",
|
||||
"integrity": "sha512-nYAbi/uLS+CU0wFtBx8TquJw2uIMKBnl04LBmiVoFrsIhqSl+0MklaA9FVMGA35NcxSJfcm92Prl2W2LfSnTqQ==",
|
||||
"version": "1.10.14",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.14.tgz",
|
||||
"integrity": "sha512-sTvc+xrDQXy3HXZFtTEClY35Efvuc3D+busYm0+rb1+Thau4HLRY9WP+sOKeGwH9/16rzfzYEqD7Ds8A9ykrHw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -5445,9 +5436,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-win32-ia32-msvc": {
|
||||
"version": "1.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.7.tgz",
|
||||
"integrity": "sha512-+aGAbsDsIxeLxw0IzyQLtvtAcI1ctlXVvVcXZMNXIXtTURM876yNrufRo4ngoXB3jnb1MLjIIjgXfFs/eZTUSw==",
|
||||
"version": "1.10.14",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.14.tgz",
|
||||
"integrity": "sha512-j2iQ4y9GWTKtES5eMU0sDsFdYni7IxME7ejFej25Tv3Fq4B+U9tgtYWlJwh1858nIWDXelHiKcSh/UICAyVMdQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -5462,9 +5453,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-win32-x64-msvc": {
|
||||
"version": "1.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.7.tgz",
|
||||
"integrity": "sha512-TBf4clpDBjF/UUnkKrT0/th76/zwvudk5wwobiTFqDywMApHip5O0VpBgZ+4raY2TM8k5+ujoy7bfHb22zu17Q==",
|
||||
"version": "1.10.14",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.14.tgz",
|
||||
"integrity": "sha512-TYtWkUSMkjs0jGPeWdtWbex4B+DlQZmN/ySVLiPI+EltYCLEXsFMkVFq6aWn48dqFHggFK0UYfvDrJUR2c3Qxg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -5504,13 +5495,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@testcontainers/postgresql": {
|
||||
"version": "10.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-10.16.0.tgz",
|
||||
"integrity": "sha512-zWFQI+3QxlEELRvVv27i6zlVEPNUz9zKaSh7iWmFlCdfhcyr78daS0FG8FIfdQ79VK7YXA4jv+dTYXa2SwXu/w==",
|
||||
"version": "10.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-10.18.0.tgz",
|
||||
"integrity": "sha512-WxkE/tBlBpoKvqDEqL3i/mL6BOBWnXb8FXKtLhEeZ3lSt0zlldkTozMmewNsKJtFTBZdv7uFwMzWyXP12t0sxQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"testcontainers": "^10.16.0"
|
||||
"testcontainers": "^10.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@turf/boolean-point-in-polygon": {
|
||||
@@ -5777,9 +5768,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.17.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.14.tgz",
|
||||
"integrity": "sha512-jsxagdikDiDBeIRaPYtArcT8my4tN1og7MtMRquFT3XNA6axxyHDRUemqDz/taRDdOUn0GnGHRCuff4q48sW9A==",
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.15.tgz",
|
||||
"integrity": "sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -5919,9 +5910,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.7.tgz",
|
||||
"integrity": "sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA==",
|
||||
"version": "19.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.8.tgz",
|
||||
"integrity": "sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -6068,21 +6059,21 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.20.0.tgz",
|
||||
"integrity": "sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==",
|
||||
"version": "8.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.23.0.tgz",
|
||||
"integrity": "sha512-vBz65tJgRrA1Q5gWlRfvoH+w943dq9K1p1yDBY2pc+a1nbBLZp7fB9+Hk8DaALUbzjqlMfgaqlVPT1REJdkt/w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.20.0",
|
||||
"@typescript-eslint/type-utils": "8.20.0",
|
||||
"@typescript-eslint/utils": "8.20.0",
|
||||
"@typescript-eslint/visitor-keys": "8.20.0",
|
||||
"@typescript-eslint/scope-manager": "8.23.0",
|
||||
"@typescript-eslint/type-utils": "8.23.0",
|
||||
"@typescript-eslint/utils": "8.23.0",
|
||||
"@typescript-eslint/visitor-keys": "8.23.0",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.3.1",
|
||||
"natural-compare": "^1.4.0",
|
||||
"ts-api-utils": "^2.0.0"
|
||||
"ts-api-utils": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -6098,16 +6089,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.20.0.tgz",
|
||||
"integrity": "sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==",
|
||||
"version": "8.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.23.0.tgz",
|
||||
"integrity": "sha512-h2lUByouOXFAlMec2mILeELUbME5SZRN/7R9Cw2RD2lRQQY08MWMM+PmVVKKJNK1aIwqTo9t/0CvOxwPbRIE2Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.20.0",
|
||||
"@typescript-eslint/types": "8.20.0",
|
||||
"@typescript-eslint/typescript-estree": "8.20.0",
|
||||
"@typescript-eslint/visitor-keys": "8.20.0",
|
||||
"@typescript-eslint/scope-manager": "8.23.0",
|
||||
"@typescript-eslint/types": "8.23.0",
|
||||
"@typescript-eslint/typescript-estree": "8.23.0",
|
||||
"@typescript-eslint/visitor-keys": "8.23.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -6123,14 +6114,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.20.0.tgz",
|
||||
"integrity": "sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==",
|
||||
"version": "8.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.23.0.tgz",
|
||||
"integrity": "sha512-OGqo7+dXHqI7Hfm+WqkZjKjsiRtFUQHPdGMXzk5mYXhJUedO7e/Y7i8AK3MyLMgZR93TX4bIzYrfyVjLC+0VSw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.20.0",
|
||||
"@typescript-eslint/visitor-keys": "8.20.0"
|
||||
"@typescript-eslint/types": "8.23.0",
|
||||
"@typescript-eslint/visitor-keys": "8.23.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -6141,16 +6132,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.20.0.tgz",
|
||||
"integrity": "sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==",
|
||||
"version": "8.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.23.0.tgz",
|
||||
"integrity": "sha512-iIuLdYpQWZKbiH+RkCGc6iu+VwscP5rCtQ1lyQ7TYuKLrcZoeJVpcLiG8DliXVkUxirW/PWlmS+d6yD51L9jvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "8.20.0",
|
||||
"@typescript-eslint/utils": "8.20.0",
|
||||
"@typescript-eslint/typescript-estree": "8.23.0",
|
||||
"@typescript-eslint/utils": "8.23.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^2.0.0"
|
||||
"ts-api-utils": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -6165,9 +6156,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.20.0.tgz",
|
||||
"integrity": "sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==",
|
||||
"version": "8.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.23.0.tgz",
|
||||
"integrity": "sha512-1sK4ILJbCmZOTt9k4vkoulT6/y5CHJ1qUYxqpF1K/DBAd8+ZUL4LlSCxOssuH5m4rUaaN0uS0HlVPvd45zjduQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -6179,20 +6170,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.20.0.tgz",
|
||||
"integrity": "sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==",
|
||||
"version": "8.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.23.0.tgz",
|
||||
"integrity": "sha512-LcqzfipsB8RTvH8FX24W4UUFk1bl+0yTOf9ZA08XngFwMg4Kj8A+9hwz8Cr/ZS4KwHrmo9PJiLZkOt49vPnuvQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.20.0",
|
||||
"@typescript-eslint/visitor-keys": "8.20.0",
|
||||
"@typescript-eslint/types": "8.23.0",
|
||||
"@typescript-eslint/visitor-keys": "8.23.0",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
"minimatch": "^9.0.4",
|
||||
"semver": "^7.6.0",
|
||||
"ts-api-utils": "^2.0.0"
|
||||
"ts-api-utils": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -6232,16 +6223,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.20.0.tgz",
|
||||
"integrity": "sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==",
|
||||
"version": "8.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.23.0.tgz",
|
||||
"integrity": "sha512-uB/+PSo6Exu02b5ZEiVtmY6RVYO7YU5xqgzTIVZwTHvvK3HsL8tZZHFaTLFtRG3CsV4A5mhOv+NZx5BlhXPyIA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@typescript-eslint/scope-manager": "8.20.0",
|
||||
"@typescript-eslint/types": "8.20.0",
|
||||
"@typescript-eslint/typescript-estree": "8.20.0"
|
||||
"@typescript-eslint/scope-manager": "8.23.0",
|
||||
"@typescript-eslint/types": "8.23.0",
|
||||
"@typescript-eslint/typescript-estree": "8.23.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -6256,13 +6247,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.20.0.tgz",
|
||||
"integrity": "sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==",
|
||||
"version": "8.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.23.0.tgz",
|
||||
"integrity": "sha512-oWWhcWDLwDfu++BGTZcmXWqpwtkwb5o7fxUIGksMQQDSdPW9prsSnfIOZMlsj4vBOSrcnjIUZMiIjODgGosFhQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.20.0",
|
||||
"@typescript-eslint/types": "8.23.0",
|
||||
"eslint-visitor-keys": "^4.2.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -6287,9 +6278,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/coverage-v8": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.3.tgz",
|
||||
"integrity": "sha512-uVbJ/xhImdNtzPnLyxCZJMTeTIYdgcC2nWtBBBpR1H6z0w8m7D+9/zrDIx2nNxgMg9r+X8+RY2qVpUDeW2b3nw==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.5.tgz",
|
||||
"integrity": "sha512-zOOWIsj5fHh3jjGwQg+P+J1FW3s4jBu1Zqga0qW60yutsBtqEqNEJKWYh7cYn1yGD+1bdPsPdC/eL4eVK56xMg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -6310,8 +6301,8 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vitest/browser": "3.0.3",
|
||||
"vitest": "3.0.3"
|
||||
"@vitest/browser": "3.0.5",
|
||||
"vitest": "3.0.5"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vitest/browser": {
|
||||
@@ -6320,14 +6311,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.3.tgz",
|
||||
"integrity": "sha512-SbRCHU4qr91xguu+dH3RUdI5dC86zm8aZWydbp961aIR7G8OYNN6ZiayFuf9WAngRbFOfdrLHCGgXTj3GtoMRQ==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.5.tgz",
|
||||
"integrity": "sha512-nNIOqupgZ4v5jWuQx2DSlHLEs7Q4Oh/7AYwNyE+k0UQzG7tSmjPXShUikn1mpNGzYEN2jJbTvLejwShMitovBA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "3.0.3",
|
||||
"@vitest/utils": "3.0.3",
|
||||
"@vitest/spy": "3.0.5",
|
||||
"@vitest/utils": "3.0.5",
|
||||
"chai": "^5.1.2",
|
||||
"tinyrainbow": "^2.0.0"
|
||||
},
|
||||
@@ -6336,13 +6327,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/mocker": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.3.tgz",
|
||||
"integrity": "sha512-XT2XBc4AN9UdaxJAeIlcSZ0ILi/GzmG5G8XSly4gaiqIvPV3HMTSIDZWJVX6QRJ0PX1m+W8Cy0K9ByXNb/bPIA==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.5.tgz",
|
||||
"integrity": "sha512-CLPNBFBIE7x6aEGbIjaQAX03ZZlBMaWwAjBdMkIf/cAn6xzLTiM3zYqO/WAbieEjsAZir6tO71mzeHZoodThvw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "3.0.3",
|
||||
"@vitest/spy": "3.0.5",
|
||||
"estree-walker": "^3.0.3",
|
||||
"magic-string": "^0.30.17"
|
||||
},
|
||||
@@ -6373,9 +6364,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/pretty-format": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.3.tgz",
|
||||
"integrity": "sha512-gCrM9F7STYdsDoNjGgYXKPq4SkSxwwIU5nkaQvdUxiQ0EcNlez+PdKOVIsUJvh9P9IeIFmjn4IIREWblOBpP2Q==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.5.tgz",
|
||||
"integrity": "sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -6386,38 +6377,38 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/runner": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.3.tgz",
|
||||
"integrity": "sha512-Rgi2kOAk5ZxWZlwPguRJFOBmWs6uvvyAAR9k3MvjRvYrG7xYvKChZcmnnpJCS98311CBDMqsW9MzzRFsj2gX3g==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.5.tgz",
|
||||
"integrity": "sha512-BAiZFityFexZQi2yN4OX3OkJC6scwRo8EhRB0Z5HIGGgd2q+Nq29LgHU/+ovCtd0fOfXj5ZI6pwdlUmC5bpi8A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/utils": "3.0.3",
|
||||
"pathe": "^2.0.1"
|
||||
"@vitest/utils": "3.0.5",
|
||||
"pathe": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/snapshot": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.3.tgz",
|
||||
"integrity": "sha512-kNRcHlI4txBGztuJfPEJ68VezlPAXLRT1u5UCx219TU3kOG2DplNxhWLwDf2h6emwmTPogzLnGVwP6epDaJN6Q==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.5.tgz",
|
||||
"integrity": "sha512-GJPZYcd7v8QNUJ7vRvLDmRwl+a1fGg4T/54lZXe+UOGy47F9yUfE18hRCtXL5aHN/AONu29NGzIXSVFh9K0feA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "3.0.3",
|
||||
"@vitest/pretty-format": "3.0.5",
|
||||
"magic-string": "^0.30.17",
|
||||
"pathe": "^2.0.1"
|
||||
"pathe": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/spy": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.3.tgz",
|
||||
"integrity": "sha512-7/dgux8ZBbF7lEIKNnEqQlyRaER9nkAL9eTmdKJkDO3hS8p59ATGwKOCUDHcBLKr7h/oi/6hP+7djQk8049T2A==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.5.tgz",
|
||||
"integrity": "sha512-5fOzHj0WbUNqPK6blI/8VzZdkBlQLnT25knX0r4dbZI9qoZDf3qAdjoMmDcLG5A83W6oUUFJgUd0EYBc2P5xqg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -6428,13 +6419,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/utils": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.3.tgz",
|
||||
"integrity": "sha512-f+s8CvyzPtMFY1eZKkIHGhPsQgYo5qCm6O8KZoim9qm1/jT64qBgGpO5tHscNH6BzRHM+edLNOP+3vO8+8pE/A==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.5.tgz",
|
||||
"integrity": "sha512-N9AX0NUoUtVwKwy21JtwzaqR5L5R5A99GAbrHfCCXK1lp593i/3AZAXhSP43wRQuxYsflrdzEfXZFo1reR1Nkg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "3.0.3",
|
||||
"@vitest/pretty-format": "3.0.5",
|
||||
"loupe": "^3.1.2",
|
||||
"tinyrainbow": "^2.0.0"
|
||||
},
|
||||
@@ -8836,9 +8827,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.18.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.18.0.tgz",
|
||||
"integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==",
|
||||
"version": "9.19.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.19.0.tgz",
|
||||
"integrity": "sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -8847,7 +8838,7 @@
|
||||
"@eslint/config-array": "^0.19.0",
|
||||
"@eslint/core": "^0.10.0",
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
"@eslint/js": "9.18.0",
|
||||
"@eslint/js": "9.19.0",
|
||||
"@eslint/plugin-kit": "^0.2.5",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
@@ -8909,9 +8900,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-prettier": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.2.tgz",
|
||||
"integrity": "sha512-1yI3/hf35wmlq66C8yOyrujQnel+v5l1Vop5Cl2I6ylyNTT1JbuUUnV3/41PzwTzcyDp/oF0jWE3HXvcH5AQOQ==",
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz",
|
||||
"integrity": "sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -9861,9 +9852,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/geo-tz": {
|
||||
"version": "8.1.2",
|
||||
"resolved": "https://registry.npmjs.org/geo-tz/-/geo-tz-8.1.2.tgz",
|
||||
"integrity": "sha512-S1udoP7MZ+CVu+7Iy/VayVNmEHTWgfJ52TjpfC2/4f+j0SB/ZXMjGrwZTqPMo6/O2m5lrGLCFCY0bkxUqiLN+g==",
|
||||
"version": "8.1.3",
|
||||
"resolved": "https://registry.npmjs.org/geo-tz/-/geo-tz-8.1.3.tgz",
|
||||
"integrity": "sha512-zzF0hjqLl+1n5tXDCxwdS/BmF+N1TdQc6rbubh6PO6/9DtntX/yBox1Ti0q24MrjajWG0fSv0gv2w6Zff/kmeA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@turf/boolean-point-in-polygon": "^7.1.0",
|
||||
@@ -11210,9 +11201,9 @@
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/loupe": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz",
|
||||
"integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==",
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz",
|
||||
"integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -11515,9 +11506,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mock-fs": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.4.1.tgz",
|
||||
"integrity": "sha512-sz/Q8K1gXXXHR+qr0GZg2ysxCRr323kuN10O7CtQjraJsFDJ4SJ+0I5MzALz7aRp9lHk8Cc/YdsT95h9Ka1aFw==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.5.0.tgz",
|
||||
"integrity": "sha512-d/P1M/RacgM3dB0sJ8rjeRNXxtapkPCUnMGmIN0ixJ16F/E4GUZCvWcSGfWGz8eaXYvn1s9baUwNjI4LOPEjiA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -11803,9 +11794,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/nestjs-cls": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nestjs-cls/-/nestjs-cls-5.0.0.tgz",
|
||||
"integrity": "sha512-0rpkCngRISkd2x9z7q0bR8R4AeZe43WkLBaDuTE6Uaw9r1OEWfEGfGS6M4OlXG20CIxDtaAIbWW8wFM5YCNRkA==",
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/nestjs-cls/-/nestjs-cls-5.0.1.tgz",
|
||||
"integrity": "sha512-TA1qkFkkWct1LXXjcNk4jxQgvZrL34bojK3D4oOo59ru7xrQ1erN3/737T+XezOZ2ZPngGVeJZ9WipaD9N+G0Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
@@ -11966,9 +11957,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nodemailer": {
|
||||
"version": "6.9.16",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.16.tgz",
|
||||
"integrity": "sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz",
|
||||
"integrity": "sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA==",
|
||||
"license": "MIT-0",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
@@ -14028,9 +14019,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
|
||||
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@@ -14569,9 +14560,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/sql-formatter": {
|
||||
"version": "15.4.9",
|
||||
"resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.4.9.tgz",
|
||||
"integrity": "sha512-5vmt2HlCAVozxsBZuXWkAki/KGawaK+b5GG5x+BtXOFVpN/8cqppblFUxHl4jxdA0cvo14lABhM+KBnrUapOlw==",
|
||||
"version": "15.4.10",
|
||||
"resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.4.10.tgz",
|
||||
"integrity": "sha512-zQfiuxU1F/C7TNu+880BdL+fuvJTd1Kj8R0wv48dfZ27NR3z1PWvQFkH8ai/HrIy+NyvXCaZBkJHp/EeZFXSOA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -15262,9 +15253,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/testcontainers": {
|
||||
"version": "10.16.0",
|
||||
"resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.16.0.tgz",
|
||||
"integrity": "sha512-oxPLuOtrRWS11A+Yn0+zXB7GkmNarflWqmy6CQJk8KJ75LZs2/zlUXDpizTbPpCGtk4kE2EQYwFZjrE967F8Wg==",
|
||||
"version": "10.18.0",
|
||||
"resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.18.0.tgz",
|
||||
"integrity": "sha512-MnwWsPjsN5QVe+lSU1LwLZVOyjgwSwv1INzkw8FekdwgvOtvJ7FThQEkbmzRcguQootgwmA9FG54NoTChZDRvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -15282,7 +15273,7 @@
|
||||
"ssh-remote-port-forward": "^1.0.4",
|
||||
"tar-fs": "^3.0.6",
|
||||
"tmp": "^0.2.3",
|
||||
"undici": "^5.28.4"
|
||||
"undici": "^5.28.5"
|
||||
}
|
||||
},
|
||||
"node_modules/testcontainers/node_modules/tmp": {
|
||||
@@ -15456,9 +15447,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ts-api-utils": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz",
|
||||
"integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz",
|
||||
"integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -15839,9 +15830,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "5.28.4",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz",
|
||||
"integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==",
|
||||
"version": "5.28.5",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.5.tgz",
|
||||
"integrity": "sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -16078,16 +16069,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite-node": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.3.tgz",
|
||||
"integrity": "sha512-0sQcwhwAEw/UJGojbhOrnq3HtiZ3tC7BzpAa0lx3QaTX0S3YX70iGcik25UBdB96pmdwjyY2uyKNYruxCDmiEg==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.5.tgz",
|
||||
"integrity": "sha512-02JEJl7SbtwSDJdYS537nU6l+ktdvcREfLksk/NDAqtdKWGqHl+joXzEubHROmS3E6pip+Xgu2tFezMu75jH7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cac": "^6.7.14",
|
||||
"debug": "^4.4.0",
|
||||
"es-module-lexer": "^1.6.0",
|
||||
"pathe": "^2.0.1",
|
||||
"pathe": "^2.0.2",
|
||||
"vite": "^5.0.0 || ^6.0.0"
|
||||
},
|
||||
"bin": {
|
||||
@@ -16580,31 +16571,31 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.3.tgz",
|
||||
"integrity": "sha512-dWdwTFUW9rcnL0LyF2F+IfvNQWB0w9DERySCk8VMG75F8k25C7LsZoh6XfCjPvcR8Nb+Lqi9JKr6vnzH7HSrpQ==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.5.tgz",
|
||||
"integrity": "sha512-4dof+HvqONw9bvsYxtkfUp2uHsTN9bV2CZIi1pWgoFpL1Lld8LA1ka9q/ONSsoScAKG7NVGf2stJTI7XRkXb2Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/expect": "3.0.3",
|
||||
"@vitest/mocker": "3.0.3",
|
||||
"@vitest/pretty-format": "^3.0.3",
|
||||
"@vitest/runner": "3.0.3",
|
||||
"@vitest/snapshot": "3.0.3",
|
||||
"@vitest/spy": "3.0.3",
|
||||
"@vitest/utils": "3.0.3",
|
||||
"@vitest/expect": "3.0.5",
|
||||
"@vitest/mocker": "3.0.5",
|
||||
"@vitest/pretty-format": "^3.0.5",
|
||||
"@vitest/runner": "3.0.5",
|
||||
"@vitest/snapshot": "3.0.5",
|
||||
"@vitest/spy": "3.0.5",
|
||||
"@vitest/utils": "3.0.5",
|
||||
"chai": "^5.1.2",
|
||||
"debug": "^4.4.0",
|
||||
"expect-type": "^1.1.0",
|
||||
"magic-string": "^0.30.17",
|
||||
"pathe": "^2.0.1",
|
||||
"pathe": "^2.0.2",
|
||||
"std-env": "^3.8.0",
|
||||
"tinybench": "^2.9.0",
|
||||
"tinyexec": "^0.3.2",
|
||||
"tinypool": "^1.0.2",
|
||||
"tinyrainbow": "^2.0.0",
|
||||
"vite": "^5.0.0 || ^6.0.0",
|
||||
"vite-node": "3.0.3",
|
||||
"vite-node": "3.0.5",
|
||||
"why-is-node-running": "^2.3.0"
|
||||
},
|
||||
"bin": {
|
||||
@@ -16618,9 +16609,10 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@edge-runtime/vm": "*",
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
||||
"@vitest/browser": "3.0.3",
|
||||
"@vitest/ui": "3.0.3",
|
||||
"@vitest/browser": "3.0.5",
|
||||
"@vitest/ui": "3.0.5",
|
||||
"happy-dom": "*",
|
||||
"jsdom": "*"
|
||||
},
|
||||
@@ -16628,6 +16620,9 @@
|
||||
"@edge-runtime/vm": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/debug": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
},
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"@nestjs/swagger": "^11.0.2",
|
||||
"@nestjs/typeorm": "^11.0.0",
|
||||
"@nestjs/websockets": "^11.0.4",
|
||||
"@opentelemetry/auto-instrumentations-node": "^0.55.0",
|
||||
"@opentelemetry/auto-instrumentations-node": "^0.56.0",
|
||||
"@opentelemetry/context-async-hooks": "^1.24.0",
|
||||
"@opentelemetry/exporter-prometheus": "^0.57.0",
|
||||
"@opentelemetry/sdk-node": "^0.57.0",
|
||||
|
||||
@@ -13,22 +13,23 @@ import { IWorker } from 'src/constants';
|
||||
import { controllers } from 'src/controllers';
|
||||
import { entities } from 'src/entities';
|
||||
import { ImmichWorker } from 'src/enum';
|
||||
import { IEventRepository } from 'src/interfaces/event.interface';
|
||||
import { IJobRepository } from 'src/interfaces/job.interface';
|
||||
import { AuthGuard } from 'src/middleware/auth.guard';
|
||||
import { ErrorInterceptor } from 'src/middleware/error.interceptor';
|
||||
import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor';
|
||||
import { GlobalExceptionFilter } from 'src/middleware/global-exception.filter';
|
||||
import { LoggingInterceptor } from 'src/middleware/logging.interceptor';
|
||||
import { providers, repositories } from 'src/repositories';
|
||||
import { repositories } from 'src/repositories';
|
||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||
import { EventRepository } from 'src/repositories/event.repository';
|
||||
import { JobRepository } from 'src/repositories/job.repository';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import { teardownTelemetry, TelemetryRepository } from 'src/repositories/telemetry.repository';
|
||||
import { services } from 'src/services';
|
||||
import { AuthService } from 'src/services/auth.service';
|
||||
import { CliService } from 'src/services/cli.service';
|
||||
import { DatabaseService } from 'src/services/database.service';
|
||||
|
||||
const common = [...services, ...providers, ...repositories];
|
||||
const common = [...repositories, ...services];
|
||||
|
||||
const middleware = [
|
||||
FileUploadInterceptor,
|
||||
@@ -78,21 +79,30 @@ class BaseModule implements OnModuleInit, OnModuleDestroy {
|
||||
constructor(
|
||||
@Inject(IWorker) private worker: ImmichWorker,
|
||||
logger: LoggingRepository,
|
||||
@Inject(IEventRepository) private eventRepository: IEventRepository,
|
||||
@Inject(IJobRepository) private jobRepository: IJobRepository,
|
||||
private eventRepository: EventRepository,
|
||||
private jobRepository: JobRepository,
|
||||
private telemetryRepository: TelemetryRepository,
|
||||
private authService: AuthService,
|
||||
) {
|
||||
logger.setAppName(this.worker);
|
||||
}
|
||||
|
||||
async onModuleInit() {
|
||||
this.telemetryRepository.setup({ repositories: [...providers.map(({ useClass }) => useClass), ...repositories] });
|
||||
this.telemetryRepository.setup({ repositories });
|
||||
|
||||
this.jobRepository.setup({ services });
|
||||
if (this.worker === ImmichWorker.MICROSERVICES) {
|
||||
this.jobRepository.startWorkers();
|
||||
}
|
||||
|
||||
this.eventRepository.setAuthFn(async (client) =>
|
||||
this.authService.authenticate({
|
||||
headers: client.request.headers,
|
||||
queryParams: {},
|
||||
metadata: { adminRoute: false, sharedLinkRoute: false, uri: '/api/socket.io' },
|
||||
}),
|
||||
);
|
||||
|
||||
this.eventRepository.setup({ services });
|
||||
await this.eventRepository.emit('app.bootstrap');
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Reflector } from '@nestjs/core';
|
||||
import { SchedulerRegistry } from '@nestjs/schedule';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { ClassConstructor } from 'class-transformer';
|
||||
import { PostgresJSDialect } from 'kysely-postgres-js';
|
||||
import { KyselyModule } from 'nestjs-kysely';
|
||||
import { OpenTelemetryModule } from 'nestjs-otel';
|
||||
@@ -13,7 +14,7 @@ import postgres from 'postgres';
|
||||
import { format } from 'sql-formatter';
|
||||
import { GENERATE_SQL_KEY, GenerateSqlQueries } from 'src/decorators';
|
||||
import { entities } from 'src/entities';
|
||||
import { providers, repositories } from 'src/repositories';
|
||||
import { repositories } from 'src/repositories';
|
||||
import { AccessRepository } from 'src/repositories/access.repository';
|
||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
@@ -45,8 +46,7 @@ export class SqlLogger implements Logger {
|
||||
|
||||
const reflector = new Reflector();
|
||||
|
||||
type Repository = (typeof providers)[0]['useClass'];
|
||||
type Provider = { provide: any; useClass: Repository };
|
||||
type Repository = ClassConstructor<any>;
|
||||
type SqlGeneratorOptions = { targetDir: string };
|
||||
|
||||
class SqlGenerator {
|
||||
@@ -59,15 +59,11 @@ class SqlGenerator {
|
||||
async run() {
|
||||
try {
|
||||
await this.setup();
|
||||
const targets = [
|
||||
...providers,
|
||||
...repositories.map((repository) => ({ provide: repository, useClass: repository as any })),
|
||||
];
|
||||
for (const repository of targets) {
|
||||
if (repository.provide === LoggingRepository) {
|
||||
for (const Repository of repositories) {
|
||||
if (Repository === LoggingRepository) {
|
||||
continue;
|
||||
}
|
||||
await this.process(repository);
|
||||
await this.process(Repository);
|
||||
}
|
||||
await this.write();
|
||||
this.stats();
|
||||
@@ -105,19 +101,19 @@ class SqlGenerator {
|
||||
TypeOrmModule.forFeature(entities),
|
||||
OpenTelemetryModule.forRoot(otel),
|
||||
],
|
||||
providers: [...providers, ...repositories, AuthService, SchedulerRegistry],
|
||||
providers: [...repositories, AuthService, SchedulerRegistry],
|
||||
}).compile();
|
||||
|
||||
this.app = await moduleFixture.createNestApplication().init();
|
||||
}
|
||||
|
||||
async process({ provide: token, useClass: Repository }: Provider) {
|
||||
async process(Repository: Repository) {
|
||||
if (!this.app) {
|
||||
throw new Error('Not initialized');
|
||||
}
|
||||
|
||||
const data: string[] = [`-- NOTE: This file is auto generated by ./sql-generator`];
|
||||
const instance = this.app.get<Repository>(token);
|
||||
const instance = this.app.get<Repository>(Repository);
|
||||
|
||||
// normal repositories
|
||||
data.push(...(await this.runTargets(instance, `${Repository.name}`)));
|
||||
|
||||
@@ -5,14 +5,14 @@ import {
|
||||
CQMode,
|
||||
ImageFormat,
|
||||
LogLevel,
|
||||
QueueName,
|
||||
ToneMapping,
|
||||
TranscodeHWAccel,
|
||||
TranscodePolicy,
|
||||
VideoCodec,
|
||||
VideoContainer,
|
||||
} from 'src/enum';
|
||||
import { ConcurrentQueueName, QueueName } from 'src/interfaces/job.interface';
|
||||
import { ImageOptions } from 'src/types';
|
||||
import { ConcurrentQueueName, ImageOptions } from 'src/types';
|
||||
|
||||
export interface SystemConfig {
|
||||
backup: {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Duration } from 'luxon';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { SemVer } from 'semver';
|
||||
import { ExifOrientation } from 'src/enum';
|
||||
import { DatabaseExtension, ExifOrientation } from 'src/enum';
|
||||
|
||||
export const POSTGRES_VERSION_RANGE = '>=14.0.0';
|
||||
export const VECTORS_VERSION_RANGE = '>=0.2 <0.4';
|
||||
@@ -16,6 +16,16 @@ export const LIFECYCLE_EXTENSION = 'x-immich-lifecycle';
|
||||
export const DEPRECATED_IN_PREFIX = 'This property was deprecated in ';
|
||||
export const ADDED_IN_PREFIX = 'This property was added in ';
|
||||
|
||||
export const JOBS_ASSET_PAGINATION_SIZE = 1000;
|
||||
export const JOBS_LIBRARY_PAGINATION_SIZE = 10_000;
|
||||
|
||||
export const EXTENSION_NAMES: Record<DatabaseExtension, string> = {
|
||||
cube: 'cube',
|
||||
earthdistance: 'earthdistance',
|
||||
vector: 'pgvector',
|
||||
vectors: 'pgvecto.rs',
|
||||
} as const;
|
||||
|
||||
export const SALT_ROUNDS = 10;
|
||||
|
||||
export const IWorker = 'IWorker';
|
||||
|
||||
@@ -35,9 +35,10 @@ import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import { ImmichHeader, RouteKey } from 'src/enum';
|
||||
import { AssetUploadInterceptor } from 'src/middleware/asset-upload.interceptor';
|
||||
import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard';
|
||||
import { FileUploadInterceptor, UploadFiles, getFiles } from 'src/middleware/file-upload.interceptor';
|
||||
import { FileUploadInterceptor, getFiles } from 'src/middleware/file-upload.interceptor';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import { AssetMediaService } from 'src/services/asset-media.service';
|
||||
import { UploadFiles } from 'src/types';
|
||||
import { sendFile } from 'src/utils/file';
|
||||
import { FileNotEmptyValidator, UUIDParamDto } from 'src/validation';
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ export class UserController {
|
||||
|
||||
@Get('me')
|
||||
@Authenticated()
|
||||
getMyUser(@Auth() auth: AuthDto): UserAdminResponseDto {
|
||||
getMyUser(@Auth() auth: AuthDto): Promise<UserAdminResponseDto> {
|
||||
return this.service.getMe(auth);
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ export class UserController {
|
||||
|
||||
@Get('me/preferences')
|
||||
@Authenticated()
|
||||
getMyPreferences(@Auth() auth: AuthDto): UserPreferencesResponseDto {
|
||||
getMyPreferences(@Auth() auth: AuthDto): Promise<UserPreferencesResponseDto> {
|
||||
return this.service.getMyPreferences(auth);
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ export class UserController {
|
||||
|
||||
@Get('me/license')
|
||||
@Authenticated()
|
||||
getUserLicense(@Auth() auth: AuthDto): LicenseResponseDto {
|
||||
getUserLicense(@Auth() auth: AuthDto): Promise<LicenseResponseDto> {
|
||||
return this.service.getLicense(auth);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ vitest.mock('src/constants', () => ({
|
||||
APP_MEDIA_LOCATION: '/photos',
|
||||
ADDED_IN_PREFIX: 'This property was added in ',
|
||||
DEPRECATED_IN_PREFIX: 'This property was deprecated in ',
|
||||
IWorker: 'IWorker',
|
||||
}));
|
||||
|
||||
describe('StorageCore', () => {
|
||||
|
||||
@@ -4,13 +4,13 @@ import { APP_MEDIA_LOCATION } from 'src/constants';
|
||||
import { AssetEntity } from 'src/entities/asset.entity';
|
||||
import { PersonEntity } from 'src/entities/person.entity';
|
||||
import { AssetFileType, AssetPathType, ImageFormat, PathType, PersonPathType, StorageFolder } from 'src/enum';
|
||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
||||
import { IMoveRepository } from 'src/interfaces/move.interface';
|
||||
import { IPersonRepository } from 'src/interfaces/person.interface';
|
||||
import { IStorageRepository } from 'src/interfaces/storage.interface';
|
||||
import { AssetRepository } from 'src/repositories/asset.repository';
|
||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||
import { CryptoRepository } from 'src/repositories/crypto.repository';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import { MoveRepository } from 'src/repositories/move.repository';
|
||||
import { PersonRepository } from 'src/repositories/person.repository';
|
||||
import { StorageRepository } from 'src/repositories/storage.repository';
|
||||
import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository';
|
||||
import { getAssetFiles } from 'src/utils/asset.util';
|
||||
import { getConfig } from 'src/utils/config';
|
||||
@@ -33,23 +33,23 @@ let instance: StorageCore | null;
|
||||
|
||||
export class StorageCore {
|
||||
private constructor(
|
||||
private assetRepository: IAssetRepository,
|
||||
private assetRepository: AssetRepository,
|
||||
private configRepository: ConfigRepository,
|
||||
private cryptoRepository: CryptoRepository,
|
||||
private moveRepository: IMoveRepository,
|
||||
private personRepository: IPersonRepository,
|
||||
private storageRepository: IStorageRepository,
|
||||
private moveRepository: MoveRepository,
|
||||
private personRepository: PersonRepository,
|
||||
private storageRepository: StorageRepository,
|
||||
private systemMetadataRepository: SystemMetadataRepository,
|
||||
private logger: LoggingRepository,
|
||||
) {}
|
||||
|
||||
static create(
|
||||
assetRepository: IAssetRepository,
|
||||
assetRepository: AssetRepository,
|
||||
configRepository: ConfigRepository,
|
||||
cryptoRepository: CryptoRepository,
|
||||
moveRepository: IMoveRepository,
|
||||
personRepository: IPersonRepository,
|
||||
storageRepository: IStorageRepository,
|
||||
moveRepository: MoveRepository,
|
||||
personRepository: PersonRepository,
|
||||
storageRepository: StorageRepository,
|
||||
systemMetadataRepository: SystemMetadataRepository,
|
||||
logger: LoggingRepository,
|
||||
) {
|
||||
|
||||
@@ -1,3 +1,53 @@
|
||||
import { Permission } from 'src/enum';
|
||||
|
||||
export type AuthUser = {
|
||||
id: string;
|
||||
isAdmin: boolean;
|
||||
name: string;
|
||||
email: string;
|
||||
quotaUsageInBytes: number;
|
||||
quotaSizeInBytes: number | null;
|
||||
};
|
||||
|
||||
export type AuthApiKey = {
|
||||
id: string;
|
||||
permissions: Permission[];
|
||||
};
|
||||
|
||||
export type AuthSharedLink = {
|
||||
id: string;
|
||||
expiresAt: Date | null;
|
||||
userId: string;
|
||||
showExif: boolean;
|
||||
allowUpload: boolean;
|
||||
allowDownload: boolean;
|
||||
password: string | null;
|
||||
};
|
||||
|
||||
export type AuthSession = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export const columns = {
|
||||
authUser: [
|
||||
'users.id',
|
||||
'users.name',
|
||||
'users.email',
|
||||
'users.isAdmin',
|
||||
'users.quotaUsageInBytes',
|
||||
'users.quotaSizeInBytes',
|
||||
],
|
||||
authApiKey: ['api_keys.id', 'api_keys.permissions'],
|
||||
authSession: ['sessions.id', 'sessions.updatedAt'],
|
||||
authSharedLink: [
|
||||
'shared_links.id',
|
||||
'shared_links.userId',
|
||||
'shared_links.expiresAt',
|
||||
'shared_links.showExif',
|
||||
'shared_links.allowUpload',
|
||||
'shared_links.allowDownload',
|
||||
'shared_links.password',
|
||||
],
|
||||
userDto: ['id', 'name', 'email', 'profileImagePath', 'profileChangedAt'],
|
||||
apiKey: ['id', 'name', 'userId', 'createdAt', 'updatedAt', 'permissions'],
|
||||
} as const;
|
||||
|
||||
32
server/src/db.d.ts
vendored
32
server/src/db.d.ts
vendored
@@ -3,23 +3,19 @@
|
||||
* Please do not edit it manually.
|
||||
*/
|
||||
|
||||
import type { ColumnType } from "kysely";
|
||||
import type { ColumnType } from 'kysely';
|
||||
import { Permission } from 'src/enum';
|
||||
|
||||
export type ArrayType<T> = ArrayTypeImpl<T> extends (infer U)[]
|
||||
? U[]
|
||||
: ArrayTypeImpl<T>;
|
||||
export type ArrayType<T> = ArrayTypeImpl<T> extends (infer U)[] ? U[] : ArrayTypeImpl<T>;
|
||||
|
||||
export type ArrayTypeImpl<T> = T extends ColumnType<infer S, infer I, infer U>
|
||||
? ColumnType<S[], I[], U[]>
|
||||
: T[];
|
||||
export type ArrayTypeImpl<T> = T extends ColumnType<infer S, infer I, infer U> ? ColumnType<S[], I[], U[]> : T[];
|
||||
|
||||
export type AssetsStatusEnum = "active" | "deleted" | "trashed";
|
||||
export type AssetsStatusEnum = 'active' | 'deleted' | 'trashed';
|
||||
|
||||
export type Generated<T> = T extends ColumnType<infer S, infer I, infer U>
|
||||
? ColumnType<S, I | undefined, U>
|
||||
: ColumnType<T, T | undefined, T>;
|
||||
export type Generated<T> =
|
||||
T extends ColumnType<infer S, infer I, infer U> ? ColumnType<S, I | undefined, U> : ColumnType<T, T | undefined, T>;
|
||||
|
||||
export type Int8 = ColumnType<string, bigint | number | string, bigint | number | string>;
|
||||
export type Int8 = ColumnType<number>;
|
||||
|
||||
export type Json = JsonValue;
|
||||
|
||||
@@ -33,7 +29,7 @@ export type JsonPrimitive = boolean | number | string | null;
|
||||
|
||||
export type JsonValue = JsonArray | JsonObject | JsonPrimitive;
|
||||
|
||||
export type Sourcetype = "exif" | "machine-learning";
|
||||
export type Sourcetype = 'exif' | 'machine-learning';
|
||||
|
||||
export type Timestamp = ColumnType<Date, Date | string, Date | string>;
|
||||
|
||||
@@ -81,7 +77,7 @@ export interface ApiKeys {
|
||||
id: Generated<string>;
|
||||
key: string;
|
||||
name: string;
|
||||
permissions: string[];
|
||||
permissions: Permission[];
|
||||
updatedAt: Generated<Timestamp>;
|
||||
userId: string;
|
||||
}
|
||||
@@ -126,8 +122,8 @@ export interface Assets {
|
||||
duplicateId: string | null;
|
||||
duration: string | null;
|
||||
encodedVideoPath: Generated<string | null>;
|
||||
fileCreatedAt: Timestamp;
|
||||
fileModifiedAt: Timestamp;
|
||||
fileCreatedAt: Timestamp | null;
|
||||
fileModifiedAt: Timestamp | null;
|
||||
id: Generated<string>;
|
||||
isArchived: Generated<boolean>;
|
||||
isExternal: Generated<boolean>;
|
||||
@@ -136,7 +132,7 @@ export interface Assets {
|
||||
isVisible: Generated<boolean>;
|
||||
libraryId: string | null;
|
||||
livePhotoVideoId: string | null;
|
||||
localDateTime: Timestamp;
|
||||
localDateTime: Timestamp | null;
|
||||
originalFileName: string;
|
||||
originalPath: string;
|
||||
ownerId: string;
|
||||
@@ -444,6 +440,6 @@ export interface DB {
|
||||
typeorm_metadata: TypeormMetadata;
|
||||
user_metadata: UserMetadata;
|
||||
users: Users;
|
||||
"vectors.pg_vector_index_stat": VectorsPgVectorIndexStat;
|
||||
'vectors.pg_vector_index_stat': VectorsPgVectorIndexStat;
|
||||
version_history: VersionHistory;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,8 @@ import { SetMetadata, applyDecorators } from '@nestjs/common';
|
||||
import { ApiExtension, ApiOperation, ApiProperty, ApiTags } from '@nestjs/swagger';
|
||||
import _ from 'lodash';
|
||||
import { ADDED_IN_PREFIX, DEPRECATED_IN_PREFIX, LIFECYCLE_EXTENSION } from 'src/constants';
|
||||
import { ImmichWorker, MetadataKey } from 'src/enum';
|
||||
import { EmitEvent } from 'src/interfaces/event.interface';
|
||||
import { JobName, QueueName } from 'src/interfaces/job.interface';
|
||||
import { ImmichWorker, JobName, MetadataKey, QueueName } from 'src/enum';
|
||||
import { EmitEvent } from 'src/repositories/event.repository';
|
||||
import { setUnion } from 'src/utils/set';
|
||||
|
||||
// PostgreSQL uses a 16-bit integer to indicate the number of bound parameters. This means that the
|
||||
|
||||
@@ -14,8 +14,8 @@ import {
|
||||
ValidateIf,
|
||||
} from 'class-validator';
|
||||
import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
||||
import { AssetType } from 'src/enum';
|
||||
import { AssetStats } from 'src/interfaces/asset.interface';
|
||||
import { AssetType, ExifOrientation } from 'src/enum';
|
||||
import { AssetStats } from 'src/repositories/asset.repository';
|
||||
import { Optional, ValidateBoolean, ValidateUUID } from 'src/validation';
|
||||
|
||||
export class DeviceIdDto {
|
||||
@@ -54,6 +54,12 @@ export class UpdateAssetBase {
|
||||
@Max(5)
|
||||
@Min(-1)
|
||||
rating?: number;
|
||||
|
||||
@Optional()
|
||||
@Min(1)
|
||||
@Max(8)
|
||||
@ApiProperty({ type: 'integer' })
|
||||
orientation?: ExifOrientation;
|
||||
}
|
||||
|
||||
export class AssetBulkUpdateDto extends UpdateAssetBase {
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Transform } from 'class-transformer';
|
||||
import { IsEmail, IsNotEmpty, IsString, MinLength } from 'class-validator';
|
||||
import { SessionEntity } from 'src/entities/session.entity';
|
||||
import { SharedLinkEntity } from 'src/entities/shared-link.entity';
|
||||
import { AuthApiKey, AuthSession, AuthSharedLink, AuthUser } from 'src/database';
|
||||
import { UserEntity } from 'src/entities/user.entity';
|
||||
import { ImmichCookie } from 'src/enum';
|
||||
import { AuthApiKey } from 'src/types';
|
||||
import { toEmail } from 'src/validation';
|
||||
|
||||
export type CookieResponse = {
|
||||
@@ -14,11 +12,11 @@ export type CookieResponse = {
|
||||
};
|
||||
|
||||
export class AuthDto {
|
||||
user!: UserEntity;
|
||||
user!: AuthUser;
|
||||
|
||||
apiKey?: AuthApiKey;
|
||||
sharedLink?: SharedLinkEntity;
|
||||
session?: SessionEntity;
|
||||
sharedLink?: AuthSharedLink;
|
||||
session?: AuthSession;
|
||||
}
|
||||
|
||||
export class LoginCredentialDto {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsEnum, IsNotEmpty } from 'class-validator';
|
||||
import { ManualJobName } from 'src/enum';
|
||||
import { JobCommand, QueueName } from 'src/interfaces/job.interface';
|
||||
import { JobCommand, ManualJobName, QueueName } from 'src/enum';
|
||||
import { ValidateBoolean } from 'src/validation';
|
||||
|
||||
export class JobIdParamDto {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsEnum, IsNotEmpty } from 'class-validator';
|
||||
import { UserResponseDto } from 'src/dtos/user.dto';
|
||||
import { PartnerDirection } from 'src/interfaces/partner.interface';
|
||||
import { PartnerDirection } from 'src/repositories/partner.repository';
|
||||
|
||||
export class UpdatePartnerDto {
|
||||
@IsNotEmpty()
|
||||
|
||||
@@ -25,13 +25,14 @@ import {
|
||||
Colorspace,
|
||||
ImageFormat,
|
||||
LogLevel,
|
||||
QueueName,
|
||||
ToneMapping,
|
||||
TranscodeHWAccel,
|
||||
TranscodePolicy,
|
||||
VideoCodec,
|
||||
VideoContainer,
|
||||
} from 'src/enum';
|
||||
import { ConcurrentQueueName, QueueName } from 'src/interfaces/job.interface';
|
||||
import { ConcurrentQueueName } from 'src/types';
|
||||
import { IsCronExpression, ValidateBoolean } from 'src/validation';
|
||||
|
||||
const isLibraryScanEnabled = (config: SystemConfigLibraryScanDto) => config.enabled;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
|
||||
import { AssetOrder } from 'src/enum';
|
||||
import { TimeBucketSize } from 'src/interfaces/asset.interface';
|
||||
import { TimeBucketSize } from 'src/repositories/asset.repository';
|
||||
import { Optional, ValidateBoolean, ValidateUUID } from 'src/validation';
|
||||
|
||||
export class TimeBucketDto {
|
||||
|
||||
@@ -47,7 +47,7 @@ export const mapUser = (entity: UserEntity): UserResponseDto => {
|
||||
email: entity.email,
|
||||
name: entity.name,
|
||||
profileImagePath: entity.profileImagePath,
|
||||
avatarColor: getPreferences(entity).avatar.color,
|
||||
avatarColor: getPreferences(entity.email, entity.metadata || []).avatar.color,
|
||||
profileChangedAt: entity.profileChangedAt,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -13,8 +13,8 @@ import { StackEntity } from 'src/entities/stack.entity';
|
||||
import { TagEntity } from 'src/entities/tag.entity';
|
||||
import { UserEntity } from 'src/entities/user.entity';
|
||||
import { AssetFileType, AssetStatus, AssetType } from 'src/enum';
|
||||
import { TimeBucketSize } from 'src/interfaces/asset.interface';
|
||||
import { AssetSearchBuilderOptions } from 'src/interfaces/search.interface';
|
||||
import { TimeBucketSize } from 'src/repositories/asset.repository';
|
||||
import { AssetSearchBuilderOptions } from 'src/repositories/search.repository';
|
||||
import { anyUuid, asUuid } from 'src/utils/database';
|
||||
import {
|
||||
Column,
|
||||
@@ -100,13 +100,13 @@ export class AssetEntity {
|
||||
deletedAt!: Date | null;
|
||||
|
||||
@Index('idx_asset_file_created_at')
|
||||
@Column({ type: 'timestamptz' })
|
||||
@Column({ type: 'timestamptz', nullable: true, default: null })
|
||||
fileCreatedAt!: Date;
|
||||
|
||||
@Column({ type: 'timestamptz' })
|
||||
@Column({ type: 'timestamptz', nullable: true, default: null })
|
||||
localDateTime!: Date;
|
||||
|
||||
@Column({ type: 'timestamptz' })
|
||||
@Column({ type: 'timestamptz', nullable: true, default: null })
|
||||
fileModifiedAt!: Date;
|
||||
|
||||
@Column({ type: 'boolean', default: false })
|
||||
@@ -180,6 +180,12 @@ export class AssetEntity {
|
||||
duplicateId!: string | null;
|
||||
}
|
||||
|
||||
export type AssetEntityPlaceholder = AssetEntity & {
|
||||
fileCreatedAt: Date | null;
|
||||
fileModifiedAt: Date | null;
|
||||
localDateTime: Date | null;
|
||||
};
|
||||
|
||||
export function withExif<O>(qb: SelectQueryBuilder<DB, 'assets', O>) {
|
||||
return qb.leftJoin('exif', 'assets.id', 'exif.assetId').select((eb) => eb.fn.toJson(eb.table('exif')).as('exifInfo'));
|
||||
}
|
||||
@@ -419,5 +425,8 @@ export function searchAssetBuilder(kysely: Kysely<DB>, options: AssetSearchBuild
|
||||
)
|
||||
.$if(!!options.withExif, withExifInner)
|
||||
.$if(!!(options.withFaces || options.withPeople || options.personIds), (qb) => qb.select(withFacesAndPeople))
|
||||
.$if(!options.withDeleted, (qb) => qb.where('assets.deletedAt', 'is', null));
|
||||
.$if(!options.withDeleted, (qb) => qb.where('assets.deletedAt', 'is', null))
|
||||
.where('assets.fileCreatedAt', 'is not', null)
|
||||
.where('assets.fileModifiedAt', 'is not', null)
|
||||
.where('assets.localDateTime', 'is not', null);
|
||||
}
|
||||
|
||||
@@ -4,13 +4,18 @@ import { DeepPartial } from 'src/types';
|
||||
import { HumanReadableSize } from 'src/utils/bytes';
|
||||
import { Column, Entity, ManyToOne, PrimaryColumn } from 'typeorm';
|
||||
|
||||
export type UserMetadataItem<T extends keyof UserMetadata = UserMetadataKey> = {
|
||||
key: T;
|
||||
value: UserMetadata[T];
|
||||
};
|
||||
|
||||
@Entity('user_metadata')
|
||||
export class UserMetadataEntity<T extends keyof UserMetadata = UserMetadataKey> {
|
||||
export class UserMetadataEntity<T extends keyof UserMetadata = UserMetadataKey> implements UserMetadataItem<T> {
|
||||
@PrimaryColumn({ type: 'uuid' })
|
||||
userId!: string;
|
||||
|
||||
@ManyToOne(() => UserEntity, (user) => user.metadata, { onUpdate: 'CASCADE', onDelete: 'CASCADE' })
|
||||
user!: UserEntity;
|
||||
user?: UserEntity;
|
||||
|
||||
@PrimaryColumn({ type: 'varchar' })
|
||||
key!: T;
|
||||
|
||||
@@ -384,3 +384,156 @@ export enum ExifOrientation {
|
||||
MirrorHorizontalRotate90CW = 7,
|
||||
Rotate270CW = 8,
|
||||
}
|
||||
|
||||
export enum DatabaseExtension {
|
||||
CUBE = 'cube',
|
||||
EARTH_DISTANCE = 'earthdistance',
|
||||
VECTOR = 'vector',
|
||||
VECTORS = 'vectors',
|
||||
}
|
||||
|
||||
export enum BootstrapEventPriority {
|
||||
// Database service should be initialized before anything else, most other services need database access
|
||||
DatabaseService = -200,
|
||||
// Initialise config after other bootstrap services, stop other services from using config on bootstrap
|
||||
SystemConfig = 100,
|
||||
}
|
||||
|
||||
export enum QueueName {
|
||||
THUMBNAIL_GENERATION = 'thumbnailGeneration',
|
||||
METADATA_EXTRACTION = 'metadataExtraction',
|
||||
VIDEO_CONVERSION = 'videoConversion',
|
||||
FACE_DETECTION = 'faceDetection',
|
||||
FACIAL_RECOGNITION = 'facialRecognition',
|
||||
SMART_SEARCH = 'smartSearch',
|
||||
DUPLICATE_DETECTION = 'duplicateDetection',
|
||||
BACKGROUND_TASK = 'backgroundTask',
|
||||
STORAGE_TEMPLATE_MIGRATION = 'storageTemplateMigration',
|
||||
MIGRATION = 'migration',
|
||||
SEARCH = 'search',
|
||||
SIDECAR = 'sidecar',
|
||||
LIBRARY = 'library',
|
||||
NOTIFICATION = 'notifications',
|
||||
BACKUP_DATABASE = 'backupDatabase',
|
||||
}
|
||||
|
||||
export enum JobName {
|
||||
//backups
|
||||
BACKUP_DATABASE = 'database-backup',
|
||||
|
||||
// conversion
|
||||
QUEUE_VIDEO_CONVERSION = 'queue-video-conversion',
|
||||
VIDEO_CONVERSION = 'video-conversion',
|
||||
|
||||
// thumbnails
|
||||
QUEUE_GENERATE_THUMBNAILS = 'queue-generate-thumbnails',
|
||||
GENERATE_THUMBNAILS = 'generate-thumbnails',
|
||||
GENERATE_PERSON_THUMBNAIL = 'generate-person-thumbnail',
|
||||
|
||||
// metadata
|
||||
QUEUE_METADATA_EXTRACTION = 'queue-metadata-extraction',
|
||||
METADATA_EXTRACTION = 'metadata-extraction',
|
||||
LINK_LIVE_PHOTOS = 'link-live-photos',
|
||||
|
||||
// user
|
||||
USER_DELETION = 'user-deletion',
|
||||
USER_DELETE_CHECK = 'user-delete-check',
|
||||
USER_SYNC_USAGE = 'user-sync-usage',
|
||||
|
||||
// asset
|
||||
ASSET_DELETION = 'asset-deletion',
|
||||
ASSET_DELETION_CHECK = 'asset-deletion-check',
|
||||
|
||||
// storage template
|
||||
STORAGE_TEMPLATE_MIGRATION = 'storage-template-migration',
|
||||
STORAGE_TEMPLATE_MIGRATION_SINGLE = 'storage-template-migration-single',
|
||||
|
||||
// tags
|
||||
TAG_CLEANUP = 'tag-cleanup',
|
||||
|
||||
// migration
|
||||
QUEUE_MIGRATION = 'queue-migration',
|
||||
MIGRATE_ASSET = 'migrate-asset',
|
||||
MIGRATE_PERSON = 'migrate-person',
|
||||
|
||||
// facial recognition
|
||||
PERSON_CLEANUP = 'person-cleanup',
|
||||
QUEUE_FACE_DETECTION = 'queue-face-detection',
|
||||
FACE_DETECTION = 'face-detection',
|
||||
QUEUE_FACIAL_RECOGNITION = 'queue-facial-recognition',
|
||||
FACIAL_RECOGNITION = 'facial-recognition',
|
||||
|
||||
// library management
|
||||
LIBRARY_QUEUE_SYNC_FILES = 'library-queue-sync-files',
|
||||
LIBRARY_QUEUE_SYNC_ASSETS = 'library-queue-sync-assets',
|
||||
LIBRARY_SYNC_FILE = 'library-sync-file',
|
||||
LIBRARY_SYNC_ASSET = 'library-sync-asset',
|
||||
LIBRARY_DELETE = 'library-delete',
|
||||
LIBRARY_QUEUE_SYNC_ALL = 'library-queue-sync-all',
|
||||
LIBRARY_QUEUE_CLEANUP = 'library-queue-cleanup',
|
||||
|
||||
// cleanup
|
||||
DELETE_FILES = 'delete-files',
|
||||
CLEAN_OLD_AUDIT_LOGS = 'clean-old-audit-logs',
|
||||
CLEAN_OLD_SESSION_TOKENS = 'clean-old-session-tokens',
|
||||
|
||||
// smart search
|
||||
QUEUE_SMART_SEARCH = 'queue-smart-search',
|
||||
SMART_SEARCH = 'smart-search',
|
||||
|
||||
QUEUE_TRASH_EMPTY = 'queue-trash-empty',
|
||||
|
||||
// duplicate detection
|
||||
QUEUE_DUPLICATE_DETECTION = 'queue-duplicate-detection',
|
||||
DUPLICATE_DETECTION = 'duplicate-detection',
|
||||
|
||||
// XMP sidecars
|
||||
QUEUE_SIDECAR = 'queue-sidecar',
|
||||
SIDECAR_DISCOVERY = 'sidecar-discovery',
|
||||
SIDECAR_SYNC = 'sidecar-sync',
|
||||
SIDECAR_WRITE = 'sidecar-write',
|
||||
|
||||
// Notification
|
||||
NOTIFY_SIGNUP = 'notify-signup',
|
||||
NOTIFY_ALBUM_INVITE = 'notify-album-invite',
|
||||
NOTIFY_ALBUM_UPDATE = 'notify-album-update',
|
||||
SEND_EMAIL = 'notification-send-email',
|
||||
|
||||
// Version check
|
||||
VERSION_CHECK = 'version-check',
|
||||
}
|
||||
|
||||
export enum JobCommand {
|
||||
START = 'start',
|
||||
PAUSE = 'pause',
|
||||
RESUME = 'resume',
|
||||
EMPTY = 'empty',
|
||||
CLEAR_FAILED = 'clear-failed',
|
||||
}
|
||||
|
||||
export enum JobStatus {
|
||||
SUCCESS = 'success',
|
||||
FAILED = 'failed',
|
||||
SKIPPED = 'skipped',
|
||||
}
|
||||
|
||||
export enum QueueCleanType {
|
||||
FAILED = 'failed',
|
||||
}
|
||||
|
||||
export enum VectorIndex {
|
||||
CLIP = 'clip_index',
|
||||
FACE = 'face_index',
|
||||
}
|
||||
|
||||
export enum DatabaseLock {
|
||||
GeodataImport = 100,
|
||||
Migrations = 200,
|
||||
SystemFileMounts = 300,
|
||||
StorageTemplateMigration = 420,
|
||||
VersionHistory = 500,
|
||||
CLIPDimSize = 512,
|
||||
Library = 1337,
|
||||
GetSystemConfig = 69,
|
||||
BackupDatabase = 42,
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import { Insertable, Updateable } from 'kysely';
|
||||
import { Albums } from 'src/db';
|
||||
import { AlbumUserCreateDto } from 'src/dtos/album.dto';
|
||||
import { AlbumEntity } from 'src/entities/album.entity';
|
||||
import { IBulkAsset } from 'src/utils/asset.util';
|
||||
|
||||
export const IAlbumRepository = 'IAlbumRepository';
|
||||
|
||||
export interface AlbumAssetCount {
|
||||
albumId: string;
|
||||
assetCount: number;
|
||||
startDate: Date | null;
|
||||
endDate: Date | null;
|
||||
}
|
||||
|
||||
export interface AlbumInfoOptions {
|
||||
withAssets: boolean;
|
||||
}
|
||||
|
||||
export interface IAlbumRepository extends IBulkAsset {
|
||||
getById(id: string, options: AlbumInfoOptions): Promise<AlbumEntity | undefined>;
|
||||
getByAssetId(ownerId: string, assetId: string): Promise<AlbumEntity[]>;
|
||||
removeAsset(assetId: string): Promise<void>;
|
||||
getMetadataForIds(ids: string[]): Promise<AlbumAssetCount[]>;
|
||||
getOwned(ownerId: string): Promise<AlbumEntity[]>;
|
||||
getShared(ownerId: string): Promise<AlbumEntity[]>;
|
||||
getNotShared(ownerId: string): Promise<AlbumEntity[]>;
|
||||
restoreAll(userId: string): Promise<void>;
|
||||
softDeleteAll(userId: string): Promise<void>;
|
||||
deleteAll(userId: string): Promise<void>;
|
||||
create(album: Insertable<Albums>, assetIds: string[], albumUsers: AlbumUserCreateDto[]): Promise<AlbumEntity>;
|
||||
update(id: string, album: Updateable<Albums>): Promise<AlbumEntity>;
|
||||
delete(id: string): Promise<void>;
|
||||
updateThumbnails(): Promise<number | undefined>;
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
import { Insertable, Updateable } from 'kysely';
|
||||
import { AssetFiles, AssetJobStatus, Assets, Exif } from 'src/db';
|
||||
import { AssetEntity } from 'src/entities/asset.entity';
|
||||
import { AssetFileType, AssetOrder, AssetStatus, AssetType } from 'src/enum';
|
||||
import { AssetSearchOptions, SearchExploreItem } from 'src/interfaces/search.interface';
|
||||
import { Paginated, PaginationOptions } from 'src/utils/pagination';
|
||||
|
||||
export type AssetStats = Record<AssetType, number>;
|
||||
|
||||
export interface AssetStatsOptions {
|
||||
isFavorite?: boolean;
|
||||
isArchived?: boolean;
|
||||
isTrashed?: boolean;
|
||||
}
|
||||
|
||||
export interface LivePhotoSearchOptions {
|
||||
ownerId: string;
|
||||
libraryId?: string | null;
|
||||
livePhotoCID: string;
|
||||
otherAssetId: string;
|
||||
type: AssetType;
|
||||
}
|
||||
|
||||
export enum WithoutProperty {
|
||||
THUMBNAIL = 'thumbnail',
|
||||
ENCODED_VIDEO = 'encoded-video',
|
||||
EXIF = 'exif',
|
||||
SMART_SEARCH = 'smart-search',
|
||||
DUPLICATE = 'duplicate',
|
||||
FACES = 'faces',
|
||||
SIDECAR = 'sidecar',
|
||||
}
|
||||
|
||||
export enum WithProperty {
|
||||
SIDECAR = 'sidecar',
|
||||
}
|
||||
|
||||
export enum TimeBucketSize {
|
||||
DAY = 'DAY',
|
||||
MONTH = 'MONTH',
|
||||
}
|
||||
|
||||
export interface AssetBuilderOptions {
|
||||
isArchived?: boolean;
|
||||
isFavorite?: boolean;
|
||||
isTrashed?: boolean;
|
||||
isDuplicate?: boolean;
|
||||
albumId?: string;
|
||||
tagId?: string;
|
||||
personId?: string;
|
||||
userIds?: string[];
|
||||
withStacked?: boolean;
|
||||
exifInfo?: boolean;
|
||||
status?: AssetStatus;
|
||||
assetType?: AssetType;
|
||||
}
|
||||
|
||||
export interface TimeBucketOptions extends AssetBuilderOptions {
|
||||
size: TimeBucketSize;
|
||||
order?: AssetOrder;
|
||||
}
|
||||
|
||||
export interface TimeBucketItem {
|
||||
timeBucket: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface MonthDay {
|
||||
day: number;
|
||||
month: number;
|
||||
}
|
||||
|
||||
export interface AssetExploreFieldOptions {
|
||||
maxFields: number;
|
||||
minAssetsPerField: number;
|
||||
}
|
||||
|
||||
export interface AssetFullSyncOptions {
|
||||
ownerId: string;
|
||||
lastId?: string;
|
||||
updatedUntil: Date;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
export interface AssetDeltaSyncOptions {
|
||||
userIds: string[];
|
||||
updatedAfter: Date;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
export interface AssetUpdateDuplicateOptions {
|
||||
targetDuplicateId: string | null;
|
||||
assetIds: string[];
|
||||
duplicateIds: string[];
|
||||
}
|
||||
|
||||
export interface UpsertFileOptions {
|
||||
assetId: string;
|
||||
type: AssetFileType;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface AssetGetByChecksumOptions {
|
||||
ownerId: string;
|
||||
checksum: Buffer;
|
||||
libraryId?: string;
|
||||
}
|
||||
|
||||
export type AssetPathEntity = Pick<AssetEntity, 'id' | 'originalPath' | 'isOffline'>;
|
||||
|
||||
export interface GetByIdsRelations {
|
||||
exifInfo?: boolean;
|
||||
faces?: { person?: boolean };
|
||||
files?: boolean;
|
||||
library?: boolean;
|
||||
owner?: boolean;
|
||||
smartSearch?: boolean;
|
||||
stack?: { assets?: boolean };
|
||||
tags?: boolean;
|
||||
}
|
||||
|
||||
export interface DuplicateGroup {
|
||||
duplicateId: string;
|
||||
assets: AssetEntity[];
|
||||
}
|
||||
|
||||
export interface DayOfYearAssets {
|
||||
yearsAgo: number;
|
||||
assets: AssetEntity[];
|
||||
}
|
||||
|
||||
export const IAssetRepository = 'IAssetRepository';
|
||||
|
||||
export interface IAssetRepository {
|
||||
create(asset: Insertable<Assets>): Promise<AssetEntity>;
|
||||
getByIds(ids: string[], relations?: GetByIdsRelations): Promise<AssetEntity[]>;
|
||||
getByIdsWithAllRelations(ids: string[]): Promise<AssetEntity[]>;
|
||||
getByDayOfYear(ownerIds: string[], monthDay: MonthDay): Promise<DayOfYearAssets[]>;
|
||||
getByChecksum(options: AssetGetByChecksumOptions): Promise<AssetEntity | undefined>;
|
||||
getByChecksums(userId: string, checksums: Buffer[]): Promise<AssetEntity[]>;
|
||||
getUploadAssetIdByChecksum(ownerId: string, checksum: Buffer): Promise<string | undefined>;
|
||||
getByAlbumId(pagination: PaginationOptions, albumId: string): Paginated<AssetEntity>;
|
||||
getByDeviceIds(ownerId: string, deviceId: string, deviceAssetIds: string[]): Promise<string[]>;
|
||||
getByUserId(pagination: PaginationOptions, userId: string, options?: AssetSearchOptions): Paginated<AssetEntity>;
|
||||
getById(id: string, relations?: GetByIdsRelations): Promise<AssetEntity | undefined>;
|
||||
getWithout(pagination: PaginationOptions, property: WithoutProperty): Paginated<AssetEntity>;
|
||||
getRandom(userIds: string[], count: number): Promise<AssetEntity[]>;
|
||||
getLastUpdatedAssetForAlbumId(albumId: string): Promise<AssetEntity | undefined>;
|
||||
getByLibraryIdAndOriginalPath(libraryId: string, originalPath: string): Promise<AssetEntity | undefined>;
|
||||
deleteAll(ownerId: string): Promise<void>;
|
||||
getAll(pagination: PaginationOptions, options?: AssetSearchOptions): Paginated<AssetEntity>;
|
||||
getAllByDeviceId(userId: string, deviceId: string): Promise<string[]>;
|
||||
getLivePhotoCount(motionId: string): Promise<number>;
|
||||
updateAll(ids: string[], options: Updateable<Assets>): Promise<void>;
|
||||
updateDuplicates(options: AssetUpdateDuplicateOptions): Promise<void>;
|
||||
update(asset: Updateable<Assets> & { id: string }): Promise<AssetEntity>;
|
||||
remove(asset: AssetEntity): Promise<void>;
|
||||
findLivePhotoMatch(options: LivePhotoSearchOptions): Promise<AssetEntity | undefined>;
|
||||
getStatistics(ownerId: string, options: AssetStatsOptions): Promise<AssetStats>;
|
||||
getTimeBuckets(options: TimeBucketOptions): Promise<TimeBucketItem[]>;
|
||||
getTimeBucket(timeBucket: string, options: TimeBucketOptions): Promise<AssetEntity[]>;
|
||||
upsertExif(exif: Insertable<Exif>): Promise<void>;
|
||||
upsertJobStatus(...jobStatus: Insertable<AssetJobStatus>[]): Promise<void>;
|
||||
getAssetIdByCity(userId: string, options: AssetExploreFieldOptions): Promise<SearchExploreItem<string>>;
|
||||
getDuplicates(userId: string): Promise<DuplicateGroup[]>;
|
||||
getAllForUserFullSync(options: AssetFullSyncOptions): Promise<AssetEntity[]>;
|
||||
getChangedDeltaSync(options: AssetDeltaSyncOptions): Promise<AssetEntity[]>;
|
||||
upsertFile(options: Insertable<AssetFiles>): Promise<void>;
|
||||
upsertFiles(options: Insertable<AssetFiles>[]): Promise<void>;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
export const ICryptoRepository = 'ICryptoRepository';
|
||||
|
||||
export interface ICryptoRepository {
|
||||
randomBytes(size: number): Buffer;
|
||||
randomUUID(): string;
|
||||
hashFile(filePath: string | Buffer): Promise<Buffer>;
|
||||
hashSha256(data: string): string;
|
||||
verifySha256(data: string, encrypted: string, publicKey: string): boolean;
|
||||
hashSha1(data: string | Buffer): Buffer;
|
||||
hashBcrypt(data: string | Buffer, saltOrRounds: string | number): Promise<string>;
|
||||
compareBcrypt(data: string | Buffer, encrypted: string): boolean;
|
||||
newPassword(bytes: number): string;
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
export enum DatabaseExtension {
|
||||
CUBE = 'cube',
|
||||
EARTH_DISTANCE = 'earthdistance',
|
||||
VECTOR = 'vector',
|
||||
VECTORS = 'vectors',
|
||||
}
|
||||
|
||||
export type VectorExtension = DatabaseExtension.VECTOR | DatabaseExtension.VECTORS;
|
||||
|
||||
export type DatabaseConnectionURL = {
|
||||
connectionType: 'url';
|
||||
url: string;
|
||||
};
|
||||
|
||||
export type DatabaseConnectionParts = {
|
||||
connectionType: 'parts';
|
||||
host: string;
|
||||
port: number;
|
||||
username: string;
|
||||
password: string;
|
||||
database: string;
|
||||
};
|
||||
|
||||
export type DatabaseConnectionParams = DatabaseConnectionURL | DatabaseConnectionParts;
|
||||
|
||||
export enum VectorIndex {
|
||||
CLIP = 'clip_index',
|
||||
FACE = 'face_index',
|
||||
}
|
||||
|
||||
export enum DatabaseLock {
|
||||
GeodataImport = 100,
|
||||
Migrations = 200,
|
||||
SystemFileMounts = 300,
|
||||
StorageTemplateMigration = 420,
|
||||
VersionHistory = 500,
|
||||
CLIPDimSize = 512,
|
||||
Library = 1337,
|
||||
GetSystemConfig = 69,
|
||||
BackupDatabase = 42,
|
||||
}
|
||||
|
||||
export const EXTENSION_NAMES: Record<DatabaseExtension, string> = {
|
||||
cube: 'cube',
|
||||
earthdistance: 'earthdistance',
|
||||
vector: 'pgvector',
|
||||
vectors: 'pgvecto.rs',
|
||||
} as const;
|
||||
|
||||
export interface ExtensionVersion {
|
||||
availableVersion: string | null;
|
||||
installedVersion: string | null;
|
||||
}
|
||||
|
||||
export interface VectorUpdateResult {
|
||||
restartRequired: boolean;
|
||||
}
|
||||
|
||||
export const IDatabaseRepository = 'IDatabaseRepository';
|
||||
|
||||
export interface IDatabaseRepository {
|
||||
init(): void;
|
||||
reconnect(): Promise<boolean>;
|
||||
shutdown(): Promise<void>;
|
||||
getExtensionVersion(extension: DatabaseExtension): Promise<ExtensionVersion>;
|
||||
getExtensionVersionRange(extension: VectorExtension): string;
|
||||
getPostgresVersion(): Promise<string>;
|
||||
getPostgresVersionRange(): string;
|
||||
createExtension(extension: DatabaseExtension): Promise<void>;
|
||||
updateVectorExtension(extension: VectorExtension, version?: string): Promise<VectorUpdateResult>;
|
||||
reindex(index: VectorIndex): Promise<void>;
|
||||
shouldReindex(name: VectorIndex): Promise<boolean>;
|
||||
runMigrations(options?: { transaction?: 'all' | 'none' | 'each' }): Promise<void>;
|
||||
withLock<R>(lock: DatabaseLock, callback: () => Promise<R>): Promise<R>;
|
||||
tryLock(lock: DatabaseLock): Promise<boolean>;
|
||||
isBusy(lock: DatabaseLock): boolean;
|
||||
wait(lock: DatabaseLock): Promise<void>;
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
import { ClassConstructor } from 'class-transformer';
|
||||
import { SystemConfig } from 'src/config';
|
||||
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
||||
import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server.dto';
|
||||
import { JobItem, QueueName } from 'src/interfaces/job.interface';
|
||||
|
||||
export const IEventRepository = 'IEventRepository';
|
||||
|
||||
type EventMap = {
|
||||
// app events
|
||||
'app.bootstrap': [];
|
||||
'app.shutdown': [];
|
||||
|
||||
'config.init': [{ newConfig: SystemConfig }];
|
||||
// config events
|
||||
'config.update': [
|
||||
{
|
||||
newConfig: SystemConfig;
|
||||
oldConfig: SystemConfig;
|
||||
},
|
||||
];
|
||||
'config.validate': [{ newConfig: SystemConfig; oldConfig: SystemConfig }];
|
||||
|
||||
// album events
|
||||
'album.update': [{ id: string; recipientIds: string[] }];
|
||||
'album.invite': [{ id: string; userId: string }];
|
||||
|
||||
// asset events
|
||||
'asset.tag': [{ assetId: string }];
|
||||
'asset.untag': [{ assetId: string }];
|
||||
'asset.hide': [{ assetId: string; userId: string }];
|
||||
'asset.show': [{ assetId: string; userId: string }];
|
||||
'asset.trash': [{ assetId: string; userId: string }];
|
||||
'asset.delete': [{ assetId: string; userId: string }];
|
||||
|
||||
// asset bulk events
|
||||
'assets.trash': [{ assetIds: string[]; userId: string }];
|
||||
'assets.delete': [{ assetIds: string[]; userId: string }];
|
||||
'assets.restore': [{ assetIds: string[]; userId: string }];
|
||||
|
||||
'job.start': [QueueName, JobItem];
|
||||
|
||||
// session events
|
||||
'session.delete': [{ sessionId: string }];
|
||||
|
||||
// stack events
|
||||
'stack.create': [{ stackId: string; userId: string }];
|
||||
'stack.update': [{ stackId: string; userId: string }];
|
||||
'stack.delete': [{ stackId: string; userId: string }];
|
||||
|
||||
// stack bulk events
|
||||
'stacks.delete': [{ stackIds: string[]; userId: string }];
|
||||
|
||||
// user events
|
||||
'user.signup': [{ notify: boolean; id: string; tempPassword?: string }];
|
||||
|
||||
// websocket events
|
||||
'websocket.connect': [{ userId: string }];
|
||||
};
|
||||
|
||||
export const serverEvents = ['config.update'] as const;
|
||||
export type ServerEvents = (typeof serverEvents)[number];
|
||||
|
||||
export type EmitEvent = keyof EventMap;
|
||||
export type EmitHandler<T extends EmitEvent> = (...args: ArgsOf<T>) => Promise<void> | void;
|
||||
export type ArgOf<T extends EmitEvent> = EventMap[T][0];
|
||||
export type ArgsOf<T extends EmitEvent> = EventMap[T];
|
||||
|
||||
export interface ClientEventMap {
|
||||
on_upload_success: [AssetResponseDto];
|
||||
on_user_delete: [string];
|
||||
on_asset_delete: [string];
|
||||
on_asset_trash: [string[]];
|
||||
on_asset_update: [AssetResponseDto];
|
||||
on_asset_hidden: [string];
|
||||
on_asset_restore: [string[]];
|
||||
on_asset_stack_update: string[];
|
||||
on_person_thumbnail: [string];
|
||||
on_server_version: [ServerVersionResponseDto];
|
||||
on_config_update: [];
|
||||
on_new_release: [ReleaseNotification];
|
||||
on_session_delete: [string];
|
||||
}
|
||||
|
||||
export type EventItem<T extends EmitEvent> = {
|
||||
event: T;
|
||||
handler: EmitHandler<T>;
|
||||
server: boolean;
|
||||
};
|
||||
|
||||
export enum BootstrapEventPriority {
|
||||
// Database service should be initialized before anything else, most other services need database access
|
||||
DatabaseService = -200,
|
||||
// Initialise config after other bootstrap services, stop other services from using config on bootstrap
|
||||
SystemConfig = 100,
|
||||
}
|
||||
|
||||
export interface IEventRepository {
|
||||
setup(options: { services: ClassConstructor<unknown>[] }): void;
|
||||
emit<T extends keyof EventMap>(event: T, ...args: ArgsOf<T>): Promise<void>;
|
||||
|
||||
/**
|
||||
* Send to connected clients for a specific user
|
||||
*/
|
||||
clientSend<E extends keyof ClientEventMap>(event: E, room: string, ...data: ClientEventMap[E]): void;
|
||||
/**
|
||||
* Send to all connected clients
|
||||
*/
|
||||
clientBroadcast<E extends keyof ClientEventMap>(event: E, ...data: ClientEventMap[E]): void;
|
||||
/**
|
||||
* Send to all connected servers
|
||||
*/
|
||||
serverSend<T extends ServerEvents>(event: T, ...args: ArgsOf<T>): void;
|
||||
}
|
||||
@@ -1,329 +0,0 @@
|
||||
import { ClassConstructor } from 'class-transformer';
|
||||
import { EmailImageAttachment } from 'src/repositories/notification.repository';
|
||||
|
||||
export enum QueueName {
|
||||
THUMBNAIL_GENERATION = 'thumbnailGeneration',
|
||||
METADATA_EXTRACTION = 'metadataExtraction',
|
||||
VIDEO_CONVERSION = 'videoConversion',
|
||||
FACE_DETECTION = 'faceDetection',
|
||||
FACIAL_RECOGNITION = 'facialRecognition',
|
||||
SMART_SEARCH = 'smartSearch',
|
||||
DUPLICATE_DETECTION = 'duplicateDetection',
|
||||
BACKGROUND_TASK = 'backgroundTask',
|
||||
STORAGE_TEMPLATE_MIGRATION = 'storageTemplateMigration',
|
||||
MIGRATION = 'migration',
|
||||
SEARCH = 'search',
|
||||
SIDECAR = 'sidecar',
|
||||
LIBRARY = 'library',
|
||||
NOTIFICATION = 'notifications',
|
||||
BACKUP_DATABASE = 'backupDatabase',
|
||||
}
|
||||
|
||||
export type ConcurrentQueueName = Exclude<
|
||||
QueueName,
|
||||
| QueueName.STORAGE_TEMPLATE_MIGRATION
|
||||
| QueueName.FACIAL_RECOGNITION
|
||||
| QueueName.DUPLICATE_DETECTION
|
||||
| QueueName.BACKUP_DATABASE
|
||||
>;
|
||||
|
||||
export enum JobCommand {
|
||||
START = 'start',
|
||||
PAUSE = 'pause',
|
||||
RESUME = 'resume',
|
||||
EMPTY = 'empty',
|
||||
CLEAR_FAILED = 'clear-failed',
|
||||
}
|
||||
|
||||
export enum JobName {
|
||||
//backups
|
||||
BACKUP_DATABASE = 'database-backup',
|
||||
|
||||
// conversion
|
||||
QUEUE_VIDEO_CONVERSION = 'queue-video-conversion',
|
||||
VIDEO_CONVERSION = 'video-conversion',
|
||||
|
||||
// thumbnails
|
||||
QUEUE_GENERATE_THUMBNAILS = 'queue-generate-thumbnails',
|
||||
GENERATE_THUMBNAILS = 'generate-thumbnails',
|
||||
GENERATE_PERSON_THUMBNAIL = 'generate-person-thumbnail',
|
||||
|
||||
// metadata
|
||||
QUEUE_METADATA_EXTRACTION = 'queue-metadata-extraction',
|
||||
METADATA_EXTRACTION = 'metadata-extraction',
|
||||
LINK_LIVE_PHOTOS = 'link-live-photos',
|
||||
|
||||
// user
|
||||
USER_DELETION = 'user-deletion',
|
||||
USER_DELETE_CHECK = 'user-delete-check',
|
||||
USER_SYNC_USAGE = 'user-sync-usage',
|
||||
|
||||
// asset
|
||||
ASSET_DELETION = 'asset-deletion',
|
||||
ASSET_DELETION_CHECK = 'asset-deletion-check',
|
||||
|
||||
// storage template
|
||||
STORAGE_TEMPLATE_MIGRATION = 'storage-template-migration',
|
||||
STORAGE_TEMPLATE_MIGRATION_SINGLE = 'storage-template-migration-single',
|
||||
|
||||
// tags
|
||||
TAG_CLEANUP = 'tag-cleanup',
|
||||
|
||||
// migration
|
||||
QUEUE_MIGRATION = 'queue-migration',
|
||||
MIGRATE_ASSET = 'migrate-asset',
|
||||
MIGRATE_PERSON = 'migrate-person',
|
||||
|
||||
// facial recognition
|
||||
PERSON_CLEANUP = 'person-cleanup',
|
||||
QUEUE_FACE_DETECTION = 'queue-face-detection',
|
||||
FACE_DETECTION = 'face-detection',
|
||||
QUEUE_FACIAL_RECOGNITION = 'queue-facial-recognition',
|
||||
FACIAL_RECOGNITION = 'facial-recognition',
|
||||
|
||||
// library management
|
||||
LIBRARY_QUEUE_SYNC_FILES = 'library-queue-sync-files',
|
||||
LIBRARY_QUEUE_SYNC_ASSETS = 'library-queue-sync-assets',
|
||||
LIBRARY_SYNC_FILE = 'library-sync-file',
|
||||
LIBRARY_SYNC_ASSET = 'library-sync-asset',
|
||||
LIBRARY_DELETE = 'library-delete',
|
||||
LIBRARY_QUEUE_SYNC_ALL = 'library-queue-sync-all',
|
||||
LIBRARY_QUEUE_CLEANUP = 'library-queue-cleanup',
|
||||
|
||||
// cleanup
|
||||
DELETE_FILES = 'delete-files',
|
||||
CLEAN_OLD_AUDIT_LOGS = 'clean-old-audit-logs',
|
||||
CLEAN_OLD_SESSION_TOKENS = 'clean-old-session-tokens',
|
||||
|
||||
// smart search
|
||||
QUEUE_SMART_SEARCH = 'queue-smart-search',
|
||||
SMART_SEARCH = 'smart-search',
|
||||
|
||||
QUEUE_TRASH_EMPTY = 'queue-trash-empty',
|
||||
|
||||
// duplicate detection
|
||||
QUEUE_DUPLICATE_DETECTION = 'queue-duplicate-detection',
|
||||
DUPLICATE_DETECTION = 'duplicate-detection',
|
||||
|
||||
// XMP sidecars
|
||||
QUEUE_SIDECAR = 'queue-sidecar',
|
||||
SIDECAR_DISCOVERY = 'sidecar-discovery',
|
||||
SIDECAR_SYNC = 'sidecar-sync',
|
||||
SIDECAR_WRITE = 'sidecar-write',
|
||||
|
||||
// Notification
|
||||
NOTIFY_SIGNUP = 'notify-signup',
|
||||
NOTIFY_ALBUM_INVITE = 'notify-album-invite',
|
||||
NOTIFY_ALBUM_UPDATE = 'notify-album-update',
|
||||
SEND_EMAIL = 'notification-send-email',
|
||||
|
||||
// Version check
|
||||
VERSION_CHECK = 'version-check',
|
||||
}
|
||||
|
||||
export const JOBS_ASSET_PAGINATION_SIZE = 1000;
|
||||
export const JOBS_LIBRARY_PAGINATION_SIZE = 10_000;
|
||||
|
||||
export interface IBaseJob {
|
||||
force?: boolean;
|
||||
}
|
||||
|
||||
export interface IDelayedJob extends IBaseJob {
|
||||
/** The minimum time to wait to execute this job, in milliseconds. */
|
||||
delay?: number;
|
||||
}
|
||||
|
||||
export interface IEntityJob extends IBaseJob {
|
||||
id: string;
|
||||
source?: 'upload' | 'sidecar-write' | 'copy';
|
||||
notify?: boolean;
|
||||
}
|
||||
|
||||
export interface IAssetDeleteJob extends IEntityJob {
|
||||
deleteOnDisk: boolean;
|
||||
}
|
||||
|
||||
export interface ILibraryFileJob extends IEntityJob {
|
||||
ownerId: string;
|
||||
assetPath: string;
|
||||
}
|
||||
|
||||
export interface ILibraryAssetJob extends IEntityJob {
|
||||
importPaths: string[];
|
||||
exclusionPatterns: string[];
|
||||
}
|
||||
|
||||
export interface IBulkEntityJob extends IBaseJob {
|
||||
ids: string[];
|
||||
}
|
||||
|
||||
export interface IDeleteFilesJob extends IBaseJob {
|
||||
files: Array<string | null | undefined>;
|
||||
}
|
||||
|
||||
export interface ISidecarWriteJob extends IEntityJob {
|
||||
description?: string;
|
||||
dateTimeOriginal?: string;
|
||||
latitude?: number;
|
||||
longitude?: number;
|
||||
rating?: number;
|
||||
tags?: true;
|
||||
}
|
||||
|
||||
export interface IDeferrableJob extends IEntityJob {
|
||||
deferred?: boolean;
|
||||
}
|
||||
|
||||
export interface INightlyJob extends IBaseJob {
|
||||
nightly?: boolean;
|
||||
}
|
||||
|
||||
export interface IEmailJob {
|
||||
to: string;
|
||||
subject: string;
|
||||
html: string;
|
||||
text: string;
|
||||
imageAttachments?: EmailImageAttachment[];
|
||||
}
|
||||
|
||||
export interface INotifySignupJob extends IEntityJob {
|
||||
tempPassword?: string;
|
||||
}
|
||||
|
||||
export interface INotifyAlbumInviteJob extends IEntityJob {
|
||||
recipientId: string;
|
||||
}
|
||||
|
||||
export interface INotifyAlbumUpdateJob extends IEntityJob, IDelayedJob {
|
||||
recipientIds: string[];
|
||||
}
|
||||
|
||||
export interface JobCounts {
|
||||
active: number;
|
||||
completed: number;
|
||||
failed: number;
|
||||
delayed: number;
|
||||
waiting: number;
|
||||
paused: number;
|
||||
}
|
||||
|
||||
export interface QueueStatus {
|
||||
isActive: boolean;
|
||||
isPaused: boolean;
|
||||
}
|
||||
|
||||
export enum QueueCleanType {
|
||||
FAILED = 'failed',
|
||||
}
|
||||
|
||||
export type JobItem =
|
||||
// Backups
|
||||
| { name: JobName.BACKUP_DATABASE; data?: IBaseJob }
|
||||
|
||||
// Transcoding
|
||||
| { name: JobName.QUEUE_VIDEO_CONVERSION; data: IBaseJob }
|
||||
| { name: JobName.VIDEO_CONVERSION; data: IEntityJob }
|
||||
|
||||
// Thumbnails
|
||||
| { name: JobName.QUEUE_GENERATE_THUMBNAILS; data: IBaseJob }
|
||||
| { name: JobName.GENERATE_THUMBNAILS; data: IEntityJob }
|
||||
|
||||
// User
|
||||
| { name: JobName.USER_DELETE_CHECK; data?: IBaseJob }
|
||||
| { name: JobName.USER_DELETION; data: IEntityJob }
|
||||
| { name: JobName.USER_SYNC_USAGE; data?: IBaseJob }
|
||||
|
||||
// Storage Template
|
||||
| { name: JobName.STORAGE_TEMPLATE_MIGRATION; data?: IBaseJob }
|
||||
| { name: JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE; data: IEntityJob }
|
||||
|
||||
// Migration
|
||||
| { name: JobName.QUEUE_MIGRATION; data?: IBaseJob }
|
||||
| { name: JobName.MIGRATE_ASSET; data: IEntityJob }
|
||||
| { name: JobName.MIGRATE_PERSON; data: IEntityJob }
|
||||
|
||||
// Metadata Extraction
|
||||
| { name: JobName.QUEUE_METADATA_EXTRACTION; data: IBaseJob }
|
||||
| { name: JobName.METADATA_EXTRACTION; data: IEntityJob }
|
||||
| { name: JobName.LINK_LIVE_PHOTOS; data: IEntityJob }
|
||||
// Sidecar Scanning
|
||||
| { name: JobName.QUEUE_SIDECAR; data: IBaseJob }
|
||||
| { name: JobName.SIDECAR_DISCOVERY; data: IEntityJob }
|
||||
| { name: JobName.SIDECAR_SYNC; data: IEntityJob }
|
||||
| { name: JobName.SIDECAR_WRITE; data: ISidecarWriteJob }
|
||||
|
||||
// Facial Recognition
|
||||
| { name: JobName.QUEUE_FACE_DETECTION; data: IBaseJob }
|
||||
| { name: JobName.FACE_DETECTION; data: IEntityJob }
|
||||
| { name: JobName.QUEUE_FACIAL_RECOGNITION; data: INightlyJob }
|
||||
| { name: JobName.FACIAL_RECOGNITION; data: IDeferrableJob }
|
||||
| { name: JobName.GENERATE_PERSON_THUMBNAIL; data: IEntityJob }
|
||||
|
||||
// Smart Search
|
||||
| { name: JobName.QUEUE_SMART_SEARCH; data: IBaseJob }
|
||||
| { name: JobName.SMART_SEARCH; data: IEntityJob }
|
||||
| { name: JobName.QUEUE_TRASH_EMPTY; data?: IBaseJob }
|
||||
|
||||
// Duplicate Detection
|
||||
| { name: JobName.QUEUE_DUPLICATE_DETECTION; data: IBaseJob }
|
||||
| { name: JobName.DUPLICATE_DETECTION; data: IEntityJob }
|
||||
|
||||
// Filesystem
|
||||
| { name: JobName.DELETE_FILES; data: IDeleteFilesJob }
|
||||
|
||||
// Cleanup
|
||||
| { name: JobName.CLEAN_OLD_AUDIT_LOGS; data?: IBaseJob }
|
||||
| { name: JobName.CLEAN_OLD_SESSION_TOKENS; data?: IBaseJob }
|
||||
|
||||
// Tags
|
||||
| { name: JobName.TAG_CLEANUP; data?: IBaseJob }
|
||||
|
||||
// Asset Deletion
|
||||
| { name: JobName.PERSON_CLEANUP; data?: IBaseJob }
|
||||
| { name: JobName.ASSET_DELETION; data: IAssetDeleteJob }
|
||||
| { name: JobName.ASSET_DELETION_CHECK; data?: IBaseJob }
|
||||
|
||||
// Library Management
|
||||
| { name: JobName.LIBRARY_SYNC_FILE; data: ILibraryFileJob }
|
||||
| { name: JobName.LIBRARY_QUEUE_SYNC_FILES; data: IEntityJob }
|
||||
| { name: JobName.LIBRARY_QUEUE_SYNC_ASSETS; data: IEntityJob }
|
||||
| { name: JobName.LIBRARY_SYNC_ASSET; data: ILibraryAssetJob }
|
||||
| { name: JobName.LIBRARY_DELETE; data: IEntityJob }
|
||||
| { name: JobName.LIBRARY_QUEUE_SYNC_ALL; data?: IBaseJob }
|
||||
| { name: JobName.LIBRARY_QUEUE_CLEANUP; data: IBaseJob }
|
||||
|
||||
// Notification
|
||||
| { name: JobName.SEND_EMAIL; data: IEmailJob }
|
||||
| { name: JobName.NOTIFY_ALBUM_INVITE; data: INotifyAlbumInviteJob }
|
||||
| { name: JobName.NOTIFY_ALBUM_UPDATE; data: INotifyAlbumUpdateJob }
|
||||
| { name: JobName.NOTIFY_SIGNUP; data: INotifySignupJob }
|
||||
|
||||
// Version check
|
||||
| { name: JobName.VERSION_CHECK; data: IBaseJob };
|
||||
|
||||
export enum JobStatus {
|
||||
SUCCESS = 'success',
|
||||
FAILED = 'failed',
|
||||
SKIPPED = 'skipped',
|
||||
}
|
||||
export type Jobs = { [K in JobItem['name']]: (JobItem & { name: K })['data'] };
|
||||
export type JobOf<T extends JobName> = Jobs[T];
|
||||
|
||||
export const IJobRepository = 'IJobRepository';
|
||||
|
||||
export interface IJobRepository {
|
||||
setup(options: { services: ClassConstructor<unknown>[] }): void;
|
||||
startWorkers(): void;
|
||||
run(job: JobItem): Promise<JobStatus>;
|
||||
setConcurrency(queueName: QueueName, concurrency: number): void;
|
||||
queue(item: JobItem): Promise<void>;
|
||||
queueAll(items: JobItem[]): Promise<void>;
|
||||
pause(name: QueueName): Promise<void>;
|
||||
resume(name: QueueName): Promise<void>;
|
||||
empty(name: QueueName): Promise<void>;
|
||||
clear(name: QueueName, type: QueueCleanType): Promise<string[]>;
|
||||
getQueueStatus(name: QueueName): Promise<QueueStatus>;
|
||||
getJobCounts(name: QueueName): Promise<JobCounts>;
|
||||
waitForQueueCompletion(...queues: QueueName[]): Promise<void>;
|
||||
removeJob(jobId: string, name: JobName): Promise<IEntityJob | undefined>;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { Insertable, Updateable } from 'kysely';
|
||||
import { Libraries } from 'src/db';
|
||||
import { LibraryStatsResponseDto } from 'src/dtos/library.dto';
|
||||
import { LibraryEntity } from 'src/entities/library.entity';
|
||||
|
||||
export const ILibraryRepository = 'ILibraryRepository';
|
||||
|
||||
export interface ILibraryRepository {
|
||||
getAll(withDeleted?: boolean): Promise<LibraryEntity[]>;
|
||||
getAllDeleted(): Promise<LibraryEntity[]>;
|
||||
get(id: string, withDeleted?: boolean): Promise<LibraryEntity | undefined>;
|
||||
create(library: Insertable<Libraries>): Promise<LibraryEntity>;
|
||||
delete(id: string): Promise<void>;
|
||||
softDelete(id: string): Promise<void>;
|
||||
update(id: string, library: Updateable<Libraries>): Promise<LibraryEntity>;
|
||||
getStatistics(id: string): Promise<LibraryStatsResponseDto | undefined>;
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
export const IMachineLearningRepository = 'IMachineLearningRepository';
|
||||
|
||||
export interface BoundingBox {
|
||||
x1: number;
|
||||
y1: number;
|
||||
x2: number;
|
||||
y2: number;
|
||||
}
|
||||
|
||||
export enum ModelTask {
|
||||
FACIAL_RECOGNITION = 'facial-recognition',
|
||||
SEARCH = 'clip',
|
||||
}
|
||||
|
||||
export enum ModelType {
|
||||
DETECTION = 'detection',
|
||||
PIPELINE = 'pipeline',
|
||||
RECOGNITION = 'recognition',
|
||||
TEXTUAL = 'textual',
|
||||
VISUAL = 'visual',
|
||||
}
|
||||
|
||||
export type ModelPayload = { imagePath: string } | { text: string };
|
||||
|
||||
type ModelOptions = { modelName: string };
|
||||
|
||||
export type FaceDetectionOptions = ModelOptions & { minScore: number };
|
||||
|
||||
type VisualResponse = { imageHeight: number; imageWidth: number };
|
||||
export type ClipVisualRequest = { [ModelTask.SEARCH]: { [ModelType.VISUAL]: ModelOptions } };
|
||||
export type ClipVisualResponse = { [ModelTask.SEARCH]: string } & VisualResponse;
|
||||
|
||||
export type ClipTextualRequest = { [ModelTask.SEARCH]: { [ModelType.TEXTUAL]: ModelOptions } };
|
||||
export type ClipTextualResponse = { [ModelTask.SEARCH]: string };
|
||||
|
||||
export type FacialRecognitionRequest = {
|
||||
[ModelTask.FACIAL_RECOGNITION]: {
|
||||
[ModelType.DETECTION]: ModelOptions & { options: { minScore: number } };
|
||||
[ModelType.RECOGNITION]: ModelOptions;
|
||||
};
|
||||
};
|
||||
|
||||
export interface Face {
|
||||
boundingBox: BoundingBox;
|
||||
embedding: string;
|
||||
score: number;
|
||||
}
|
||||
|
||||
export type FacialRecognitionResponse = { [ModelTask.FACIAL_RECOGNITION]: Face[] } & VisualResponse;
|
||||
export type DetectedFaces = { faces: Face[] } & VisualResponse;
|
||||
export type MachineLearningRequest = ClipVisualRequest | ClipTextualRequest | FacialRecognitionRequest;
|
||||
|
||||
export interface IMachineLearningRepository {
|
||||
encodeImage(urls: string[], imagePath: string, config: ModelOptions): Promise<string>;
|
||||
encodeText(urls: string[], text: string, config: ModelOptions): Promise<string>;
|
||||
detectFaces(urls: string[], imagePath: string, config: FaceDetectionOptions): Promise<DetectedFaces>;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { Insertable, Updateable } from 'kysely';
|
||||
import { MoveHistory } from 'src/db';
|
||||
import { MoveEntity } from 'src/entities/move.entity';
|
||||
import { PathType } from 'src/enum';
|
||||
|
||||
export const IMoveRepository = 'IMoveRepository';
|
||||
|
||||
export type MoveCreate = Pick<MoveEntity, 'oldPath' | 'newPath' | 'entityId' | 'pathType'> & Partial<MoveEntity>;
|
||||
|
||||
export interface IMoveRepository {
|
||||
create(entity: Insertable<MoveHistory>): Promise<MoveEntity>;
|
||||
getByEntity(entityId: string, pathType: PathType): Promise<MoveEntity | undefined>;
|
||||
update(id: string, entity: Updateable<MoveHistory>): Promise<MoveEntity>;
|
||||
delete(id: string): Promise<MoveEntity>;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import { Updateable } from 'kysely';
|
||||
import { Partners } from 'src/db';
|
||||
import { PartnerEntity } from 'src/entities/partner.entity';
|
||||
|
||||
export interface PartnerIds {
|
||||
sharedById: string;
|
||||
sharedWithId: string;
|
||||
}
|
||||
|
||||
export enum PartnerDirection {
|
||||
SharedBy = 'shared-by',
|
||||
SharedWith = 'shared-with',
|
||||
}
|
||||
|
||||
export const IPartnerRepository = 'IPartnerRepository';
|
||||
|
||||
export interface IPartnerRepository {
|
||||
getAll(userId: string): Promise<PartnerEntity[]>;
|
||||
get(partner: PartnerIds): Promise<PartnerEntity | undefined>;
|
||||
create(partner: PartnerIds): Promise<PartnerEntity>;
|
||||
remove(partner: PartnerIds): Promise<void>;
|
||||
update(partner: PartnerIds, entity: Updateable<Partners>): Promise<PartnerEntity>;
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
import { Insertable, Selectable, Updateable } from 'kysely';
|
||||
import { AssetFaces, FaceSearch, Person } from 'src/db';
|
||||
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
|
||||
import { PersonEntity } from 'src/entities/person.entity';
|
||||
import { SourceType } from 'src/enum';
|
||||
import { Paginated, PaginationOptions } from 'src/utils/pagination';
|
||||
import { FindOptionsRelations } from 'typeorm';
|
||||
|
||||
export const IPersonRepository = 'IPersonRepository';
|
||||
|
||||
export interface PersonSearchOptions {
|
||||
minimumFaceCount: number;
|
||||
withHidden: boolean;
|
||||
closestFaceAssetId?: string;
|
||||
}
|
||||
|
||||
export interface PersonNameSearchOptions {
|
||||
withHidden?: boolean;
|
||||
}
|
||||
|
||||
export interface PersonNameResponse {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface AssetFaceId {
|
||||
assetId: string;
|
||||
personId: string;
|
||||
}
|
||||
|
||||
export interface UpdateFacesData {
|
||||
oldPersonId?: string;
|
||||
faceIds?: string[];
|
||||
newPersonId: string;
|
||||
}
|
||||
|
||||
export interface PersonStatistics {
|
||||
assets: number;
|
||||
}
|
||||
|
||||
export interface PeopleStatistics {
|
||||
total: number;
|
||||
hidden: number;
|
||||
}
|
||||
|
||||
export interface DeleteFacesOptions {
|
||||
sourceType: SourceType;
|
||||
}
|
||||
|
||||
export type UnassignFacesOptions = DeleteFacesOptions;
|
||||
|
||||
export type SelectFaceOptions = (keyof Selectable<AssetFaces>)[];
|
||||
|
||||
export interface IPersonRepository {
|
||||
getAll(options?: Partial<PersonEntity>): AsyncIterableIterator<PersonEntity>;
|
||||
getAllForUser(pagination: PaginationOptions, userId: string, options: PersonSearchOptions): Paginated<PersonEntity>;
|
||||
getAllWithoutFaces(): Promise<PersonEntity[]>;
|
||||
getById(personId: string): Promise<PersonEntity | null>;
|
||||
getByName(userId: string, personName: string, options: PersonNameSearchOptions): Promise<PersonEntity[]>;
|
||||
getDistinctNames(userId: string, options: PersonNameSearchOptions): Promise<PersonNameResponse[]>;
|
||||
|
||||
create(person: Insertable<Person>): Promise<PersonEntity>;
|
||||
createAll(people: Insertable<Person>[]): Promise<string[]>;
|
||||
delete(entities: PersonEntity[]): Promise<void>;
|
||||
deleteFaces(options: DeleteFacesOptions): Promise<void>;
|
||||
refreshFaces(
|
||||
facesToAdd: Insertable<AssetFaces>[],
|
||||
faceIdsToRemove: string[],
|
||||
embeddingsToAdd?: Insertable<FaceSearch>[],
|
||||
): Promise<void>;
|
||||
getAllFaces(options?: Partial<AssetFaceEntity>): AsyncIterableIterator<AssetFaceEntity>;
|
||||
getFaceById(id: string): Promise<AssetFaceEntity>;
|
||||
getFaceByIdWithAssets(
|
||||
id: string,
|
||||
relations?: FindOptionsRelations<AssetFaceEntity>,
|
||||
select?: SelectFaceOptions,
|
||||
): Promise<AssetFaceEntity | undefined>;
|
||||
getFaces(assetId: string): Promise<AssetFaceEntity[]>;
|
||||
getFacesByIds(ids: AssetFaceId[]): Promise<AssetFaceEntity[]>;
|
||||
getRandomFace(personId: string): Promise<AssetFaceEntity | undefined>;
|
||||
getStatistics(personId: string): Promise<PersonStatistics>;
|
||||
reassignFace(assetFaceId: string, newPersonId: string): Promise<number>;
|
||||
getNumberOfPeople(userId: string): Promise<PeopleStatistics>;
|
||||
reassignFaces(data: UpdateFacesData): Promise<number>;
|
||||
unassignFaces(options: UnassignFacesOptions): Promise<void>;
|
||||
update(person: Updateable<Person> & { id: string }): Promise<PersonEntity>;
|
||||
updateAll(people: Insertable<Person>[]): Promise<void>;
|
||||
getLatestFaceDate(): Promise<string | undefined>;
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
import { AssetEntity } from 'src/entities/asset.entity';
|
||||
import { GeodataPlacesEntity } from 'src/entities/geodata-places.entity';
|
||||
import { AssetStatus, AssetType } from 'src/enum';
|
||||
import { Paginated } from 'src/utils/pagination';
|
||||
|
||||
export const ISearchRepository = 'ISearchRepository';
|
||||
|
||||
export interface SearchResult<T> {
|
||||
/** total matches */
|
||||
total: number;
|
||||
/** collection size */
|
||||
count: number;
|
||||
/** current page */
|
||||
page: number;
|
||||
/** items for page */
|
||||
items: T[];
|
||||
/** score */
|
||||
distances: number[];
|
||||
facets: SearchFacet[];
|
||||
}
|
||||
|
||||
export interface SearchFacet {
|
||||
fieldName: string;
|
||||
counts: Array<{
|
||||
count: number;
|
||||
value: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export type SearchExploreItemSet<T> = Array<{
|
||||
value: string;
|
||||
data: T;
|
||||
}>;
|
||||
|
||||
export interface SearchExploreItem<T> {
|
||||
fieldName: string;
|
||||
items: SearchExploreItemSet<T>;
|
||||
}
|
||||
|
||||
export interface SearchAssetIDOptions {
|
||||
checksum?: Buffer;
|
||||
deviceAssetId?: string;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export interface SearchUserIdOptions {
|
||||
deviceId?: string;
|
||||
libraryId?: string | null;
|
||||
userIds?: string[];
|
||||
}
|
||||
|
||||
export type SearchIdOptions = SearchAssetIDOptions & SearchUserIdOptions;
|
||||
|
||||
export interface SearchStatusOptions {
|
||||
isArchived?: boolean;
|
||||
isEncoded?: boolean;
|
||||
isFavorite?: boolean;
|
||||
isMotion?: boolean;
|
||||
isOffline?: boolean;
|
||||
isVisible?: boolean;
|
||||
isNotInAlbum?: boolean;
|
||||
type?: AssetType;
|
||||
status?: AssetStatus;
|
||||
withArchived?: boolean;
|
||||
withDeleted?: boolean;
|
||||
}
|
||||
|
||||
export interface SearchOneToOneRelationOptions {
|
||||
withExif?: boolean;
|
||||
withStacked?: boolean;
|
||||
}
|
||||
|
||||
export interface SearchRelationOptions extends SearchOneToOneRelationOptions {
|
||||
withFaces?: boolean;
|
||||
withPeople?: boolean;
|
||||
}
|
||||
|
||||
export interface SearchDateOptions {
|
||||
createdBefore?: Date;
|
||||
createdAfter?: Date;
|
||||
takenBefore?: Date;
|
||||
takenAfter?: Date;
|
||||
trashedBefore?: Date;
|
||||
trashedAfter?: Date;
|
||||
updatedBefore?: Date;
|
||||
updatedAfter?: Date;
|
||||
}
|
||||
|
||||
export interface SearchPathOptions {
|
||||
encodedVideoPath?: string;
|
||||
originalFileName?: string;
|
||||
originalPath?: string;
|
||||
previewPath?: string;
|
||||
thumbnailPath?: string;
|
||||
}
|
||||
|
||||
export interface SearchExifOptions {
|
||||
city?: string | null;
|
||||
country?: string | null;
|
||||
lensModel?: string | null;
|
||||
make?: string | null;
|
||||
model?: string | null;
|
||||
state?: string | null;
|
||||
description?: string | null;
|
||||
}
|
||||
|
||||
export interface SearchEmbeddingOptions {
|
||||
embedding: string;
|
||||
userIds: string[];
|
||||
}
|
||||
|
||||
export interface SearchPeopleOptions {
|
||||
personIds?: string[];
|
||||
}
|
||||
|
||||
export interface SearchTagOptions {
|
||||
tagIds?: string[];
|
||||
}
|
||||
|
||||
export interface SearchOrderOptions {
|
||||
orderDirection?: 'asc' | 'desc';
|
||||
}
|
||||
|
||||
export interface SearchPaginationOptions {
|
||||
page: number;
|
||||
size: number;
|
||||
}
|
||||
|
||||
type BaseAssetSearchOptions = SearchDateOptions &
|
||||
SearchIdOptions &
|
||||
SearchExifOptions &
|
||||
SearchOrderOptions &
|
||||
SearchPathOptions &
|
||||
SearchStatusOptions &
|
||||
SearchUserIdOptions &
|
||||
SearchPeopleOptions &
|
||||
SearchTagOptions;
|
||||
|
||||
export type AssetSearchOptions = BaseAssetSearchOptions & SearchRelationOptions;
|
||||
|
||||
export type AssetSearchOneToOneRelationOptions = BaseAssetSearchOptions & SearchOneToOneRelationOptions;
|
||||
|
||||
export type AssetSearchBuilderOptions = Omit<AssetSearchOptions, 'orderDirection'>;
|
||||
|
||||
export type SmartSearchOptions = SearchDateOptions &
|
||||
SearchEmbeddingOptions &
|
||||
SearchExifOptions &
|
||||
SearchOneToOneRelationOptions &
|
||||
SearchStatusOptions &
|
||||
SearchUserIdOptions &
|
||||
SearchPeopleOptions &
|
||||
SearchTagOptions;
|
||||
|
||||
export interface FaceEmbeddingSearch extends SearchEmbeddingOptions {
|
||||
hasPerson?: boolean;
|
||||
numResults: number;
|
||||
maxDistance: number;
|
||||
}
|
||||
|
||||
export interface AssetDuplicateSearch {
|
||||
assetId: string;
|
||||
embedding: string;
|
||||
maxDistance: number;
|
||||
type: AssetType;
|
||||
userIds: string[];
|
||||
}
|
||||
|
||||
export interface FaceSearchResult {
|
||||
distance: number;
|
||||
id: string;
|
||||
personId: string | null;
|
||||
}
|
||||
|
||||
export interface AssetDuplicateResult {
|
||||
assetId: string;
|
||||
duplicateId: string | null;
|
||||
distance: number;
|
||||
}
|
||||
|
||||
export interface GetStatesOptions {
|
||||
country?: string;
|
||||
}
|
||||
|
||||
export interface GetCitiesOptions extends GetStatesOptions {
|
||||
state?: string;
|
||||
}
|
||||
|
||||
export interface GetCameraModelsOptions {
|
||||
make?: string;
|
||||
}
|
||||
|
||||
export interface GetCameraMakesOptions {
|
||||
model?: string;
|
||||
}
|
||||
|
||||
export interface ISearchRepository {
|
||||
searchMetadata(pagination: SearchPaginationOptions, options: AssetSearchOptions): Paginated<AssetEntity>;
|
||||
searchSmart(pagination: SearchPaginationOptions, options: SmartSearchOptions): Paginated<AssetEntity>;
|
||||
searchDuplicates(options: AssetDuplicateSearch): Promise<AssetDuplicateResult[]>;
|
||||
searchFaces(search: FaceEmbeddingSearch): Promise<FaceSearchResult[]>;
|
||||
searchRandom(size: number, options: AssetSearchOptions): Promise<AssetEntity[]>;
|
||||
upsert(assetId: string, embedding: string): Promise<void>;
|
||||
searchPlaces(placeName: string): Promise<GeodataPlacesEntity[]>;
|
||||
getAssetsByCity(userIds: string[]): Promise<AssetEntity[]>;
|
||||
deleteAllSearchEmbeddings(): Promise<void>;
|
||||
getDimensionSize(): Promise<number>;
|
||||
setDimensionSize(dimSize: number): Promise<void>;
|
||||
getCountries(userIds: string[]): Promise<Array<string | null>>;
|
||||
getStates(userIds: string[], options: GetStatesOptions): Promise<Array<string | null>>;
|
||||
getCities(userIds: string[], options: GetCitiesOptions): Promise<Array<string | null>>;
|
||||
getCameraMakes(userIds: string[], options: GetCameraMakesOptions): Promise<Array<string | null>>;
|
||||
getCameraModels(userIds: string[], options: GetCameraModelsOptions): Promise<Array<string | null>>;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import { Insertable, Updateable } from 'kysely';
|
||||
import { SharedLinks } from 'src/db';
|
||||
import { SharedLinkEntity } from 'src/entities/shared-link.entity';
|
||||
|
||||
export const ISharedLinkRepository = 'ISharedLinkRepository';
|
||||
|
||||
export type SharedLinkSearchOptions = {
|
||||
userId: string;
|
||||
albumId?: string;
|
||||
};
|
||||
|
||||
export interface ISharedLinkRepository {
|
||||
getAll(options: SharedLinkSearchOptions): Promise<SharedLinkEntity[]>;
|
||||
get(userId: string, id: string): Promise<SharedLinkEntity | undefined>;
|
||||
getByKey(key: Buffer): Promise<SharedLinkEntity | undefined>;
|
||||
create(entity: Insertable<SharedLinks> & { assetIds?: string[] }): Promise<SharedLinkEntity>;
|
||||
update(entity: Updateable<SharedLinks> & { id: string; assetIds?: string[] }): Promise<SharedLinkEntity>;
|
||||
remove(entity: SharedLinkEntity): Promise<void>;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import { Updateable } from 'kysely';
|
||||
import { StackEntity } from 'src/entities/stack.entity';
|
||||
|
||||
export const IStackRepository = 'IStackRepository';
|
||||
|
||||
export interface StackSearch {
|
||||
ownerId: string;
|
||||
primaryAssetId?: string;
|
||||
}
|
||||
|
||||
export interface IStackRepository {
|
||||
search(query: StackSearch): Promise<StackEntity[]>;
|
||||
create(stack: { ownerId: string; assetIds: string[] }): Promise<StackEntity>;
|
||||
update(id: string, entity: Updateable<StackEntity>): Promise<StackEntity>;
|
||||
delete(id: string): Promise<void>;
|
||||
deleteAll(ids: string[]): Promise<void>;
|
||||
getById(id: string): Promise<StackEntity | undefined>;
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
import { WatchOptions } from 'chokidar';
|
||||
import { Stats } from 'node:fs';
|
||||
import { FileReadOptions } from 'node:fs/promises';
|
||||
import { Readable, Writable } from 'node:stream';
|
||||
import { CrawlOptionsDto, WalkOptionsDto } from 'src/dtos/library.dto';
|
||||
|
||||
export interface ImmichReadStream {
|
||||
stream: Readable;
|
||||
type?: string;
|
||||
length?: number;
|
||||
}
|
||||
|
||||
export interface ImmichZipStream extends ImmichReadStream {
|
||||
addFile: (inputPath: string, filename: string) => void;
|
||||
finalize: () => Promise<void>;
|
||||
}
|
||||
|
||||
export interface DiskUsage {
|
||||
available: number;
|
||||
free: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
export const IStorageRepository = 'IStorageRepository';
|
||||
|
||||
export interface WatchEvents {
|
||||
onReady(): void;
|
||||
onAdd(path: string): void;
|
||||
onChange(path: string): void;
|
||||
onUnlink(path: string): void;
|
||||
onError(error: Error): void;
|
||||
}
|
||||
|
||||
export interface IStorageRepository {
|
||||
createZipStream(): ImmichZipStream;
|
||||
createReadStream(filepath: string, mimeType?: string | null): Promise<ImmichReadStream>;
|
||||
readFile(filepath: string, options?: FileReadOptions<Buffer>): Promise<Buffer>;
|
||||
createFile(filepath: string, buffer: Buffer): Promise<void>;
|
||||
createWriteStream(filepath: string): Writable;
|
||||
createOrOverwriteFile(filepath: string, buffer: Buffer): Promise<void>;
|
||||
overwriteFile(filepath: string, buffer: Buffer): Promise<void>;
|
||||
realpath(filepath: string): Promise<string>;
|
||||
unlink(filepath: string): Promise<void>;
|
||||
unlinkDir(folder: string, options?: { recursive?: boolean; force?: boolean }): Promise<void>;
|
||||
removeEmptyDirs(folder: string, self?: boolean): Promise<void>;
|
||||
checkFileExists(filepath: string, mode?: number): Promise<boolean>;
|
||||
mkdirSync(filepath: string): void;
|
||||
checkDiskUsage(folder: string): Promise<DiskUsage>;
|
||||
readdir(folder: string): Promise<string[]>;
|
||||
stat(filepath: string): Promise<Stats>;
|
||||
crawl(options: CrawlOptionsDto): Promise<string[]>;
|
||||
walk(options: WalkOptionsDto): AsyncGenerator<string[]>;
|
||||
copyFile(source: string, target: string): Promise<void>;
|
||||
rename(source: string, target: string): Promise<void>;
|
||||
watch(paths: string[], options: WatchOptions, events: Partial<WatchEvents>): () => Promise<void>;
|
||||
utimes(filepath: string, atime: Date, mtime: Date): Promise<void>;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { TagEntity } from 'src/entities/tag.entity';
|
||||
import { IBulkAsset } from 'src/utils/asset.util';
|
||||
|
||||
export const ITagRepository = 'ITagRepository';
|
||||
|
||||
export type AssetTagItem = { assetId: string; tagId: string };
|
||||
|
||||
export interface ITagRepository extends IBulkAsset {
|
||||
getAll(userId: string): Promise<TagEntity[]>;
|
||||
getByValue(userId: string, value: string): Promise<TagEntity | null>;
|
||||
upsertValue(request: { userId: string; value: string; parent?: TagEntity }): Promise<TagEntity>;
|
||||
|
||||
create(tag: Partial<TagEntity>): Promise<TagEntity>;
|
||||
get(id: string): Promise<TagEntity | null>;
|
||||
update(tag: { id: string } & Partial<TagEntity>): Promise<TagEntity>;
|
||||
delete(id: string): Promise<void>;
|
||||
|
||||
upsertAssetTags({ assetId, tagIds }: { assetId: string; tagIds: string[] }): Promise<void>;
|
||||
upsertAssetIds(items: AssetTagItem[]): Promise<AssetTagItem[]>;
|
||||
deleteEmptyTags(): Promise<void>;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import { Insertable, Updateable } from 'kysely';
|
||||
import { Users } from 'src/db';
|
||||
import { UserMetadata } from 'src/entities/user-metadata.entity';
|
||||
import { UserEntity } from 'src/entities/user.entity';
|
||||
|
||||
export interface UserListFilter {
|
||||
withDeleted?: boolean;
|
||||
}
|
||||
|
||||
export interface UserStatsQueryResponse {
|
||||
userId: string;
|
||||
userName: string;
|
||||
photos: number;
|
||||
videos: number;
|
||||
usage: number;
|
||||
usagePhotos: number;
|
||||
usageVideos: number;
|
||||
quotaSizeInBytes: number | null;
|
||||
}
|
||||
|
||||
export interface UserFindOptions {
|
||||
withDeleted?: boolean;
|
||||
}
|
||||
|
||||
export const IUserRepository = 'IUserRepository';
|
||||
|
||||
export interface IUserRepository {
|
||||
get(id: string, options: UserFindOptions): Promise<UserEntity | undefined>;
|
||||
getAdmin(): Promise<UserEntity | undefined>;
|
||||
hasAdmin(): Promise<boolean>;
|
||||
getByEmail(email: string, withPassword?: boolean): Promise<UserEntity | undefined>;
|
||||
getByStorageLabel(storageLabel: string): Promise<UserEntity | undefined>;
|
||||
getByOAuthId(oauthId: string): Promise<UserEntity | undefined>;
|
||||
getDeletedUsers(): Promise<UserEntity[]>;
|
||||
getList(filter?: UserListFilter): Promise<UserEntity[]>;
|
||||
getUserStats(): Promise<UserStatsQueryResponse[]>;
|
||||
create(user: Insertable<Users>): Promise<UserEntity>;
|
||||
update(id: string, user: Updateable<Users>): Promise<UserEntity>;
|
||||
restore(id: string): Promise<UserEntity>;
|
||||
upsertMetadata<T extends keyof UserMetadata>(id: string, item: { key: T; value: UserMetadata[T] }): Promise<void>;
|
||||
deleteMetadata<T extends keyof UserMetadata>(id: string, key: T): Promise<void>;
|
||||
delete(user: UserEntity, hard?: boolean): Promise<UserEntity>;
|
||||
updateUsage(id: string, delta: number): Promise<void>;
|
||||
syncUsage(id?: string): Promise<void>;
|
||||
}
|
||||
@@ -10,14 +10,10 @@ import { UploadFieldName } from 'src/dtos/asset-media.dto';
|
||||
import { RouteKey } from 'src/enum';
|
||||
import { AuthRequest } from 'src/middleware/auth.guard';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import { AssetMediaService, UploadFile } from 'src/services/asset-media.service';
|
||||
import { AssetMediaService } from 'src/services/asset-media.service';
|
||||
import { ImmichFile, UploadFile, UploadFiles } from 'src/types';
|
||||
import { asRequest, mapToUploadFile } from 'src/utils/asset.util';
|
||||
|
||||
export interface UploadFiles {
|
||||
assetData: ImmichFile[];
|
||||
sidecarData: ImmichFile[];
|
||||
}
|
||||
|
||||
export function getFile(files: UploadFiles, property: 'assetData' | 'sidecarData') {
|
||||
const file = files[property]?.[0];
|
||||
return file ? mapToUploadFile(file) : file;
|
||||
@@ -30,12 +26,6 @@ export function getFiles(files: UploadFiles) {
|
||||
};
|
||||
}
|
||||
|
||||
export interface ImmichFile extends Express.Multer.File {
|
||||
/** sha1 hash of file */
|
||||
uuid: string;
|
||||
checksum: Buffer;
|
||||
}
|
||||
|
||||
type DiskStorageCallback = (error: Error | null, result: string) => void;
|
||||
|
||||
type ImmichMulterFile = Express.Multer.File & { uuid: string };
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DatabaseExtension } from 'src/interfaces/database.interface';
|
||||
import { DatabaseExtension } from 'src/enum';
|
||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DatabaseExtension } from 'src/interfaces/database.interface';
|
||||
import { DatabaseExtension } from 'src/enum';
|
||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DatabaseExtension } from 'src/interfaces/database.interface';
|
||||
import { DatabaseExtension } from 'src/enum';
|
||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
|
||||
18
server/src/migrations/1737845696644-NullableDates.ts
Normal file
18
server/src/migrations/1737845696644-NullableDates.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class NullableDates1737845696644 implements MigrationInterface {
|
||||
name = 'NullableDates1737845696644'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "fileCreatedAt" DROP NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "localDateTime" DROP NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "fileModifiedAt" DROP NOT NULL`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "fileModifiedAt" SET NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "localDateTime" SET NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "fileCreatedAt" SET NOT NULL`);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -43,3 +43,6 @@ where
|
||||
and "activity"."albumId" = $2
|
||||
and "activity"."isLiked" = $3
|
||||
and "assets"."deletedAt" is null
|
||||
and "assets"."fileCreatedAt" is not null
|
||||
and "assets"."fileModifiedAt" is not null
|
||||
and "assets"."localDateTime" is not null
|
||||
|
||||
@@ -3,29 +3,28 @@
|
||||
-- ApiKeyRepository.getKey
|
||||
select
|
||||
"api_keys"."id",
|
||||
"api_keys"."key",
|
||||
"api_keys"."userId",
|
||||
"api_keys"."permissions",
|
||||
to_json("user") as "user"
|
||||
from
|
||||
"api_keys"
|
||||
inner join lateral (
|
||||
(
|
||||
select
|
||||
"users".*,
|
||||
to_json(obj)
|
||||
from
|
||||
(
|
||||
select
|
||||
array_agg("user_metadata") as "metadata"
|
||||
"users"."id",
|
||||
"users"."name",
|
||||
"users"."email",
|
||||
"users"."isAdmin",
|
||||
"users"."quotaUsageInBytes",
|
||||
"users"."quotaSizeInBytes"
|
||||
from
|
||||
"user_metadata"
|
||||
"users"
|
||||
where
|
||||
"users"."id" = "user_metadata"."userId"
|
||||
) as "metadata"
|
||||
from
|
||||
"users"
|
||||
where
|
||||
"users"."id" = "api_keys"."userId"
|
||||
and "users"."deletedAt" is null
|
||||
) as "user" on true
|
||||
"users"."id" = "api_keys"."userId"
|
||||
and "users"."deletedAt" is null
|
||||
) as obj
|
||||
) as "user"
|
||||
from
|
||||
"api_keys"
|
||||
where
|
||||
"api_keys"."key" = $1
|
||||
|
||||
|
||||
@@ -159,6 +159,9 @@ where
|
||||
"ownerId" = $1::uuid
|
||||
and "deviceId" = $2
|
||||
and "isVisible" = $3
|
||||
and "assets"."fileCreatedAt" is not null
|
||||
and "assets"."fileModifiedAt" is not null
|
||||
and "assets"."localDateTime" is not null
|
||||
and "deletedAt" is null
|
||||
|
||||
-- AssetRepository.getLivePhotoCount
|
||||
@@ -260,6 +263,9 @@ with
|
||||
where
|
||||
"assets"."deletedAt" is null
|
||||
and "assets"."isVisible" = $2
|
||||
and "assets"."fileCreatedAt" is not null
|
||||
and "assets"."fileModifiedAt" is not null
|
||||
and "assets"."localDateTime" is not null
|
||||
)
|
||||
select
|
||||
"timeBucket",
|
||||
|
||||
@@ -13,6 +13,9 @@ where
|
||||
and "assets"."isFavorite" = $4
|
||||
and "assets"."isArchived" = $5
|
||||
and "assets"."deletedAt" is null
|
||||
and "assets"."fileCreatedAt" is not null
|
||||
and "assets"."fileModifiedAt" is not null
|
||||
and "assets"."localDateTime" is not null
|
||||
order by
|
||||
"assets"."fileCreatedAt" desc
|
||||
limit
|
||||
@@ -34,6 +37,9 @@ offset
|
||||
and "assets"."isFavorite" = $4
|
||||
and "assets"."isArchived" = $5
|
||||
and "assets"."deletedAt" is null
|
||||
and "assets"."fileCreatedAt" is not null
|
||||
and "assets"."fileModifiedAt" is not null
|
||||
and "assets"."localDateTime" is not null
|
||||
and "assets"."id" < $6
|
||||
order by
|
||||
random()
|
||||
@@ -54,6 +60,9 @@ union all
|
||||
and "assets"."isFavorite" = $11
|
||||
and "assets"."isArchived" = $12
|
||||
and "assets"."deletedAt" is null
|
||||
and "assets"."fileCreatedAt" is not null
|
||||
and "assets"."fileModifiedAt" is not null
|
||||
and "assets"."localDateTime" is not null
|
||||
and "assets"."id" > $13
|
||||
order by
|
||||
random()
|
||||
@@ -77,6 +86,9 @@ where
|
||||
and "assets"."isFavorite" = $4
|
||||
and "assets"."isArchived" = $5
|
||||
and "assets"."deletedAt" is null
|
||||
and "assets"."fileCreatedAt" is not null
|
||||
and "assets"."fileModifiedAt" is not null
|
||||
and "assets"."localDateTime" is not null
|
||||
order by
|
||||
smart_search.embedding <=> $6
|
||||
limit
|
||||
|
||||
@@ -10,41 +10,29 @@ where
|
||||
|
||||
-- SessionRepository.getByToken
|
||||
select
|
||||
"sessions".*,
|
||||
to_json("user") as "user"
|
||||
from
|
||||
"sessions"
|
||||
inner join lateral (
|
||||
"sessions"."id",
|
||||
"sessions"."updatedAt",
|
||||
(
|
||||
select
|
||||
"id",
|
||||
"email",
|
||||
"createdAt",
|
||||
"profileImagePath",
|
||||
"isAdmin",
|
||||
"shouldChangePassword",
|
||||
"deletedAt",
|
||||
"oauthId",
|
||||
"updatedAt",
|
||||
"storageLabel",
|
||||
"name",
|
||||
"quotaSizeInBytes",
|
||||
"quotaUsageInBytes",
|
||||
"status",
|
||||
"profileChangedAt",
|
||||
to_json(obj)
|
||||
from
|
||||
(
|
||||
select
|
||||
array_agg("user_metadata") as "metadata"
|
||||
"users"."id",
|
||||
"users"."name",
|
||||
"users"."email",
|
||||
"users"."isAdmin",
|
||||
"users"."quotaUsageInBytes",
|
||||
"users"."quotaSizeInBytes"
|
||||
from
|
||||
"user_metadata"
|
||||
"users"
|
||||
where
|
||||
"users"."id" = "user_metadata"."userId"
|
||||
) as "metadata"
|
||||
from
|
||||
"users"
|
||||
where
|
||||
"users"."id" = "sessions"."userId"
|
||||
and "users"."deletedAt" is null
|
||||
) as "user" on true
|
||||
"users"."id" = "sessions"."userId"
|
||||
and "users"."deletedAt" is null
|
||||
) as obj
|
||||
) as "user"
|
||||
from
|
||||
"sessions"
|
||||
where
|
||||
"sessions"."token" = $1
|
||||
|
||||
|
||||
@@ -153,12 +153,19 @@ where
|
||||
"shared_links"."type" = $2
|
||||
or "album"."id" is not null
|
||||
)
|
||||
and "shared_links"."albumId" = $3
|
||||
order by
|
||||
"shared_links"."createdAt" desc
|
||||
|
||||
-- SharedLinkRepository.getByKey
|
||||
select
|
||||
"shared_links".*,
|
||||
"shared_links"."id",
|
||||
"shared_links"."userId",
|
||||
"shared_links"."expiresAt",
|
||||
"shared_links"."showExif",
|
||||
"shared_links"."allowUpload",
|
||||
"shared_links"."allowDownload",
|
||||
"shared_links"."password",
|
||||
(
|
||||
select
|
||||
to_json(obj)
|
||||
@@ -166,20 +173,11 @@ select
|
||||
(
|
||||
select
|
||||
"users"."id",
|
||||
"users"."email",
|
||||
"users"."createdAt",
|
||||
"users"."profileImagePath",
|
||||
"users"."isAdmin",
|
||||
"users"."shouldChangePassword",
|
||||
"users"."deletedAt",
|
||||
"users"."oauthId",
|
||||
"users"."updatedAt",
|
||||
"users"."storageLabel",
|
||||
"users"."name",
|
||||
"users"."quotaSizeInBytes",
|
||||
"users"."email",
|
||||
"users"."isAdmin",
|
||||
"users"."quotaUsageInBytes",
|
||||
"users"."status",
|
||||
"users"."profileChangedAt"
|
||||
"users"."quotaSizeInBytes"
|
||||
from
|
||||
"users"
|
||||
where
|
||||
|
||||
@@ -10,6 +10,9 @@ where
|
||||
and "isVisible" = $3
|
||||
and "isArchived" = $4
|
||||
and "deletedAt" is null
|
||||
and "fileModifiedAt" is not null
|
||||
and "fileModifiedAt" is not null
|
||||
and "localDateTime" is not null
|
||||
|
||||
-- ViewRepository.getAssetsByOriginalPath
|
||||
select
|
||||
@@ -23,6 +26,9 @@ where
|
||||
and "isVisible" = $2
|
||||
and "isArchived" = $3
|
||||
and "deletedAt" is null
|
||||
and "fileModifiedAt" is not null
|
||||
and "fileModifiedAt" is not null
|
||||
and "localDateTime" is not null
|
||||
and "originalPath" like $4
|
||||
and "originalPath" not like $5
|
||||
order by
|
||||
|
||||
@@ -65,6 +65,9 @@ export class ActivityRepository {
|
||||
.where('activity.albumId', '=', albumId)
|
||||
.where('activity.isLiked', '=', false)
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.where('assets.fileCreatedAt', 'is not', null)
|
||||
.where('assets.fileModifiedAt', 'is not', null)
|
||||
.where('assets.localDateTime', 'is not', null)
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
return count as number;
|
||||
|
||||
@@ -6,7 +6,17 @@ import { Albums, DB } from 'src/db';
|
||||
import { Chunked, ChunkedArray, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { AlbumUserCreateDto } from 'src/dtos/album.dto';
|
||||
import { AlbumEntity } from 'src/entities/album.entity';
|
||||
import { AlbumAssetCount, AlbumInfoOptions, IAlbumRepository } from 'src/interfaces/album.interface';
|
||||
|
||||
export interface AlbumAssetCount {
|
||||
albumId: string;
|
||||
assetCount: number;
|
||||
startDate: Date | null;
|
||||
endDate: Date | null;
|
||||
}
|
||||
|
||||
export interface AlbumInfoOptions {
|
||||
withAssets: boolean;
|
||||
}
|
||||
|
||||
const userColumns = [
|
||||
'id',
|
||||
@@ -71,7 +81,7 @@ const withAssets = (eb: ExpressionBuilder<DB, 'albums'>) => {
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class AlbumRepository implements IAlbumRepository {
|
||||
export class AlbumRepository {
|
||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, { withAssets: true }] })
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Insertable, Kysely, Updateable } from 'kysely';
|
||||
import { jsonObjectFrom } from 'kysely/helpers/postgres';
|
||||
import { InjectKysely } from 'nestjs-kysely';
|
||||
import { columns } from 'src/database';
|
||||
import { ApiKeys, DB } from 'src/db';
|
||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { asUuid } from 'src/utils/database';
|
||||
|
||||
const columns = ['id', 'name', 'userId', 'createdAt', 'updatedAt', 'permissions'] as const;
|
||||
|
||||
@Injectable()
|
||||
export class ApiKeyRepository {
|
||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||
@@ -33,29 +33,15 @@ export class ApiKeyRepository {
|
||||
getKey(hashedToken: string) {
|
||||
return this.db
|
||||
.selectFrom('api_keys')
|
||||
.innerJoinLateral(
|
||||
(eb) =>
|
||||
.select((eb) => [
|
||||
...columns.authApiKey,
|
||||
jsonObjectFrom(
|
||||
eb
|
||||
.selectFrom('users')
|
||||
.selectAll('users')
|
||||
.select((eb) =>
|
||||
eb
|
||||
.selectFrom('user_metadata')
|
||||
.whereRef('users.id', '=', 'user_metadata.userId')
|
||||
.select((eb) => eb.fn('array_agg', [eb.table('user_metadata')]).as('metadata'))
|
||||
.as('metadata'),
|
||||
)
|
||||
.select(columns.authUser)
|
||||
.whereRef('users.id', '=', 'api_keys.userId')
|
||||
.where('users.deletedAt', 'is', null)
|
||||
.as('user'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.select((eb) => [
|
||||
'api_keys.id',
|
||||
'api_keys.key',
|
||||
'api_keys.userId',
|
||||
'api_keys.permissions',
|
||||
eb.fn.toJson('user').as('user'),
|
||||
.where('users.deletedAt', 'is', null),
|
||||
).as('user'),
|
||||
])
|
||||
.where('api_keys.key', '=', hashedToken)
|
||||
.executeTakeFirst();
|
||||
@@ -65,7 +51,7 @@ export class ApiKeyRepository {
|
||||
getById(userId: string, id: string) {
|
||||
return this.db
|
||||
.selectFrom('api_keys')
|
||||
.select(columns)
|
||||
.select(columns.apiKey)
|
||||
.where('id', '=', asUuid(id))
|
||||
.where('userId', '=', userId)
|
||||
.executeTakeFirst();
|
||||
@@ -75,7 +61,7 @@ export class ApiKeyRepository {
|
||||
getByUserId(userId: string) {
|
||||
return this.db
|
||||
.selectFrom('api_keys')
|
||||
.select(columns)
|
||||
.select(columns.apiKey)
|
||||
.where('userId', '=', userId)
|
||||
.orderBy('createdAt', 'desc')
|
||||
.execute();
|
||||
|
||||
@@ -7,6 +7,7 @@ import { AssetFiles, AssetJobStatus, Assets, DB, Exif } from 'src/db';
|
||||
import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators';
|
||||
import {
|
||||
AssetEntity,
|
||||
AssetEntityPlaceholder,
|
||||
hasPeople,
|
||||
searchAssetBuilder,
|
||||
truncatedDate,
|
||||
@@ -21,34 +22,138 @@ import {
|
||||
withTagId,
|
||||
withTags,
|
||||
} from 'src/entities/asset.entity';
|
||||
import { AssetFileType, AssetStatus, AssetType } from 'src/enum';
|
||||
import {
|
||||
AssetDeltaSyncOptions,
|
||||
AssetExploreFieldOptions,
|
||||
AssetFullSyncOptions,
|
||||
AssetGetByChecksumOptions,
|
||||
AssetStats,
|
||||
AssetStatsOptions,
|
||||
AssetUpdateDuplicateOptions,
|
||||
DayOfYearAssets,
|
||||
DuplicateGroup,
|
||||
GetByIdsRelations,
|
||||
IAssetRepository,
|
||||
LivePhotoSearchOptions,
|
||||
MonthDay,
|
||||
TimeBucketItem,
|
||||
TimeBucketOptions,
|
||||
TimeBucketSize,
|
||||
WithProperty,
|
||||
WithoutProperty,
|
||||
} from 'src/interfaces/asset.interface';
|
||||
import { AssetSearchOptions, SearchExploreItem, SearchExploreItemSet } from 'src/interfaces/search.interface';
|
||||
import { AssetFileType, AssetOrder, AssetStatus, AssetType } from 'src/enum';
|
||||
import { MapMarker, MapMarkerSearchOptions } from 'src/repositories/map.repository';
|
||||
import { AssetSearchOptions, SearchExploreItem, SearchExploreItemSet } from 'src/repositories/search.repository';
|
||||
import { anyUuid, asUuid, mapUpsertColumns } from 'src/utils/database';
|
||||
import { Paginated, PaginationOptions, paginationHelper } from 'src/utils/pagination';
|
||||
|
||||
export type AssetStats = Record<AssetType, number>;
|
||||
|
||||
export interface AssetStatsOptions {
|
||||
isFavorite?: boolean;
|
||||
isArchived?: boolean;
|
||||
isTrashed?: boolean;
|
||||
}
|
||||
|
||||
export interface LivePhotoSearchOptions {
|
||||
ownerId: string;
|
||||
libraryId?: string | null;
|
||||
livePhotoCID: string;
|
||||
otherAssetId: string;
|
||||
type: AssetType;
|
||||
}
|
||||
|
||||
export enum WithoutProperty {
|
||||
THUMBNAIL = 'thumbnail',
|
||||
ENCODED_VIDEO = 'encoded-video',
|
||||
EXIF = 'exif',
|
||||
SMART_SEARCH = 'smart-search',
|
||||
DUPLICATE = 'duplicate',
|
||||
FACES = 'faces',
|
||||
SIDECAR = 'sidecar',
|
||||
}
|
||||
|
||||
export enum WithProperty {
|
||||
SIDECAR = 'sidecar',
|
||||
}
|
||||
|
||||
export enum TimeBucketSize {
|
||||
DAY = 'DAY',
|
||||
MONTH = 'MONTH',
|
||||
}
|
||||
|
||||
export interface AssetBuilderOptions {
|
||||
isArchived?: boolean;
|
||||
isFavorite?: boolean;
|
||||
isTrashed?: boolean;
|
||||
isDuplicate?: boolean;
|
||||
albumId?: string;
|
||||
tagId?: string;
|
||||
personId?: string;
|
||||
userIds?: string[];
|
||||
withStacked?: boolean;
|
||||
exifInfo?: boolean;
|
||||
status?: AssetStatus;
|
||||
assetType?: AssetType;
|
||||
}
|
||||
|
||||
export interface TimeBucketOptions extends AssetBuilderOptions {
|
||||
size: TimeBucketSize;
|
||||
order?: AssetOrder;
|
||||
}
|
||||
|
||||
export interface TimeBucketItem {
|
||||
timeBucket: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface MonthDay {
|
||||
day: number;
|
||||
month: number;
|
||||
}
|
||||
|
||||
export interface AssetExploreFieldOptions {
|
||||
maxFields: number;
|
||||
minAssetsPerField: number;
|
||||
}
|
||||
|
||||
export interface AssetFullSyncOptions {
|
||||
ownerId: string;
|
||||
lastId?: string;
|
||||
updatedUntil: Date;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
export interface AssetDeltaSyncOptions {
|
||||
userIds: string[];
|
||||
updatedAfter: Date;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
export interface AssetUpdateDuplicateOptions {
|
||||
targetDuplicateId: string | null;
|
||||
assetIds: string[];
|
||||
duplicateIds: string[];
|
||||
}
|
||||
|
||||
export interface UpsertFileOptions {
|
||||
assetId: string;
|
||||
type: AssetFileType;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface AssetGetByChecksumOptions {
|
||||
ownerId: string;
|
||||
checksum: Buffer;
|
||||
libraryId?: string;
|
||||
}
|
||||
|
||||
export type AssetPathEntity = Pick<AssetEntity, 'id' | 'originalPath' | 'isOffline'>;
|
||||
|
||||
export interface GetByIdsRelations {
|
||||
exifInfo?: boolean;
|
||||
faces?: { person?: boolean };
|
||||
files?: boolean;
|
||||
library?: boolean;
|
||||
owner?: boolean;
|
||||
smartSearch?: boolean;
|
||||
stack?: { assets?: boolean };
|
||||
tags?: boolean;
|
||||
}
|
||||
|
||||
export interface DuplicateGroup {
|
||||
duplicateId: string;
|
||||
assets: AssetEntity[];
|
||||
}
|
||||
|
||||
export interface DayOfYearAssets {
|
||||
yearsAgo: number;
|
||||
assets: AssetEntity[];
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class AssetRepository implements IAssetRepository {
|
||||
export class AssetRepository {
|
||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||
|
||||
async upsertExif(exif: Insertable<Exif>): Promise<void> {
|
||||
@@ -79,8 +184,12 @@ export class AssetRepository implements IAssetRepository {
|
||||
.execute();
|
||||
}
|
||||
|
||||
create(asset: Insertable<Assets>): Promise<AssetEntity> {
|
||||
return this.db.insertInto('assets').values(asset).returningAll().executeTakeFirst() as any as Promise<AssetEntity>;
|
||||
create(asset: Insertable<Assets>): Promise<AssetEntityPlaceholder> {
|
||||
return this.db
|
||||
.insertInto('assets')
|
||||
.values(asset)
|
||||
.returningAll()
|
||||
.executeTakeFirst() as any as Promise<AssetEntityPlaceholder>;
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, { day: 1, month: 1 }] })
|
||||
@@ -291,6 +400,9 @@ export class AssetRepository implements IAssetRepository {
|
||||
.where('ownerId', '=', asUuid(ownerId))
|
||||
.where('deviceId', '=', deviceId)
|
||||
.where('isVisible', '=', true)
|
||||
.where('assets.fileCreatedAt', 'is not', null)
|
||||
.where('assets.fileModifiedAt', 'is not', null)
|
||||
.where('assets.localDateTime', 'is not', null)
|
||||
.where('deletedAt', 'is', null)
|
||||
.execute();
|
||||
|
||||
@@ -458,7 +570,10 @@ export class AssetRepository implements IAssetRepository {
|
||||
.where('job_status.duplicatesDetectedAt', 'is', null)
|
||||
.where('job_status.previewAt', 'is not', null)
|
||||
.where((eb) => eb.exists(eb.selectFrom('smart_search').where('assetId', '=', eb.ref('assets.id'))))
|
||||
.where('assets.isVisible', '=', true),
|
||||
.where('assets.isVisible', '=', true)
|
||||
.where('assets.fileCreatedAt', 'is not', null)
|
||||
.where('assets.fileModifiedAt', 'is not', null)
|
||||
.where('assets.localDateTime', 'is not', null),
|
||||
)
|
||||
.$if(property === WithoutProperty.ENCODED_VIDEO, (qb) =>
|
||||
qb
|
||||
@@ -552,6 +667,9 @@ export class AssetRepository implements IAssetRepository {
|
||||
.select((eb) => eb.fn.countAll().filterWhere('type', '=', AssetType.VIDEO).as(AssetType.VIDEO))
|
||||
.select((eb) => eb.fn.countAll().filterWhere('type', '=', AssetType.OTHER).as(AssetType.OTHER))
|
||||
.where('ownerId', '=', asUuid(ownerId))
|
||||
.where('assets.fileCreatedAt', 'is not', null)
|
||||
.where('assets.fileModifiedAt', 'is not', null)
|
||||
.where('assets.localDateTime', 'is not', null)
|
||||
.where('isVisible', '=', true)
|
||||
.$if(isArchived !== undefined, (qb) => qb.where('isArchived', '=', isArchived!))
|
||||
.$if(isFavorite !== undefined, (qb) => qb.where('isFavorite', '=', isFavorite!))
|
||||
@@ -584,6 +702,9 @@ export class AssetRepository implements IAssetRepository {
|
||||
.$if(!!options.isTrashed, (qb) => qb.where('assets.status', '!=', AssetStatus.DELETED))
|
||||
.where('assets.deletedAt', options.isTrashed ? 'is not' : 'is', null)
|
||||
.where('assets.isVisible', '=', true)
|
||||
.where('assets.fileCreatedAt', 'is not', null)
|
||||
.where('assets.fileModifiedAt', 'is not', null)
|
||||
.where('assets.localDateTime', 'is not', null)
|
||||
.$if(!!options.albumId, (qb) =>
|
||||
qb
|
||||
.innerJoin('albums_assets_assets', 'assets.id', 'albums_assets_assets.assetsId')
|
||||
|
||||
@@ -13,9 +13,16 @@ import { Notice } from 'postgres';
|
||||
import { citiesFile, excludePaths, IWorker } from 'src/constants';
|
||||
import { Telemetry } from 'src/decorators';
|
||||
import { EnvDto } from 'src/dtos/env.dto';
|
||||
import { ImmichEnvironment, ImmichHeader, ImmichTelemetry, ImmichWorker, LogLevel } from 'src/enum';
|
||||
import { DatabaseConnectionParams, DatabaseExtension, VectorExtension } from 'src/interfaces/database.interface';
|
||||
import { QueueName } from 'src/interfaces/job.interface';
|
||||
import {
|
||||
DatabaseExtension,
|
||||
ImmichEnvironment,
|
||||
ImmichHeader,
|
||||
ImmichTelemetry,
|
||||
ImmichWorker,
|
||||
LogLevel,
|
||||
QueueName,
|
||||
} from 'src/enum';
|
||||
import { DatabaseConnectionParams, VectorExtension } from 'src/types';
|
||||
import { setDifference } from 'src/utils/set';
|
||||
import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions.js';
|
||||
|
||||
|
||||
@@ -2,11 +2,10 @@ import { Injectable } from '@nestjs/common';
|
||||
import { compareSync, hash } from 'bcrypt';
|
||||
import { createHash, createPublicKey, createVerify, randomBytes, randomUUID } from 'node:crypto';
|
||||
import { createReadStream } from 'node:fs';
|
||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
||||
|
||||
@Injectable()
|
||||
export class CryptoRepository implements ICryptoRepository {
|
||||
randomUUID() {
|
||||
export class CryptoRepository {
|
||||
randomUUID(): string {
|
||||
return randomUUID();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,26 +4,18 @@ import AsyncLock from 'async-lock';
|
||||
import { Kysely, sql } from 'kysely';
|
||||
import { InjectKysely } from 'nestjs-kysely';
|
||||
import semver from 'semver';
|
||||
import { POSTGRES_VERSION_RANGE, VECTOR_VERSION_RANGE, VECTORS_VERSION_RANGE } from 'src/constants';
|
||||
import { EXTENSION_NAMES, POSTGRES_VERSION_RANGE, VECTOR_VERSION_RANGE, VECTORS_VERSION_RANGE } from 'src/constants';
|
||||
import { DB } from 'src/db';
|
||||
import {
|
||||
DatabaseExtension,
|
||||
DatabaseLock,
|
||||
EXTENSION_NAMES,
|
||||
ExtensionVersion,
|
||||
IDatabaseRepository,
|
||||
VectorExtension,
|
||||
VectorIndex,
|
||||
VectorUpdateResult,
|
||||
} from 'src/interfaces/database.interface';
|
||||
import { DatabaseExtension, DatabaseLock, VectorIndex } from 'src/enum';
|
||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import { ExtensionVersion, VectorExtension, VectorUpdateResult } from 'src/types';
|
||||
import { UPSERT_COLUMNS } from 'src/utils/database';
|
||||
import { isValidInteger } from 'src/validation';
|
||||
import { DataSource, EntityManager, EntityMetadata, QueryRunner } from 'typeorm';
|
||||
|
||||
@Injectable()
|
||||
export class DatabaseRepository implements IDatabaseRepository {
|
||||
export class DatabaseRepository {
|
||||
private vectorExtension: VectorExtension;
|
||||
private readonly asyncLock = new AsyncLock();
|
||||
|
||||
|
||||
@@ -10,21 +10,15 @@ import {
|
||||
import { ClassConstructor } from 'class-transformer';
|
||||
import _ from 'lodash';
|
||||
import { Server, Socket } from 'socket.io';
|
||||
import { SystemConfig } from 'src/config';
|
||||
import { EventConfig } from 'src/decorators';
|
||||
import { ImmichWorker, MetadataKey } from 'src/enum';
|
||||
import {
|
||||
ArgsOf,
|
||||
ClientEventMap,
|
||||
EmitEvent,
|
||||
EmitHandler,
|
||||
EventItem,
|
||||
IEventRepository,
|
||||
serverEvents,
|
||||
ServerEvents,
|
||||
} from 'src/interfaces/event.interface';
|
||||
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server.dto';
|
||||
import { ImmichWorker, MetadataKey, QueueName } from 'src/enum';
|
||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import { AuthService } from 'src/services/auth.service';
|
||||
import { JobItem } from 'src/types';
|
||||
import { handlePromiseError } from 'src/utils/misc';
|
||||
|
||||
type EmitHandlers = Partial<{ [T in EmitEvent]: Array<EventItem<T>> }>;
|
||||
@@ -37,14 +31,99 @@ type Item<T extends EmitEvent> = {
|
||||
label: string;
|
||||
};
|
||||
|
||||
type EventMap = {
|
||||
// app events
|
||||
'app.bootstrap': [];
|
||||
'app.shutdown': [];
|
||||
|
||||
'config.init': [{ newConfig: SystemConfig }];
|
||||
// config events
|
||||
'config.update': [
|
||||
{
|
||||
newConfig: SystemConfig;
|
||||
oldConfig: SystemConfig;
|
||||
},
|
||||
];
|
||||
'config.validate': [{ newConfig: SystemConfig; oldConfig: SystemConfig }];
|
||||
|
||||
// album events
|
||||
'album.update': [{ id: string; recipientIds: string[] }];
|
||||
'album.invite': [{ id: string; userId: string }];
|
||||
|
||||
// asset events
|
||||
'asset.tag': [{ assetId: string }];
|
||||
'asset.untag': [{ assetId: string }];
|
||||
'asset.hide': [{ assetId: string; userId: string }];
|
||||
'asset.show': [{ assetId: string; userId: string }];
|
||||
'asset.trash': [{ assetId: string; userId: string }];
|
||||
'asset.delete': [{ assetId: string; userId: string }];
|
||||
|
||||
// asset bulk events
|
||||
'assets.trash': [{ assetIds: string[]; userId: string }];
|
||||
'assets.delete': [{ assetIds: string[]; userId: string }];
|
||||
'assets.restore': [{ assetIds: string[]; userId: string }];
|
||||
|
||||
'job.start': [QueueName, JobItem];
|
||||
|
||||
// session events
|
||||
'session.delete': [{ sessionId: string }];
|
||||
|
||||
// stack events
|
||||
'stack.create': [{ stackId: string; userId: string }];
|
||||
'stack.update': [{ stackId: string; userId: string }];
|
||||
'stack.delete': [{ stackId: string; userId: string }];
|
||||
|
||||
// stack bulk events
|
||||
'stacks.delete': [{ stackIds: string[]; userId: string }];
|
||||
|
||||
// user events
|
||||
'user.signup': [{ notify: boolean; id: string; tempPassword?: string }];
|
||||
|
||||
// websocket events
|
||||
'websocket.connect': [{ userId: string }];
|
||||
};
|
||||
|
||||
export const serverEvents = ['config.update'] as const;
|
||||
export type ServerEvents = (typeof serverEvents)[number];
|
||||
|
||||
export type EmitEvent = keyof EventMap;
|
||||
export type EmitHandler<T extends EmitEvent> = (...args: ArgsOf<T>) => Promise<void> | void;
|
||||
export type ArgOf<T extends EmitEvent> = EventMap[T][0];
|
||||
export type ArgsOf<T extends EmitEvent> = EventMap[T];
|
||||
|
||||
export interface ClientEventMap {
|
||||
on_upload_success: [AssetResponseDto];
|
||||
on_user_delete: [string];
|
||||
on_asset_delete: [string];
|
||||
on_asset_trash: [string[]];
|
||||
on_asset_update: [AssetResponseDto];
|
||||
on_asset_hidden: [string];
|
||||
on_asset_restore: [string[]];
|
||||
on_asset_stack_update: string[];
|
||||
on_person_thumbnail: [string];
|
||||
on_server_version: [ServerVersionResponseDto];
|
||||
on_config_update: [];
|
||||
on_new_release: [ReleaseNotification];
|
||||
on_session_delete: [string];
|
||||
}
|
||||
|
||||
export type EventItem<T extends EmitEvent> = {
|
||||
event: T;
|
||||
handler: EmitHandler<T>;
|
||||
server: boolean;
|
||||
};
|
||||
|
||||
export type AuthFn = (client: Socket) => Promise<AuthDto>;
|
||||
|
||||
@WebSocketGateway({
|
||||
cors: true,
|
||||
path: '/api/socket.io',
|
||||
transports: ['websocket'],
|
||||
})
|
||||
@Injectable()
|
||||
export class EventRepository implements OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit, IEventRepository {
|
||||
export class EventRepository implements OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit {
|
||||
private emitHandlers: EmitHandlers = {};
|
||||
private authFn?: AuthFn;
|
||||
|
||||
@WebSocketServer()
|
||||
private server?: Server;
|
||||
@@ -122,11 +201,7 @@ export class EventRepository implements OnGatewayConnection, OnGatewayDisconnect
|
||||
async handleConnection(client: Socket) {
|
||||
try {
|
||||
this.logger.log(`Websocket Connect: ${client.id}`);
|
||||
const auth = await this.moduleRef.get(AuthService).authenticate({
|
||||
headers: client.request.headers,
|
||||
queryParams: {},
|
||||
metadata: { adminRoute: false, sharedLinkRoute: false, uri: '/api/socket.io' },
|
||||
});
|
||||
const auth = await this.authenticate(client);
|
||||
await client.join(auth.user.id);
|
||||
if (auth.session) {
|
||||
await client.join(auth.session.id);
|
||||
@@ -182,4 +257,16 @@ export class EventRepository implements OnGatewayConnection, OnGatewayDisconnect
|
||||
this.logger.debug(`Server event: ${event} (send)`);
|
||||
this.server?.serverSideEmit(event, ...args);
|
||||
}
|
||||
|
||||
setAuthFn(fn: (client: Socket) => Promise<AuthDto>) {
|
||||
this.authFn = fn;
|
||||
}
|
||||
|
||||
private async authenticate(client: Socket) {
|
||||
if (!this.authFn) {
|
||||
throw new Error('Auth function not set');
|
||||
}
|
||||
|
||||
return this.authFn(client);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,3 @@
|
||||
import { IAlbumRepository } from 'src/interfaces/album.interface';
|
||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
||||
import { IDatabaseRepository } from 'src/interfaces/database.interface';
|
||||
import { IEventRepository } from 'src/interfaces/event.interface';
|
||||
import { IJobRepository } from 'src/interfaces/job.interface';
|
||||
import { ILibraryRepository } from 'src/interfaces/library.interface';
|
||||
import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
|
||||
import { IMoveRepository } from 'src/interfaces/move.interface';
|
||||
import { IPartnerRepository } from 'src/interfaces/partner.interface';
|
||||
import { IPersonRepository } from 'src/interfaces/person.interface';
|
||||
import { ISearchRepository } from 'src/interfaces/search.interface';
|
||||
import { ISharedLinkRepository } from 'src/interfaces/shared-link.interface';
|
||||
import { IStackRepository } from 'src/interfaces/stack.interface';
|
||||
import { IStorageRepository } from 'src/interfaces/storage.interface';
|
||||
import { ITagRepository } from 'src/interfaces/tag.interface';
|
||||
import { IUserRepository } from 'src/interfaces/user.interface';
|
||||
import { AccessRepository } from 'src/repositories/access.repository';
|
||||
import { ActivityRepository } from 'src/repositories/activity.repository';
|
||||
import { AlbumUserRepository } from 'src/repositories/album-user.repository';
|
||||
@@ -58,44 +41,41 @@ import { ViewRepository } from 'src/repositories/view-repository';
|
||||
export const repositories = [
|
||||
AccessRepository,
|
||||
ActivityRepository,
|
||||
AlbumRepository,
|
||||
AlbumUserRepository,
|
||||
AuditRepository,
|
||||
ApiKeyRepository,
|
||||
AssetRepository,
|
||||
ConfigRepository,
|
||||
CronRepository,
|
||||
CryptoRepository,
|
||||
DatabaseRepository,
|
||||
EventRepository,
|
||||
JobRepository,
|
||||
LibraryRepository,
|
||||
LoggingRepository,
|
||||
MachineLearningRepository,
|
||||
MapRepository,
|
||||
MediaRepository,
|
||||
MemoryRepository,
|
||||
MetadataRepository,
|
||||
MoveRepository,
|
||||
NotificationRepository,
|
||||
OAuthRepository,
|
||||
PartnerRepository,
|
||||
PersonRepository,
|
||||
ProcessRepository,
|
||||
SearchRepository,
|
||||
SessionRepository,
|
||||
ServerInfoRepository,
|
||||
SharedLinkRepository,
|
||||
StackRepository,
|
||||
StorageRepository,
|
||||
SystemMetadataRepository,
|
||||
TagRepository,
|
||||
TelemetryRepository,
|
||||
TrashRepository,
|
||||
UserRepository,
|
||||
ViewRepository,
|
||||
VersionHistoryRepository,
|
||||
];
|
||||
|
||||
export const providers = [
|
||||
{ provide: IAlbumRepository, useClass: AlbumRepository },
|
||||
{ provide: IAssetRepository, useClass: AssetRepository },
|
||||
{ provide: ICryptoRepository, useClass: CryptoRepository },
|
||||
{ provide: IDatabaseRepository, useClass: DatabaseRepository },
|
||||
{ provide: IEventRepository, useClass: EventRepository },
|
||||
{ provide: IJobRepository, useClass: JobRepository },
|
||||
{ provide: ILibraryRepository, useClass: LibraryRepository },
|
||||
{ provide: IMachineLearningRepository, useClass: MachineLearningRepository },
|
||||
{ provide: IMoveRepository, useClass: MoveRepository },
|
||||
{ provide: IPartnerRepository, useClass: PartnerRepository },
|
||||
{ provide: IPersonRepository, useClass: PersonRepository },
|
||||
{ provide: ISearchRepository, useClass: SearchRepository },
|
||||
{ provide: ISharedLinkRepository, useClass: SharedLinkRepository },
|
||||
{ provide: IStackRepository, useClass: StackRepository },
|
||||
{ provide: IStorageRepository, useClass: StorageRepository },
|
||||
{ provide: ITagRepository, useClass: TagRepository },
|
||||
{ provide: IUserRepository, useClass: UserRepository },
|
||||
];
|
||||
|
||||
@@ -1,26 +1,15 @@
|
||||
import { getQueueToken } from '@nestjs/bullmq';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ModuleRef, Reflector } from '@nestjs/core';
|
||||
import { JobsOptions, Queue, Worker } from 'bullmq';
|
||||
import { ClassConstructor } from 'class-transformer';
|
||||
import { setTimeout } from 'node:timers/promises';
|
||||
import { JobConfig } from 'src/decorators';
|
||||
import { MetadataKey } from 'src/enum';
|
||||
import { IEventRepository } from 'src/interfaces/event.interface';
|
||||
import {
|
||||
IEntityJob,
|
||||
IJobRepository,
|
||||
JobCounts,
|
||||
JobItem,
|
||||
JobName,
|
||||
JobOf,
|
||||
JobStatus,
|
||||
QueueCleanType,
|
||||
QueueName,
|
||||
QueueStatus,
|
||||
} from 'src/interfaces/job.interface';
|
||||
import { JobName, JobStatus, MetadataKey, QueueCleanType, QueueName } from 'src/enum';
|
||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||
import { EventRepository } from 'src/repositories/event.repository';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import { IEntityJob, JobCounts, JobItem, JobOf, QueueStatus } from 'src/types';
|
||||
import { getKeyByValue, getMethodNames, ImmichStartupError } from 'src/utils/misc';
|
||||
|
||||
type JobMapItem = {
|
||||
@@ -31,14 +20,14 @@ type JobMapItem = {
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class JobRepository implements IJobRepository {
|
||||
export class JobRepository {
|
||||
private workers: Partial<Record<QueueName, Worker>> = {};
|
||||
private handlers: Partial<Record<JobName, JobMapItem>> = {};
|
||||
|
||||
constructor(
|
||||
private moduleRef: ModuleRef,
|
||||
private configRepository: ConfigRepository,
|
||||
@Inject(IEventRepository) private eventRepository: IEventRepository,
|
||||
private eventRepository: EventRepository,
|
||||
private logger: LoggingRepository,
|
||||
) {
|
||||
this.logger.setContext(JobRepository.name);
|
||||
|
||||
@@ -7,7 +7,6 @@ import { DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { LibraryStatsResponseDto } from 'src/dtos/library.dto';
|
||||
import { LibraryEntity } from 'src/entities/library.entity';
|
||||
import { AssetType } from 'src/enum';
|
||||
import { ILibraryRepository } from 'src/interfaces/library.interface';
|
||||
|
||||
const userColumns = [
|
||||
'users.id',
|
||||
@@ -34,7 +33,7 @@ const withOwner = (eb: ExpressionBuilder<DB, 'libraries'>) => {
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class LibraryRepository implements ILibraryRepository {
|
||||
export class LibraryRepository {
|
||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
|
||||
@@ -1,21 +1,60 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { CLIPConfig } from 'src/dtos/model-config.dto';
|
||||
import {
|
||||
ClipTextualResponse,
|
||||
ClipVisualResponse,
|
||||
FaceDetectionOptions,
|
||||
FacialRecognitionResponse,
|
||||
IMachineLearningRepository,
|
||||
MachineLearningRequest,
|
||||
ModelPayload,
|
||||
ModelTask,
|
||||
ModelType,
|
||||
} from 'src/interfaces/machine-learning.interface';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
|
||||
export interface BoundingBox {
|
||||
x1: number;
|
||||
y1: number;
|
||||
x2: number;
|
||||
y2: number;
|
||||
}
|
||||
|
||||
export enum ModelTask {
|
||||
FACIAL_RECOGNITION = 'facial-recognition',
|
||||
SEARCH = 'clip',
|
||||
}
|
||||
|
||||
export enum ModelType {
|
||||
DETECTION = 'detection',
|
||||
PIPELINE = 'pipeline',
|
||||
RECOGNITION = 'recognition',
|
||||
TEXTUAL = 'textual',
|
||||
VISUAL = 'visual',
|
||||
}
|
||||
|
||||
export type ModelPayload = { imagePath: string } | { text: string };
|
||||
|
||||
type ModelOptions = { modelName: string };
|
||||
|
||||
export type FaceDetectionOptions = ModelOptions & { minScore: number };
|
||||
|
||||
type VisualResponse = { imageHeight: number; imageWidth: number };
|
||||
export type ClipVisualRequest = { [ModelTask.SEARCH]: { [ModelType.VISUAL]: ModelOptions } };
|
||||
export type ClipVisualResponse = { [ModelTask.SEARCH]: string } & VisualResponse;
|
||||
|
||||
export type ClipTextualRequest = { [ModelTask.SEARCH]: { [ModelType.TEXTUAL]: ModelOptions } };
|
||||
export type ClipTextualResponse = { [ModelTask.SEARCH]: string };
|
||||
|
||||
export type FacialRecognitionRequest = {
|
||||
[ModelTask.FACIAL_RECOGNITION]: {
|
||||
[ModelType.DETECTION]: ModelOptions & { options: { minScore: number } };
|
||||
[ModelType.RECOGNITION]: ModelOptions;
|
||||
};
|
||||
};
|
||||
|
||||
export interface Face {
|
||||
boundingBox: BoundingBox;
|
||||
embedding: string;
|
||||
score: number;
|
||||
}
|
||||
|
||||
export type FacialRecognitionResponse = { [ModelTask.FACIAL_RECOGNITION]: Face[] } & VisualResponse;
|
||||
export type DetectedFaces = { faces: Face[] } & VisualResponse;
|
||||
export type MachineLearningRequest = ClipVisualRequest | ClipTextualRequest | FacialRecognitionRequest;
|
||||
|
||||
@Injectable()
|
||||
export class MachineLearningRepository implements IMachineLearningRepository {
|
||||
export class MachineLearningRepository {
|
||||
constructor(private logger: LoggingRepository) {
|
||||
this.logger.setContext(MachineLearningRepository.name);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { jsonArrayFrom } from 'kysely/helpers/postgres';
|
||||
import { InjectKysely } from 'nestjs-kysely';
|
||||
import { DB, Memories } from 'src/db';
|
||||
import { Chunked, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { IBulkAsset } from 'src/utils/asset.util';
|
||||
import { IBulkAsset } from 'src/types';
|
||||
|
||||
@Injectable()
|
||||
export class MemoryRepository implements IBulkAsset {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { BinaryField, DefaultReadTaskOptions, ExifTool, Tags } from 'exiftool-vendored';
|
||||
import geotz from 'geo-tz';
|
||||
import { LogLevel } from 'src/enum';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
|
||||
interface ExifDuration {
|
||||
@@ -101,6 +102,9 @@ export class MetadataRepository {
|
||||
}
|
||||
|
||||
async writeTags(path: string, tags: Partial<Tags>): Promise<void> {
|
||||
if (this.logger.isLevelEnabled(LogLevel.VERBOSE)) {
|
||||
this.logger.verbose(`Writing tags ${JSON.stringify(tags)} to ${path}`);
|
||||
}
|
||||
try {
|
||||
await this.exiftool.write(path, tags);
|
||||
} catch (error) {
|
||||
|
||||
@@ -5,10 +5,11 @@ import { DB, MoveHistory } from 'src/db';
|
||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { MoveEntity } from 'src/entities/move.entity';
|
||||
import { PathType } from 'src/enum';
|
||||
import { IMoveRepository } from 'src/interfaces/move.interface';
|
||||
|
||||
export type MoveCreate = Pick<MoveEntity, 'oldPath' | 'newPath' | 'entityId' | 'pathType'> & Partial<MoveEntity>;
|
||||
|
||||
@Injectable()
|
||||
export class MoveRepository implements IMoveRepository {
|
||||
export class MoveRepository {
|
||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||
|
||||
create(entity: Insertable<MoveHistory>): Promise<MoveEntity> {
|
||||
|
||||
@@ -7,12 +7,7 @@ import { AlbumUpdateEmail } from 'src/emails/album-update.email';
|
||||
import { TestEmail } from 'src/emails/test.email';
|
||||
import { WelcomeEmail } from 'src/emails/welcome.email';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
|
||||
export type EmailImageAttachment = {
|
||||
filename: string;
|
||||
path: string;
|
||||
cid: string;
|
||||
};
|
||||
import { EmailImageAttachment } from 'src/types';
|
||||
|
||||
export type SendEmailOptions = {
|
||||
from: string;
|
||||
|
||||
@@ -5,7 +5,16 @@ import { InjectKysely } from 'nestjs-kysely';
|
||||
import { DB, Partners, Users } from 'src/db';
|
||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { PartnerEntity } from 'src/entities/partner.entity';
|
||||
import { IPartnerRepository, PartnerIds } from 'src/interfaces/partner.interface';
|
||||
|
||||
export interface PartnerIds {
|
||||
sharedById: string;
|
||||
sharedWithId: string;
|
||||
}
|
||||
|
||||
export enum PartnerDirection {
|
||||
SharedBy = 'shared-by',
|
||||
SharedWith = 'shared-with',
|
||||
}
|
||||
|
||||
const columns = ['id', 'name', 'email', 'profileImagePath', 'profileChangedAt'] as const;
|
||||
|
||||
@@ -28,7 +37,7 @@ const withSharedWith = (eb: ExpressionBuilder<DB, 'partners'>) => {
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class PartnerRepository implements IPartnerRepository {
|
||||
export class PartnerRepository {
|
||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ExpressionBuilder, Insertable, Kysely, sql } from 'kysely';
|
||||
import { ExpressionBuilder, Insertable, Kysely, Selectable, sql } from 'kysely';
|
||||
import { jsonObjectFrom } from 'kysely/helpers/postgres';
|
||||
import { InjectKysely } from 'nestjs-kysely';
|
||||
import { AssetFaces, DB, FaceSearch, Person } from 'src/db';
|
||||
@@ -7,23 +7,53 @@ import { ChunkedArray, DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
|
||||
import { PersonEntity } from 'src/entities/person.entity';
|
||||
import { SourceType } from 'src/enum';
|
||||
import {
|
||||
AssetFaceId,
|
||||
DeleteFacesOptions,
|
||||
IPersonRepository,
|
||||
PeopleStatistics,
|
||||
PersonNameResponse,
|
||||
PersonNameSearchOptions,
|
||||
PersonSearchOptions,
|
||||
PersonStatistics,
|
||||
SelectFaceOptions,
|
||||
UnassignFacesOptions,
|
||||
UpdateFacesData,
|
||||
} from 'src/interfaces/person.interface';
|
||||
import { mapUpsertColumns } from 'src/utils/database';
|
||||
import { Paginated, PaginationOptions } from 'src/utils/pagination';
|
||||
import { FindOptionsRelations } from 'typeorm';
|
||||
|
||||
export interface PersonSearchOptions {
|
||||
minimumFaceCount: number;
|
||||
withHidden: boolean;
|
||||
closestFaceAssetId?: string;
|
||||
}
|
||||
|
||||
export interface PersonNameSearchOptions {
|
||||
withHidden?: boolean;
|
||||
}
|
||||
|
||||
export interface PersonNameResponse {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface AssetFaceId {
|
||||
assetId: string;
|
||||
personId: string;
|
||||
}
|
||||
|
||||
export interface UpdateFacesData {
|
||||
oldPersonId?: string;
|
||||
faceIds?: string[];
|
||||
newPersonId: string;
|
||||
}
|
||||
|
||||
export interface PersonStatistics {
|
||||
assets: number;
|
||||
}
|
||||
|
||||
export interface PeopleStatistics {
|
||||
total: number;
|
||||
hidden: number;
|
||||
}
|
||||
|
||||
export interface DeleteFacesOptions {
|
||||
sourceType: SourceType;
|
||||
}
|
||||
|
||||
export type UnassignFacesOptions = DeleteFacesOptions;
|
||||
|
||||
export type SelectFaceOptions = (keyof Selectable<AssetFaces>)[];
|
||||
|
||||
const withPerson = (eb: ExpressionBuilder<DB, 'asset_faces'>) => {
|
||||
return jsonObjectFrom(
|
||||
eb.selectFrom('person').selectAll('person').whereRef('person.id', '=', 'asset_faces.personId'),
|
||||
@@ -43,7 +73,7 @@ const withFaceSearch = (eb: ExpressionBuilder<DB, 'asset_faces'>) => {
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class PersonRepository implements IPersonRepository {
|
||||
export class PersonRepository {
|
||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||
|
||||
@GenerateSql({ params: [{ oldPersonId: DummyValue.UUID, newPersonId: DummyValue.UUID }] })
|
||||
|
||||
@@ -6,26 +6,202 @@ import { DB } from 'src/db';
|
||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { AssetEntity, searchAssetBuilder } from 'src/entities/asset.entity';
|
||||
import { GeodataPlacesEntity } from 'src/entities/geodata-places.entity';
|
||||
import { AssetType } from 'src/enum';
|
||||
import {
|
||||
AssetDuplicateSearch,
|
||||
AssetSearchOptions,
|
||||
FaceEmbeddingSearch,
|
||||
GetCameraMakesOptions,
|
||||
GetCameraModelsOptions,
|
||||
GetCitiesOptions,
|
||||
GetStatesOptions,
|
||||
ISearchRepository,
|
||||
SearchPaginationOptions,
|
||||
SmartSearchOptions,
|
||||
} from 'src/interfaces/search.interface';
|
||||
import { AssetStatus, AssetType } from 'src/enum';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import { anyUuid, asUuid } from 'src/utils/database';
|
||||
import { Paginated } from 'src/utils/pagination';
|
||||
import { isValidInteger } from 'src/validation';
|
||||
|
||||
export interface SearchResult<T> {
|
||||
/** total matches */
|
||||
total: number;
|
||||
/** collection size */
|
||||
count: number;
|
||||
/** current page */
|
||||
page: number;
|
||||
/** items for page */
|
||||
items: T[];
|
||||
/** score */
|
||||
distances: number[];
|
||||
facets: SearchFacet[];
|
||||
}
|
||||
|
||||
export interface SearchFacet {
|
||||
fieldName: string;
|
||||
counts: Array<{
|
||||
count: number;
|
||||
value: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export type SearchExploreItemSet<T> = Array<{
|
||||
value: string;
|
||||
data: T;
|
||||
}>;
|
||||
|
||||
export interface SearchExploreItem<T> {
|
||||
fieldName: string;
|
||||
items: SearchExploreItemSet<T>;
|
||||
}
|
||||
|
||||
export interface SearchAssetIDOptions {
|
||||
checksum?: Buffer;
|
||||
deviceAssetId?: string;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export interface SearchUserIdOptions {
|
||||
deviceId?: string;
|
||||
libraryId?: string | null;
|
||||
userIds?: string[];
|
||||
}
|
||||
|
||||
export type SearchIdOptions = SearchAssetIDOptions & SearchUserIdOptions;
|
||||
|
||||
export interface SearchStatusOptions {
|
||||
isArchived?: boolean;
|
||||
isEncoded?: boolean;
|
||||
isFavorite?: boolean;
|
||||
isMotion?: boolean;
|
||||
isOffline?: boolean;
|
||||
isVisible?: boolean;
|
||||
isNotInAlbum?: boolean;
|
||||
type?: AssetType;
|
||||
status?: AssetStatus;
|
||||
withArchived?: boolean;
|
||||
withDeleted?: boolean;
|
||||
}
|
||||
|
||||
export interface SearchOneToOneRelationOptions {
|
||||
withExif?: boolean;
|
||||
withStacked?: boolean;
|
||||
}
|
||||
|
||||
export interface SearchRelationOptions extends SearchOneToOneRelationOptions {
|
||||
withFaces?: boolean;
|
||||
withPeople?: boolean;
|
||||
}
|
||||
|
||||
export interface SearchDateOptions {
|
||||
createdBefore?: Date;
|
||||
createdAfter?: Date;
|
||||
takenBefore?: Date;
|
||||
takenAfter?: Date;
|
||||
trashedBefore?: Date;
|
||||
trashedAfter?: Date;
|
||||
updatedBefore?: Date;
|
||||
updatedAfter?: Date;
|
||||
}
|
||||
|
||||
export interface SearchPathOptions {
|
||||
encodedVideoPath?: string;
|
||||
originalFileName?: string;
|
||||
originalPath?: string;
|
||||
previewPath?: string;
|
||||
thumbnailPath?: string;
|
||||
}
|
||||
|
||||
export interface SearchExifOptions {
|
||||
city?: string | null;
|
||||
country?: string | null;
|
||||
lensModel?: string | null;
|
||||
make?: string | null;
|
||||
model?: string | null;
|
||||
state?: string | null;
|
||||
description?: string | null;
|
||||
}
|
||||
|
||||
export interface SearchEmbeddingOptions {
|
||||
embedding: string;
|
||||
userIds: string[];
|
||||
}
|
||||
|
||||
export interface SearchPeopleOptions {
|
||||
personIds?: string[];
|
||||
}
|
||||
|
||||
export interface SearchTagOptions {
|
||||
tagIds?: string[];
|
||||
}
|
||||
|
||||
export interface SearchOrderOptions {
|
||||
orderDirection?: 'asc' | 'desc';
|
||||
}
|
||||
|
||||
export interface SearchPaginationOptions {
|
||||
page: number;
|
||||
size: number;
|
||||
}
|
||||
|
||||
type BaseAssetSearchOptions = SearchDateOptions &
|
||||
SearchIdOptions &
|
||||
SearchExifOptions &
|
||||
SearchOrderOptions &
|
||||
SearchPathOptions &
|
||||
SearchStatusOptions &
|
||||
SearchUserIdOptions &
|
||||
SearchPeopleOptions &
|
||||
SearchTagOptions;
|
||||
|
||||
export type AssetSearchOptions = BaseAssetSearchOptions & SearchRelationOptions;
|
||||
|
||||
export type AssetSearchOneToOneRelationOptions = BaseAssetSearchOptions & SearchOneToOneRelationOptions;
|
||||
|
||||
export type AssetSearchBuilderOptions = Omit<AssetSearchOptions, 'orderDirection'>;
|
||||
|
||||
export type SmartSearchOptions = SearchDateOptions &
|
||||
SearchEmbeddingOptions &
|
||||
SearchExifOptions &
|
||||
SearchOneToOneRelationOptions &
|
||||
SearchStatusOptions &
|
||||
SearchUserIdOptions &
|
||||
SearchPeopleOptions &
|
||||
SearchTagOptions;
|
||||
|
||||
export interface FaceEmbeddingSearch extends SearchEmbeddingOptions {
|
||||
hasPerson?: boolean;
|
||||
numResults: number;
|
||||
maxDistance: number;
|
||||
}
|
||||
|
||||
export interface AssetDuplicateSearch {
|
||||
assetId: string;
|
||||
embedding: string;
|
||||
maxDistance: number;
|
||||
type: AssetType;
|
||||
userIds: string[];
|
||||
}
|
||||
|
||||
export interface FaceSearchResult {
|
||||
distance: number;
|
||||
id: string;
|
||||
personId: string | null;
|
||||
}
|
||||
|
||||
export interface AssetDuplicateResult {
|
||||
assetId: string;
|
||||
duplicateId: string | null;
|
||||
distance: number;
|
||||
}
|
||||
|
||||
export interface GetStatesOptions {
|
||||
country?: string;
|
||||
}
|
||||
|
||||
export interface GetCitiesOptions extends GetStatesOptions {
|
||||
state?: string;
|
||||
}
|
||||
|
||||
export interface GetCameraModelsOptions {
|
||||
make?: string;
|
||||
}
|
||||
|
||||
export interface GetCameraMakesOptions {
|
||||
model?: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class SearchRepository implements ISearchRepository {
|
||||
export class SearchRepository {
|
||||
constructor(
|
||||
private logger: LoggingRepository,
|
||||
@InjectKysely() private db: Kysely<DB>,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Insertable, Kysely, Updateable } from 'kysely';
|
||||
import { jsonObjectFrom } from 'kysely/helpers/postgres';
|
||||
import { InjectKysely } from 'nestjs-kysely';
|
||||
import { columns } from 'src/database';
|
||||
import { DB, Sessions } from 'src/db';
|
||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { withUser } from 'src/entities/session.entity';
|
||||
@@ -25,9 +27,16 @@ export class SessionRepository {
|
||||
getByToken(token: string) {
|
||||
return this.db
|
||||
.selectFrom('sessions')
|
||||
.innerJoinLateral(withUser, (join) => join.onTrue())
|
||||
.selectAll('sessions')
|
||||
.select((eb) => eb.fn.toJson('user').as('user'))
|
||||
.select((eb) => [
|
||||
...columns.authSession,
|
||||
jsonObjectFrom(
|
||||
eb
|
||||
.selectFrom('users')
|
||||
.select(columns.authUser)
|
||||
.whereRef('users.id', '=', 'sessions.userId')
|
||||
.where('users.deletedAt', 'is', null),
|
||||
).as('user'),
|
||||
])
|
||||
.where('sessions.token', '=', token)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
@@ -3,14 +3,19 @@ import { Insertable, Kysely, sql, Updateable } from 'kysely';
|
||||
import { jsonObjectFrom } from 'kysely/helpers/postgres';
|
||||
import _ from 'lodash';
|
||||
import { InjectKysely } from 'nestjs-kysely';
|
||||
import { columns } from 'src/database';
|
||||
import { DB, SharedLinks } from 'src/db';
|
||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { SharedLinkEntity } from 'src/entities/shared-link.entity';
|
||||
import { SharedLinkType } from 'src/enum';
|
||||
import { ISharedLinkRepository, SharedLinkSearchOptions } from 'src/interfaces/shared-link.interface';
|
||||
|
||||
export type SharedLinkSearchOptions = {
|
||||
userId: string;
|
||||
albumId?: string;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class SharedLinkRepository implements ISharedLinkRepository {
|
||||
export class SharedLinkRepository {
|
||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] })
|
||||
@@ -92,7 +97,7 @@ export class SharedLinkRepository implements ISharedLinkRepository {
|
||||
.executeTakeFirst() as Promise<SharedLinkEntity | undefined>;
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
@GenerateSql({ params: [{ userId: DummyValue.UUID, albumId: DummyValue.UUID }] })
|
||||
getAll({ userId, albumId }: SharedLinkSearchOptions): Promise<SharedLinkEntity[]> {
|
||||
return this.db
|
||||
.selectFrom('shared_links')
|
||||
@@ -156,39 +161,20 @@ export class SharedLinkRepository implements ISharedLinkRepository {
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.BUFFER] })
|
||||
async getByKey(key: Buffer): Promise<SharedLinkEntity | undefined> {
|
||||
async getByKey(key: Buffer) {
|
||||
return this.db
|
||||
.selectFrom('shared_links')
|
||||
.selectAll('shared_links')
|
||||
.where('shared_links.key', '=', key)
|
||||
.leftJoin('albums', 'albums.id', 'shared_links.albumId')
|
||||
.where('albums.deletedAt', 'is', null)
|
||||
.select((eb) =>
|
||||
.select((eb) => [
|
||||
...columns.authSharedLink,
|
||||
jsonObjectFrom(
|
||||
eb
|
||||
.selectFrom('users')
|
||||
.select([
|
||||
'users.id',
|
||||
'users.email',
|
||||
'users.createdAt',
|
||||
'users.profileImagePath',
|
||||
'users.isAdmin',
|
||||
'users.shouldChangePassword',
|
||||
'users.deletedAt',
|
||||
'users.oauthId',
|
||||
'users.updatedAt',
|
||||
'users.storageLabel',
|
||||
'users.name',
|
||||
'users.quotaSizeInBytes',
|
||||
'users.quotaUsageInBytes',
|
||||
'users.status',
|
||||
'users.profileChangedAt',
|
||||
])
|
||||
.whereRef('users.id', '=', 'shared_links.userId'),
|
||||
eb.selectFrom('users').select(columns.authUser).whereRef('users.id', '=', 'shared_links.userId'),
|
||||
).as('user'),
|
||||
)
|
||||
])
|
||||
.where((eb) => eb.or([eb('shared_links.type', '=', SharedLinkType.INDIVIDUAL), eb('albums.id', 'is not', null)]))
|
||||
.executeTakeFirst() as Promise<SharedLinkEntity | undefined>;
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async create(entity: Insertable<SharedLinks> & { assetIds?: string[] }): Promise<SharedLinkEntity> {
|
||||
|
||||
@@ -5,9 +5,13 @@ import { InjectKysely } from 'nestjs-kysely';
|
||||
import { DB } from 'src/db';
|
||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { StackEntity } from 'src/entities/stack.entity';
|
||||
import { IStackRepository, StackSearch } from 'src/interfaces/stack.interface';
|
||||
import { asUuid } from 'src/utils/database';
|
||||
|
||||
export interface StackSearch {
|
||||
ownerId: string;
|
||||
primaryAssetId?: string;
|
||||
}
|
||||
|
||||
const withAssets = (eb: ExpressionBuilder<DB, 'asset_stack'>, withTags = false) => {
|
||||
return jsonArrayFrom(
|
||||
eb
|
||||
@@ -35,7 +39,7 @@ const withAssets = (eb: ExpressionBuilder<DB, 'asset_stack'>, withTags = false)
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class StackRepository implements IStackRepository {
|
||||
export class StackRepository {
|
||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||
|
||||
@GenerateSql({ params: [{ ownerId: DummyValue.UUID }] })
|
||||
|
||||
@@ -5,20 +5,38 @@ import { escapePath, glob, globStream } from 'fast-glob';
|
||||
import { constants, createReadStream, createWriteStream, existsSync, mkdirSync } from 'node:fs';
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { Writable } from 'node:stream';
|
||||
import { Readable, Writable } from 'node:stream';
|
||||
import { CrawlOptionsDto, WalkOptionsDto } from 'src/dtos/library.dto';
|
||||
import {
|
||||
DiskUsage,
|
||||
IStorageRepository,
|
||||
ImmichReadStream,
|
||||
ImmichZipStream,
|
||||
WatchEvents,
|
||||
} from 'src/interfaces/storage.interface';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import { mimeTypes } from 'src/utils/mime-types';
|
||||
|
||||
export interface WatchEvents {
|
||||
onReady(): void;
|
||||
onAdd(path: string): void;
|
||||
onChange(path: string): void;
|
||||
onUnlink(path: string): void;
|
||||
onError(error: Error): void;
|
||||
}
|
||||
|
||||
export interface ImmichReadStream {
|
||||
stream: Readable;
|
||||
type?: string;
|
||||
length?: number;
|
||||
}
|
||||
|
||||
export interface ImmichZipStream extends ImmichReadStream {
|
||||
addFile: (inputPath: string, filename: string) => void;
|
||||
finalize: () => Promise<void>;
|
||||
}
|
||||
|
||||
export interface DiskUsage {
|
||||
available: number;
|
||||
free: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class StorageRepository implements IStorageRepository {
|
||||
export class StorageRepository {
|
||||
constructor(private logger: LoggingRepository) {
|
||||
this.logger.setContext(StorageRepository.name);
|
||||
}
|
||||
|
||||
@@ -2,12 +2,13 @@ import { Injectable } from '@nestjs/common';
|
||||
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
|
||||
import { Chunked, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { TagEntity } from 'src/entities/tag.entity';
|
||||
import { AssetTagItem, ITagRepository } from 'src/interfaces/tag.interface';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import { DataSource, In, Repository } from 'typeorm';
|
||||
|
||||
export type AssetTagItem = { assetId: string; tagId: string };
|
||||
|
||||
@Injectable()
|
||||
export class TagRepository implements ITagRepository {
|
||||
export class TagRepository {
|
||||
constructor(
|
||||
@InjectDataSource() private dataSource: DataSource,
|
||||
@InjectRepository(TagEntity) private repository: Repository<TagEntity>,
|
||||
|
||||
@@ -3,15 +3,9 @@ import { Insertable, Kysely, sql, Updateable } from 'kysely';
|
||||
import { InjectKysely } from 'nestjs-kysely';
|
||||
import { DB, UserMetadata as DbUserMetadata, Users } from 'src/db';
|
||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { UserMetadata } from 'src/entities/user-metadata.entity';
|
||||
import { UserMetadata, UserMetadataItem } from 'src/entities/user-metadata.entity';
|
||||
import { UserEntity, withMetadata } from 'src/entities/user.entity';
|
||||
import { UserStatus } from 'src/enum';
|
||||
import {
|
||||
IUserRepository,
|
||||
UserFindOptions,
|
||||
UserListFilter,
|
||||
UserStatsQueryResponse,
|
||||
} from 'src/interfaces/user.interface';
|
||||
import { asUuid } from 'src/utils/database';
|
||||
|
||||
const columns = [
|
||||
@@ -34,8 +28,27 @@ const columns = [
|
||||
|
||||
type Upsert = Insertable<DbUserMetadata>;
|
||||
|
||||
export interface UserListFilter {
|
||||
withDeleted?: boolean;
|
||||
}
|
||||
|
||||
export interface UserStatsQueryResponse {
|
||||
userId: string;
|
||||
userName: string;
|
||||
photos: number;
|
||||
videos: number;
|
||||
usage: number;
|
||||
usagePhotos: number;
|
||||
usageVideos: number;
|
||||
quotaSizeInBytes: number | null;
|
||||
}
|
||||
|
||||
export interface UserFindOptions {
|
||||
withDeleted?: boolean;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UserRepository implements IUserRepository {
|
||||
export class UserRepository {
|
||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.BOOLEAN] })
|
||||
@@ -51,6 +64,14 @@ export class UserRepository implements IUserRepository {
|
||||
.executeTakeFirst() as Promise<UserEntity | undefined>;
|
||||
}
|
||||
|
||||
getMetadata(userId: string) {
|
||||
return this.db
|
||||
.selectFrom('user_metadata')
|
||||
.select(['key', 'value'])
|
||||
.where('user_metadata.userId', '=', userId)
|
||||
.execute() as Promise<UserMetadataItem[]>;
|
||||
}
|
||||
|
||||
@GenerateSql()
|
||||
getAdmin(): Promise<UserEntity | undefined> {
|
||||
return this.db
|
||||
@@ -250,7 +271,7 @@ export class UserRepository implements IUserRepository {
|
||||
eb
|
||||
.selectFrom('assets')
|
||||
.leftJoin('exif', 'exif.assetId', 'assets.id')
|
||||
.select((eb) => eb.fn.coalesce(eb.fn.sum('exif.fileSizeInByte'), eb.lit(0)).as('usage'))
|
||||
.select((eb) => eb.fn.coalesce(eb.fn.sum<number>('exif.fileSizeInByte'), eb.lit(0)).as('usage'))
|
||||
.where('assets.libraryId', 'is', null)
|
||||
.where('assets.ownerId', '=', eb.ref('users.id')),
|
||||
updatedAt: new Date(),
|
||||
|
||||
@@ -18,6 +18,9 @@ export class ViewRepository {
|
||||
.where('isVisible', '=', true)
|
||||
.where('isArchived', '=', false)
|
||||
.where('deletedAt', 'is', null)
|
||||
.where('fileModifiedAt', 'is not', null)
|
||||
.where('fileModifiedAt', 'is not', null)
|
||||
.where('localDateTime', 'is not', null)
|
||||
.execute();
|
||||
|
||||
return results.map((row) => row.directoryPath.replaceAll(/^\/|\/$/g, ''));
|
||||
@@ -35,6 +38,9 @@ export class ViewRepository {
|
||||
.where('isVisible', '=', true)
|
||||
.where('isArchived', '=', false)
|
||||
.where('deletedAt', 'is', null)
|
||||
.where('fileModifiedAt', 'is not', null)
|
||||
.where('fileModifiedAt', 'is not', null)
|
||||
.where('localDateTime', 'is not', null)
|
||||
.where('originalPath', 'like', `%${normalizedPath}/%`)
|
||||
.where('originalPath', 'not like', `%${normalizedPath}/%/%`)
|
||||
.orderBy(
|
||||
|
||||
@@ -16,7 +16,7 @@ import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import { AlbumUserEntity } from 'src/entities/album-user.entity';
|
||||
import { AlbumEntity } from 'src/entities/album.entity';
|
||||
import { Permission } from 'src/enum';
|
||||
import { AlbumAssetCount, AlbumInfoOptions } from 'src/interfaces/album.interface';
|
||||
import { AlbumAssetCount, AlbumInfoOptions } from 'src/repositories/album.repository';
|
||||
import { BaseService } from 'src/services/base.service';
|
||||
import { addAssets, removeAssets } from 'src/utils/asset.util';
|
||||
|
||||
|
||||
@@ -9,8 +9,7 @@ import { AssetMediaStatus, AssetRejectReason, AssetUploadAction } from 'src/dtos
|
||||
import { AssetMediaCreateDto, AssetMediaReplaceDto, AssetMediaSize, UploadFieldName } from 'src/dtos/asset-media.dto';
|
||||
import { AssetFileEntity } from 'src/entities/asset-files.entity';
|
||||
import { ASSET_CHECKSUM_CONSTRAINT, AssetEntity } from 'src/entities/asset.entity';
|
||||
import { AssetFileType, AssetStatus, AssetType, CacheControl } from 'src/enum';
|
||||
import { JobName } from 'src/interfaces/job.interface';
|
||||
import { AssetFileType, AssetStatus, AssetType, CacheControl, JobName } from 'src/enum';
|
||||
import { AuthRequest } from 'src/middleware/auth.guard';
|
||||
import { AssetMediaService } from 'src/services/asset-media.service';
|
||||
import { ImmichFileResponse } from 'src/utils/file';
|
||||
|
||||
@@ -21,29 +21,22 @@ import {
|
||||
} from 'src/dtos/asset-media.dto';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import { ASSET_CHECKSUM_CONSTRAINT, AssetEntity } from 'src/entities/asset.entity';
|
||||
import { AssetStatus, AssetType, CacheControl, Permission, StorageFolder } from 'src/enum';
|
||||
import { JobName } from 'src/interfaces/job.interface';
|
||||
import { AssetStatus, AssetType, CacheControl, JobName, Permission, StorageFolder } from 'src/enum';
|
||||
import { AuthRequest } from 'src/middleware/auth.guard';
|
||||
import { BaseService } from 'src/services/base.service';
|
||||
import { UploadFile } from 'src/types';
|
||||
import { requireUploadAccess } from 'src/utils/access';
|
||||
import { asRequest, getAssetFiles, onBeforeLink } from 'src/utils/asset.util';
|
||||
import { getFilenameExtension, getFileNameWithoutExtension, ImmichFileResponse } from 'src/utils/file';
|
||||
import { mimeTypes } from 'src/utils/mime-types';
|
||||
import { fromChecksum } from 'src/utils/request';
|
||||
export interface UploadRequest {
|
||||
|
||||
interface UploadRequest {
|
||||
auth: AuthDto | null;
|
||||
fieldName: UploadFieldName;
|
||||
file: UploadFile;
|
||||
}
|
||||
|
||||
export interface UploadFile {
|
||||
uuid: string;
|
||||
checksum: Buffer;
|
||||
originalPath: string;
|
||||
originalName: string;
|
||||
size: number;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class AssetMediaService extends BaseService {
|
||||
async getUploadAssetIdByChecksum(auth: AuthDto, checksum?: string): Promise<AssetMediaResponseDto | undefined> {
|
||||
|
||||
@@ -3,9 +3,8 @@ import { DateTime } from 'luxon';
|
||||
import { mapAsset } from 'src/dtos/asset-response.dto';
|
||||
import { AssetJobName, AssetStatsResponseDto } from 'src/dtos/asset.dto';
|
||||
import { AssetEntity } from 'src/entities/asset.entity';
|
||||
import { AssetStatus, AssetType } from 'src/enum';
|
||||
import { AssetStats } from 'src/interfaces/asset.interface';
|
||||
import { JobName, JobStatus } from 'src/interfaces/job.interface';
|
||||
import { AssetStatus, AssetType, JobName, JobStatus } from 'src/enum';
|
||||
import { AssetStats } from 'src/repositories/asset.repository';
|
||||
import { AssetService } from 'src/services/asset.service';
|
||||
import { assetStub } from 'test/fixtures/asset.stub';
|
||||
import { authStub } from 'test/fixtures/auth.stub';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user