Compare commits
41 Commits
fix/genera
...
refactor/t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6e1170e6d | ||
|
|
53680d9643 | ||
|
|
b2d00405f1 | ||
|
|
cf60f4cdcd | ||
|
|
d764a59011 | ||
|
|
7ee1b977c1 | ||
|
|
9838634067 | ||
|
|
eee793bfe4 | ||
|
|
b3342323de | ||
|
|
6f3cb4f1bb | ||
|
|
54ed78d0bf | ||
|
|
265ed0b38f | ||
|
|
63c2f4415b | ||
|
|
a7cfd7f183 | ||
|
|
ee4c45d5d3 | ||
|
|
24334aa3df | ||
|
|
882baecf21 | ||
|
|
f16327d0ab | ||
|
|
8353db6a50 | ||
|
|
5270107926 | ||
|
|
740ca14a68 | ||
|
|
966ab22065 | ||
|
|
78fbe0fd49 | ||
|
|
5862c454b7 | ||
|
|
8ee495b08f | ||
|
|
83db851b00 | ||
|
|
70037018c8 | ||
|
|
796444d211 | ||
|
|
0d66a15d9b | ||
|
|
3cf8ed5f2d | ||
|
|
ff01af2450 | ||
|
|
2de1b832e5 | ||
|
|
25142bb6c6 | ||
|
|
01660b20fd | ||
|
|
9affee1ea0 | ||
|
|
d02a82b618 | ||
|
|
ad87dff18d | ||
|
|
b7e06e7b6f | ||
|
|
21f49572b1 | ||
|
|
2b7d28528d | ||
|
|
cf4cf56ac0 |
@@ -6,28 +6,35 @@ services:
|
||||
- IMMICH_SERVER_URL=http://127.0.0.1:2283/
|
||||
volumes: !override # bind mount host to /workspaces/immich
|
||||
- ..:/workspaces/immich
|
||||
- cli_node_modules:/workspaces/immich/cli/node_modules
|
||||
- e2e_node_modules:/workspaces/immich/e2e/node_modules
|
||||
- open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules
|
||||
- server_node_modules:/workspaces/immich/server/node_modules
|
||||
- web_node_modules:/workspaces/immich/web/node_modules
|
||||
- ${UPLOAD_LOCATION}/photos:/data
|
||||
- ${UPLOAD_LOCATION:-upload-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/data
|
||||
- pnpm-store:/usr/src/app/.pnpm-store
|
||||
- server-node_modules:/usr/src/app/server/node_modules
|
||||
- web-node_modules:/usr/src/app/web/node_modules
|
||||
- github-node_modules:/usr/src/app/.github/node_modules
|
||||
- cli-node_modules:/usr/src/app/cli/node_modules
|
||||
- docs-node_modules:/usr/src/app/docs/node_modules
|
||||
- e2e-node_modules:/usr/src/app/e2e/node_modules
|
||||
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
|
||||
- app-node_modules:/usr/src/app/node_modules
|
||||
- sveltekit:/usr/src/app/web/.svelte-kit
|
||||
- coverage:/usr/src/app/web/coverage
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
|
||||
immich-web:
|
||||
env_file: !reset []
|
||||
immich-machine-learning:
|
||||
env_file: !reset []
|
||||
database:
|
||||
env_file: !reset []
|
||||
environment: !override
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD-postgres}
|
||||
POSTGRES_USER: ${DB_USERNAME-postgres}
|
||||
POSTGRES_DB: ${DB_DATABASE_NAME-immich}
|
||||
POSTGRES_INITDB_ARGS: '--data-checksums'
|
||||
POSTGRES_HOST_AUTH_METHOD: md5
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
|
||||
|
||||
- ${UPLOAD_LOCATION:-postgres-devcontainer-volume}${UPLOAD_LOCATION:+/postgres}:/var/lib/postgresql/data
|
||||
redis:
|
||||
env_file: !reset []
|
||||
volumes:
|
||||
# Node modules for each service to avoid conflicts and ensure consistent dependencies
|
||||
cli_node_modules:
|
||||
e2e_node_modules:
|
||||
open_api_node_modules:
|
||||
server_node_modules:
|
||||
web_node_modules:
|
||||
|
||||
# UPLOAD_LOCATION must be set to a absolute path or vol-upload
|
||||
vol-upload:
|
||||
|
||||
# DB_DATA_LOCATION must be set to a absolute path or vol-database
|
||||
vol-database:
|
||||
upload-devcontainer-volume:
|
||||
postgres-devcontainer-volume:
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
"userEnvProbe": "loginInteractiveShell",
|
||||
"remoteEnv": {
|
||||
// The location where your uploaded files are stored
|
||||
"UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./Library}",
|
||||
"UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./library}",
|
||||
// Connection secret for postgres. You should change it to a random password
|
||||
// Please use only the characters `A-Za-z0-9`, without special characters or spaces
|
||||
"DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}",
|
||||
|
||||
4
.github/workflows/build-mobile.yml
vendored
@@ -73,7 +73,7 @@ jobs:
|
||||
|
||||
- name: Restore Gradle Cache
|
||||
id: cache-gradle-restore
|
||||
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
|
||||
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
@@ -130,7 +130,7 @@ jobs:
|
||||
|
||||
- name: Save Gradle Cache
|
||||
id: cache-gradle-save
|
||||
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
|
||||
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
if: github.ref == 'refs/heads/main'
|
||||
with:
|
||||
path: |
|
||||
|
||||
4
.github/workflows/cli.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
||||
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- run: pnpm build
|
||||
- run: pnpm publish
|
||||
- run: pnpm publish --no-git-checks
|
||||
if: ${{ github.event_name == 'release' }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
with:
|
||||
registry: ghcr.io
|
||||
|
||||
6
.github/workflows/codeql-analysis.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
|
||||
uses: github/codeql-action/init@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
|
||||
uses: github/codeql-action/autobuild@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
@@ -76,6 +76,6 @@ jobs:
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
|
||||
uses: github/codeql-action/analyze@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5
|
||||
with:
|
||||
category: '/language:${{matrix.language}}'
|
||||
|
||||
4
.github/workflows/docker.yml
vendored
@@ -53,7 +53,7 @@ jobs:
|
||||
suffix: ['', '-cuda', '-rocm', '-openvino', '-armnn', '-rknn']
|
||||
steps:
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
suffix: ['']
|
||||
steps:
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
|
||||
2
.github/workflows/sdk.yml
vendored
@@ -35,6 +35,6 @@ jobs:
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
- name: Publish
|
||||
run: pnpm publish
|
||||
run: pnpm publish --no-git-checks
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
14
.vscode/launch.json
vendored
@@ -18,6 +18,20 @@
|
||||
"name": "Immich Workers",
|
||||
"remoteRoot": "/usr/src/app/server",
|
||||
"localRoot": "${workspaceFolder}/server"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Immich CLI",
|
||||
"program": "${workspaceFolder}/cli/dist/index.js",
|
||||
"args": ["upload", "--help"],
|
||||
"runtimeArgs": ["--enable-source-maps"],
|
||||
"console": "integratedTerminal",
|
||||
"resolveSourceMapLocations": ["${workspaceFolder}/cli/dist/**/*.js.map"],
|
||||
"sourceMaps": true,
|
||||
"outFiles": ["${workspaceFolder}/cli/dist/**/*.js"],
|
||||
"skipFiles": ["<node_internals>/**"],
|
||||
"preLaunchTask": "Build Immich CLI"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
5
.vscode/tasks.json
vendored
@@ -70,6 +70,11 @@
|
||||
"runOn": "folderOpen"
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Build Immich CLI",
|
||||
"type": "shell",
|
||||
"command": "pnpm --filter cli build:dev"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
<a href="readme_i18n/README_de_DE.md">Deutsch</a>
|
||||
<a href="readme_i18n/README_nl_NL.md">Nederlands</a>
|
||||
<a href="readme_i18n/README_tr_TR.md">Türkçe</a>
|
||||
<a href="readme_i18n/README_zh_CN.md">中文</a>
|
||||
<a href="readme_i18n/README_zh_CN.md">简体中文</a>
|
||||
<a href="readme_i18n/README_zh_TW.md">正體中文</a>
|
||||
<a href="readme_i18n/README_uk_UA.md">Українська</a>
|
||||
<a href="readme_i18n/README_ru_RU.md">Русский</a>
|
||||
<a href="readme_i18n/README_pt_BR.md">Português Brasileiro</a>
|
||||
|
||||
@@ -13,15 +13,23 @@ Then, to build the open-api client run the following in the open-api folder:
|
||||
|
||||
$ ./bin/generate-open-api.sh
|
||||
|
||||
To run the Immich CLI from source, run the following in the cli folder:
|
||||
## Run from build
|
||||
|
||||
Go to the cli folder and build it:
|
||||
|
||||
$ pnpm install
|
||||
$ pnpm run build
|
||||
$ ts-node .
|
||||
$ node dist/index.js
|
||||
|
||||
You'll need ts-node, the easiest way to install it is to use pnpm:
|
||||
## Run and Debug from source (VSCode)
|
||||
|
||||
$ pnpm i -g ts-node
|
||||
With VScode you can run and debug the Immich CLI. Go to the launch.json file, find the Immich CLI config and change this with the command you need to debug
|
||||
|
||||
`"args": ["upload", "--help"],`
|
||||
|
||||
replace that for the command of your choice.
|
||||
|
||||
## Install from build
|
||||
|
||||
You can also build and install the CLI using
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/micromatch": "^4.0.9",
|
||||
"@types/mock-fs": "^4.13.1",
|
||||
"@types/node": "^22.18.1",
|
||||
"@types/node": "^22.18.8",
|
||||
"@vitest/coverage-v8": "^3.0.0",
|
||||
"byte-size": "^9.0.0",
|
||||
"cli-progress": "^3.12.0",
|
||||
@@ -43,6 +43,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
"build:dev": "vite build --sourcemap true",
|
||||
"lint": "eslint \"src/**/*.ts\" --max-warnings 0",
|
||||
"lint:fix": "npm run lint -- --fix",
|
||||
"prepack": "npm run build",
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# External Libraries
|
||||
|
||||
:::info
|
||||
Currently an external library can only belong to a single user which is selected when the library is initially created.
|
||||
:::
|
||||
|
||||
External libraries track assets stored in the filesystem outside of Immich. When the external library is scanned, Immich will load videos and photos from disk and create the corresponding assets. These assets will then be shown in the main timeline, and they will look and behave like any other asset, including viewing on the map, adding to albums, etc. Later, if a file is modified outside of Immich, you need to scan the library for the changes to show up.
|
||||
|
||||
If an external asset is deleted from disk, Immich will move it to trash on rescan. To restore the asset, you need to restore the original file. After 30 days the file will be removed from trash, and any changes to metadata within Immich will be lost.
|
||||
|
||||
@@ -21,6 +21,10 @@ Restart Immich by running `docker compose up -d`.
|
||||
|
||||
# Create the library
|
||||
|
||||
:::info
|
||||
External library management requires administrator access and the steps below assume you are using an admin account.
|
||||
:::
|
||||
|
||||
In the Immich web UI:
|
||||
|
||||
- click the **Administration** link in the upper right corner.
|
||||
|
||||
@@ -16,7 +16,7 @@ Immich can easily be installed on a Synology NAS using Container Manager within
|
||||
|
||||
## Step 1 - Download the required files
|
||||
|
||||
Create a directory of your choice (e.g. `./immich-app`) to house Immich. In general, it's a best practice to have all Docker-based applications running under the `./docker` directory, so in this case, your directory structure will look like `./docker/immich-app`.
|
||||
Create a directory of your choice (e.g. `./immich-app`) to house Immich. In general, it's best practice to have all Docker-based applications running under the `./docker` directory, so in this case, your directory structure will look like `./docker/immich-app`.
|
||||
|
||||
Now create a `./postgres` and `./library` directory as sub-directories of the `./docker/immich-app`.
|
||||
|
||||
@@ -25,7 +25,7 @@ When you're all done, you should have the following:
|
||||
- `./docker/immich-app/postgres`
|
||||
- `./docker/immich-app/library`
|
||||
|
||||
Download [`docker-compose.yml`](https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml) and [`example.env`](https://github.com/immich-app/immich/releases/latest/download/example.env) to your computer. Upload the files to the `./docker/immich-app` directory, and rename `example.env` to `.env`.
|
||||
Download [`docker-compose.yml`](https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml) and [`example.env`](https://github.com/immich-app/immich/releases/latest/download/example.env) to your computer. Upload the files to the `./docker/immich-app` directory, and rename `example.env` to `.env`. Note: If you plan to use the Synology Text editor to edit the `.env` file on the NAS within File Station, you will need to rename it to a temporary name (e.g. `example.txt`) in order to see 'Open with Text Editor' in the file context menu. Once saved, rename it back to `.env`.
|
||||
|
||||
## Step 2 - Populate the .env file with custom values
|
||||
|
||||
@@ -34,23 +34,23 @@ Follow [Step 2 in Docker Compose](/install/docker-compose#step-2---populate-the-
|
||||
## Step 3 - Create a new project in Container Manager
|
||||
|
||||
Open Container Manager, and select the "**Project**" action on the left navigation bar and then click "**Create**".
|
||||

|
||||

|
||||
|
||||
In the settings of your new project, set "**Project name**" to a name you'll remember, such as _immich-app_. When setting the "**Path**", select the `./docker/immich-app` directory you created earlier. Doing so will prompt a message to use the existing `docker-compose.yml` already present in the directory for your project. Click "**OK**" to continue.
|
||||
|
||||

|
||||

|
||||
|
||||
The following screen will give you the option to further customize your `docker-compose.yml` file, giving you a warning regarding the `start_interval` property. Under the `healthcheck` heading, remove the `start_interval: 30s` completely and click "**Next**".
|
||||
The following screen will give you the option to further customize your `docker-compose.yml` file. Take note of `DB_STORAGE_TYPE: 'HDD'`and uncomment if applicable for your Synology setup.
|
||||
|
||||

|
||||

|
||||
|
||||
Skip the section asking to set-up a portal for Web Station, and then complete the wizard which will build and start the containers for your project.
|
||||
|
||||
Once your containers are successfully running, navigate to the "**Container**" section of Container Manager, right-click on the "**immich-server**" container, and choose the "**Details**".
|
||||
|
||||
Scroll to the bottom of the "**Details**" section, and find the `IP Address` of the container, located in the `Network` section. Take note of the container's IP address as you will need it for **Step 4**.
|
||||
Scroll to the bottom of the "**Details**" section and find the `IP Address` listed in the `Network` section. Take note of the container's IP address as you will need it for **Step 4**.
|
||||
|
||||

|
||||

|
||||
|
||||
## Step 4 - Configure Firewall Settings
|
||||
|
||||
@@ -63,8 +63,66 @@ Open "**Control Panel**" on your Synology NAS, and select "**Security**". Naviga
|
||||
Click "**Edit Rules**" and add the following firewall rules:
|
||||
|
||||
- Add a "**Source IP**" rule for the IP address of your container that you obtained in Step 3 above
|
||||
|
||||

|
||||
|
||||
- Add a "**Ports**" rule for the port specified in the `docker-compose.yml`, which should be `2283`
|
||||
|
||||

|
||||
|
||||
## Next Steps
|
||||
|
||||
Read the [Post Installation](/install/post-install.mdx) steps and [upgrade instructions](/install/upgrading.md).
|
||||
|
||||
<details>
|
||||
<summary>Updating Immich using Container Manager</summary>
|
||||
Check the post installation and upgrade instructions at the links above before proceeding with this section.
|
||||
|
||||
## Step 1. Backup
|
||||
|
||||
Ensure your photos and videos are backed up. Your `.env` settings will define where they are stored. There is no need to delete any files or folders within the `docker` folder when doing a release upgrade unless instructed in the release notes.
|
||||
|
||||
## Step 2. Check release notes
|
||||
|
||||
Always check the [release notes](https://github.com/immich-app/immich/releases) before proceeding with an update!
|
||||
|
||||
## Step 3. Stop containers & clean up
|
||||
|
||||
Open **Container Manager**. Select **Project** then your Immich app
|
||||
|
||||

|
||||
|
||||
Select **Stop**
|
||||
|
||||

|
||||
|
||||
Select **Action** then **Clean**. This removes the containers.
|
||||
|
||||

|
||||
|
||||
Go to **Image** and select **Remove Unused Images**.
|
||||
|
||||

|
||||
|
||||
## Step 4. Build
|
||||
|
||||
Go to **Project**, select **Action** then **Build**. This will download, unpack, install and start the containers.
|
||||
|
||||

|
||||
|
||||
## Step 5. Update firewall rule
|
||||
|
||||
The default behavior is to automatically start the containers once installed. If `immich_server` runs for a few seconds and then stops, it may be because the firewall rule no longer matches the server IP address.
|
||||
|
||||
Go to the **Container** section. Click on `immich_server` and scroll down on **General** to find the IP address.
|
||||

|
||||
|
||||
Go to Synology **Control Panel**. Select **Security** and **Firewall**.
|
||||
|
||||

|
||||
|
||||
In this example, the IP addresses mismatch and the firewall rule needs to be edited to match above.
|
||||
|
||||

|
||||
|
||||
</details>
|
||||
|
||||
@@ -387,27 +387,35 @@ To migrate from the old storage configuration to the new one, you will need to c
|
||||
3. **Copy the data** from the old datasets to the new dataset. We advise using the `rsync` command to copy the data, as it will preserve the permissions and ownership of the files. The following commands are examples:
|
||||
|
||||
```bash
|
||||
rsync -av /mnt/tank/immich/library/ /mnt/tank/immich/data/library/
|
||||
rsync -av /mnt/tank/immich/upload/ /mnt/tank/immich/data/upload/
|
||||
rsync -av /mnt/tank/immich/thumbs/ /mnt/tank/immich/data/thumbs/
|
||||
rsync -av /mnt/tank/immich/profile/ /mnt/tank/immich/data/profile/
|
||||
rsync -av /mnt/tank/immich/video/ /mnt/tank/immich/data/encoded-video/
|
||||
rsync -av /mnt/tank/immich/backups/ /mnt/tank/immich/data/backups/
|
||||
sudo rsync -av /mnt/tank/immich/library/ /mnt/tank/immich/data/library/
|
||||
sudo rsync -av /mnt/tank/immich/upload/ /mnt/tank/immich/data/upload/
|
||||
sudo rsync -av /mnt/tank/immich/thumbs/ /mnt/tank/immich/data/thumbs/
|
||||
sudo rsync -av /mnt/tank/immich/profile/ /mnt/tank/immich/data/profile/
|
||||
sudo rsync -av /mnt/tank/immich/video/ /mnt/tank/immich/data/encoded-video/
|
||||
sudo rsync -av /mnt/tank/immich/backups/ /mnt/tank/immich/data/backups/
|
||||
```
|
||||
|
||||
Make sure to replace `/mnt/tank/immich/` with the correct path to your old datasets and `/mnt/tank/immich/data/` with the correct path to your new dataset.
|
||||
|
||||
:::tip
|
||||
If you were using **ixVolume (dataset created automatically by the system)** for Immich data storage, the path to the data should be `/mnt/.ix-apps/app_mounts/immich/`. You have to use this path instead of `/mnt/tank/immich/` in the `rsync` command above, for example:
|
||||
If you were using **ixVolume (dataset created automatically by the system)** for some of Immich data storage, the path to the data should be `/mnt/.ix-apps/app_mounts/immich/`. You have to use this path instead of `/mnt/tank/immich/` in the `rsync` command above, for example:
|
||||
|
||||
```bash
|
||||
rsync -av /mnt/.ix-apps/app_mounts/immich/library/ /mnt/tank/immich/data/library/
|
||||
sudo rsync -av /mnt/.ix-apps/app_mounts/immich/library/ /mnt/tank/immich/data/library/
|
||||
```
|
||||
|
||||
If you also were storing your files in the **ixVolume**, the **_upload_** folder is named `uploads` instead of `upload`, so the command to run should be:
|
||||
|
||||
```bash
|
||||
sudo rsync -av /mnt/.ix-apps/app_mounts/immich/uploads/ /mnt/tank/immich/data/upload/
|
||||
```
|
||||
|
||||
This means that depending on your old storage configuration, you might have to use a mix of paths in the `rsync` commands above.
|
||||
|
||||
If you were also using an ixVolume for Postgres data storage, you also should, first create the pgData dataset, as described in the [Setting up Storage Datasets](#setting-up-storage-datasets) section above, and then you can use the following command to copy the Postgres data:
|
||||
|
||||
```bash
|
||||
rsync -av /mnt/.ix-apps/app_mounts/immich/pgData/ /mnt/tank/immich/pgData/
|
||||
sudo rsync -av /mnt/.ix-apps/app_mounts/immich/pgData/ /mnt/tank/immich/pgData/
|
||||
```
|
||||
|
||||
:::
|
||||
@@ -416,7 +424,7 @@ rsync -av /mnt/.ix-apps/app_mounts/immich/pgData/ /mnt/tank/immich/pgData/
|
||||
Make sure that for each folder, the `.immich` file is copied as well, as it contains important metadata for Immich. If for some reason the `.immich` file is not copied, you can copy it manually with the `rsync` command, for example:
|
||||
|
||||
```bash
|
||||
rsync -av /mnt/tank/immich/library/.immich /mnt/tank/immich/data/library/
|
||||
sudo rsync -av /mnt/tank/immich/library/.immich /mnt/tank/immich/data/library/
|
||||
```
|
||||
|
||||
Replace `library` with the name of the folder where you are copying the file.
|
||||
@@ -437,38 +445,37 @@ This will recreate the Immich container with the new storage configuration and s
|
||||
|
||||
If everything went well, you should now be able to access Immich with the new storage configuration. You can verify that the data has been copied correctly by checking the Immich web interface and ensuring that all your photos and videos are still available. You may delete the old datasets, if you no longer need them, using the TrueNAS web interface.
|
||||
|
||||
:::tip
|
||||
If you were using **ixVolume (dataset created automatically by the system)** or folders for Immich data storage, you can delete the old datasets using the following commands:
|
||||
|
||||
```bash
|
||||
rm -r /mnt/.ix-apps/app_mounts/immich/library
|
||||
rm -r /mnt/.ix-apps/app_mounts/immich/uploads
|
||||
rm -r /mnt/.ix-apps/app_mounts/immich/thumbs
|
||||
rm -r /mnt/.ix-apps/app_mounts/immich/profile
|
||||
rm -r /mnt/.ix-apps/app_mounts/immich/video
|
||||
rm -r /mnt/.ix-apps/app_mounts/immich/backups
|
||||
sudo rm -r /mnt/.ix-apps/app_mounts/immich/*
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="migrate-old-dataset" label="Keep the existing datasets">
|
||||
|
||||
|
||||
To migrate from the old storage configuration to the new one without creating new datasets.
|
||||
|
||||
1. **Stop the Immich app** from the TrueNAS web interface to ensure no data is being written while you are updating the app.
|
||||
2. **Update the datasets permissions**: Ensure that the datasets used for Immich data storage (`library`, `upload`, `thumbs`, `profile`, `video`, `backups`) have the correct permissions set for the user who will run Immich. The user should have ***modify*** permissions on these datasets. The default user for Immich is `apps` (UID 568) and the default group is `apps` (GID 568). If you are using a different user, make sure to set the permissions accordingly. You can do this from the TrueNAS web interface by going to the **Datasets** screen, selecting each dataset, clicking on the **Edit** button next to **Permissions**, and adding the user with ***modify*** permissions.
|
||||
2. **Update the datasets permissions**: Ensure that the datasets used for Immich data storage (`library`, `upload`, `thumbs`, `profile`, `video`, `backups`) have the correct permissions set for the user who will run Immich. The user should have **_modify_** permissions on these datasets. The default user for Immich is `apps` (UID 568) and the default group is `apps` (GID 568). If you are using a different user, make sure to set the permissions accordingly. You can do this from the TrueNAS web interface by going to the **Datasets** screen, selecting each dataset, clicking on the **Edit** button next to **Permissions**, and adding the user with **_modify_** permissions.
|
||||
3. **Update the Immich app** to use the existing datasets:
|
||||
- Go to the **Installed Applications** screen and select Immich from the list of installed applications.
|
||||
- Click **Edit** on the **Application Info** widget.
|
||||
- In the **Storage Configuration** section, untick the **Use Old Storage Configuration (Deprecated)** checkbox.
|
||||
- For the **Data Storage**, you can keep the **ixVolume (dataset created automatically by the system)** as no data will be directly written to it. We recommend selecting **Host Path (Path that already exists on the system)** and then select a **new** dataset you created for Immich data storage, for example, `data`.
|
||||
- For the **Postgres Data Storage**, keep **Host Path (Path that already exists on the system)** and then select the existing dataset you used for Postgres data storage, for example, `pgData`.
|
||||
- Following the instructions in the [Multiple Datasets for Immich Storage](#additional-storage-advanced-users) section, you can add, **for each old dataset**, a new Additional Storage with the following settings:
|
||||
- **Type**: `Host Path (Path that already exists on the system)`
|
||||
- **Mount Path**: `/data/<folder-name>` (e.g. `/data/library`)
|
||||
- **Host Path**: `/mnt/<your-pool-name>/<dataset-name>` (e.g. `/mnt/tank/immich/library`)
|
||||
:::danger Ensure using the correct paths names
|
||||
Make sure to replace `<folder-name>` with the actual name of the folder used by Immich: `library`, `upload`, `thumbs`, `profile`, `encoded-video`, and `backups`. Also, replace `<your-pool-name>` and `<dataset-name>` with the actual names of your pool and dataset.
|
||||
:::
|
||||
- **Read Only**: Keep it unticked as Immich needs to write to these datasets.
|
||||
- Click **Update** at the bottom of the page to save changes.
|
||||
- Go to the **Installed Applications** screen and select Immich from the list of installed applications.
|
||||
- Click **Edit** on the **Application Info** widget.
|
||||
- In the **Storage Configuration** section, untick the **Use Old Storage Configuration (Deprecated)** checkbox.
|
||||
- For the **Data Storage**, you can keep the **ixVolume (dataset created automatically by the system)** as no data will be directly written to it. We recommend selecting **Host Path (Path that already exists on the system)** and then select a **new** dataset you created for Immich data storage, for example, `data`.
|
||||
- For the **Postgres Data Storage**, keep **Host Path (Path that already exists on the system)** and then select the existing dataset you used for Postgres data storage, for example, `pgData`.
|
||||
- Following the instructions in the [Multiple Datasets for Immich Storage](#additional-storage-advanced-users) section, you can add, **for each old dataset**, a new Additional Storage with the following settings:
|
||||
- **Type**: `Host Path (Path that already exists on the system)`
|
||||
- **Mount Path**: `/data/<folder-name>` (e.g. `/data/library`)
|
||||
- **Host Path**: `/mnt/<your-pool-name>/<dataset-name>` (e.g. `/mnt/tank/immich/library`)
|
||||
:::danger Ensure using the correct paths names
|
||||
Make sure to replace `<folder-name>` with the actual name of the folder used by Immich: `library`, `upload`, `thumbs`, `profile`, `encoded-video`, and `backups`. Also, replace `<your-pool-name>` and `<dataset-name>` with the actual names of your pool and dataset.
|
||||
:::
|
||||
- **Read Only**: Keep it unticked as Immich needs to write to these datasets.
|
||||
- Click **Update** at the bottom of the page to save changes.
|
||||
4. **Start the Immich app** from the TrueNAS web interface. This will recreate the Immich container with the new storage configuration and start the app. If everything went well, you should now be able to access Immich with the new storage configuration. You can verify that the data is still available by checking the Immich web interface and ensuring that all your photos and videos are still accessible.
|
||||
|
||||
</TabItem>
|
||||
|
||||
@@ -23,11 +23,6 @@ const projects: CommunityProjectProps[] = [
|
||||
description: 'A Python script to sync folders as albums.',
|
||||
url: 'https://git.orenit.solutions/open/immichalbumpull',
|
||||
},
|
||||
{
|
||||
title: 'Remove offline files',
|
||||
description: 'A simple way to remove orphaned offline assets from the Immich database',
|
||||
url: 'https://github.com/Thoroslives/immich_remove_offline_files',
|
||||
},
|
||||
{
|
||||
title: 'Immich-Tools',
|
||||
description: 'Provides scripts for handling problems on the repair page.',
|
||||
@@ -120,6 +115,11 @@ const projects: CommunityProjectProps[] = [
|
||||
description: 'Auto-stack photos with identical filenames and differing extensions (i.e. JPG+RAW)',
|
||||
url: 'https://github.com/sid3windr/immich-stack',
|
||||
},
|
||||
{
|
||||
title: 'Immich Stack',
|
||||
description: 'Automatically groups similar photos into stacks within the Immich photo management system.',
|
||||
url: 'https://github.com/Majorfi/immich-stack/',
|
||||
},
|
||||
];
|
||||
|
||||
function CommunityProject({ title, description, url }: CommunityProjectProps): JSX.Element {
|
||||
|
||||
BIN
docs/static/img/synology-action-clean.png
vendored
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
docs/static/img/synology-build.png
vendored
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
docs/static/img/synology-container-ip.png
vendored
Normal file
|
After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 153 KiB |
BIN
docs/static/img/synology-custom-port-firewall-rule.png
vendored
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
docs/static/img/synology-fw-ipedit.png
vendored
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
docs/static/img/synology-fw-rules.png
vendored
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
docs/static/img/synology-ipaddress-firewall-rule.png
vendored
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
docs/static/img/synology-project-stop.png
vendored
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
docs/static/img/synology-remove-unused.png
vendored
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
docs/static/img/synology-select-proj.png
vendored
Normal file
|
After Width: | Height: | Size: 26 KiB |
@@ -35,7 +35,7 @@ services:
|
||||
- 2285:2285
|
||||
|
||||
redis:
|
||||
image: redis:6.2-alpine@sha256:7fe72c486b910f6b1a9769c937dad5d63648ddee82e056f47417542dd40825bb
|
||||
image: redis:6.2-alpine@sha256:2185e741f4c1e7b0ea9ca1e163a3767c4270a73086b6bbea2049a7203212fb7f
|
||||
|
||||
database:
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:11ced39d65a92a54d12890ced6a26cc2003f92697d6f0d4d944b98459dba7138
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"@playwright/test": "^1.44.1",
|
||||
"@socket.io/component-emitter": "^3.1.2",
|
||||
"@types/luxon": "^3.4.2",
|
||||
"@types/node": "^22.18.1",
|
||||
"@types/node": "^22.18.8",
|
||||
"@types/oidc-provider": "^9.0.0",
|
||||
"@types/pg": "^8.15.1",
|
||||
"@types/pngjs": "^6.0.4",
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"add_to_albums": "Add to albums",
|
||||
"add_to_albums_count": "Add to albums ({count})",
|
||||
"add_to_shared_album": "Add to shared album",
|
||||
"add_upload_to_stack": "Add upload to stack",
|
||||
"add_url": "Add URL",
|
||||
"added_to_archive": "Added to archive",
|
||||
"added_to_favorites": "Added to favorites",
|
||||
|
||||
@@ -13,8 +13,16 @@ else:
|
||||
|
||||
module_dir = Path(__file__).parent
|
||||
|
||||
|
||||
def is_ipv6(host: str) -> bool:
|
||||
try:
|
||||
return ip_address(host).version == 6
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
|
||||
bind_host = non_prefixed_settings.immich_host
|
||||
if ip_address(bind_host).version == 6:
|
||||
if is_ipv6(bind_host):
|
||||
bind_host = f"[{bind_host}]"
|
||||
bind_address = f"{bind_host}:{non_prefixed_settings.immich_port}"
|
||||
|
||||
|
||||
@@ -7,8 +7,16 @@ import requests
|
||||
port = os.getenv("IMMICH_PORT", 3003)
|
||||
host = os.getenv("IMMICH_HOST", "0.0.0.0")
|
||||
|
||||
|
||||
def is_ipv6(host: str) -> bool:
|
||||
try:
|
||||
return ip_address(host).version == 6
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
|
||||
host = "localhost" if host == "0.0.0.0" else host
|
||||
host = f"[{host}]" if ip_address(host).version == 6 else host
|
||||
host = f"[{host}]" if is_ipv6(host) else host
|
||||
|
||||
try:
|
||||
response = requests.get(f"http://{host}:{port}/ping", timeout=2)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tools]
|
||||
node = "22.20.0"
|
||||
flutter = "3.35.4"
|
||||
flutter = "3.35.5"
|
||||
pnpm = "10.15.1"
|
||||
|
||||
[tools."github:CQLabs/homebrew-dcm"]
|
||||
|
||||
@@ -136,6 +136,7 @@ private open class BackgroundWorkerPigeonCodec : StandardMessageCodec() {
|
||||
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||
interface BackgroundWorkerFgHostApi {
|
||||
fun enable()
|
||||
fun saveNotificationMessage(title: String, body: String)
|
||||
fun configure(settings: BackgroundWorkerSettings)
|
||||
fun disable()
|
||||
|
||||
@@ -164,6 +165,25 @@ interface BackgroundWorkerFgHostApi {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.saveNotificationMessage$separatedMessageChannelSuffix", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val titleArg = args[0] as String
|
||||
val bodyArg = args[1] as String
|
||||
val wrapped: List<Any?> = try {
|
||||
api.saveNotificationMessage(titleArg, bodyArg)
|
||||
listOf(null)
|
||||
} catch (exception: Throwable) {
|
||||
BackgroundWorkerPigeonUtils.wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.configure$separatedMessageChannelSuffix", codec)
|
||||
if (api != null) {
|
||||
@@ -204,7 +224,6 @@ interface BackgroundWorkerFgHostApi {
|
||||
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||
interface BackgroundWorkerBgHostApi {
|
||||
fun onInitialized()
|
||||
fun showNotification(title: String, content: String)
|
||||
fun close()
|
||||
|
||||
companion object {
|
||||
@@ -232,25 +251,6 @@ interface BackgroundWorkerBgHostApi {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.showNotification$separatedMessageChannelSuffix", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val titleArg = args[0] as String
|
||||
val contentArg = args[1] as String
|
||||
val wrapped: List<Any?> = try {
|
||||
api.showNotification(titleArg, contentArg)
|
||||
listOf(null)
|
||||
} catch (exception: Throwable) {
|
||||
BackgroundWorkerPigeonUtils.wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.close$separatedMessageChannelSuffix", codec)
|
||||
if (api != null) {
|
||||
|
||||
@@ -73,6 +73,8 @@ class BackgroundWorker(context: Context, params: WorkerParameters) :
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
)
|
||||
notificationManager.createNotificationChannel(notificationChannel)
|
||||
val notificationConfig = BackgroundWorkerPreferences(ctx).getNotificationConfig()
|
||||
showNotification(notificationConfig.first, notificationConfig.second)
|
||||
|
||||
loader.ensureInitializationCompleteAsync(ctx, null, Handler(Looper.getMainLooper())) {
|
||||
engine = FlutterEngine(ctx)
|
||||
@@ -109,7 +111,7 @@ class BackgroundWorker(context: Context, params: WorkerParameters) :
|
||||
}
|
||||
|
||||
// TODO: Move this to a separate NotificationManager class
|
||||
override fun showNotification(title: String, content: String) {
|
||||
private fun showNotification(title: String, content: String) {
|
||||
val notification = NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.notification_icon)
|
||||
.setOnlyAlertOnce(true)
|
||||
|
||||
@@ -20,6 +20,10 @@ class BackgroundWorkerApiImpl(context: Context) : BackgroundWorkerFgHostApi {
|
||||
enqueueMediaObserver(ctx)
|
||||
}
|
||||
|
||||
override fun saveNotificationMessage(title: String, body: String) {
|
||||
BackgroundWorkerPreferences(ctx).updateNotificationConfig(title, body)
|
||||
}
|
||||
|
||||
override fun configure(settings: BackgroundWorkerSettings) {
|
||||
BackgroundWorkerPreferences(ctx).updateSettings(settings)
|
||||
enqueueMediaObserver(ctx)
|
||||
|
||||
@@ -10,9 +10,13 @@ class BackgroundWorkerPreferences(private val ctx: Context) {
|
||||
private const val SHARED_PREF_MIN_DELAY_KEY = "BackgroundWorker::minDelaySeconds"
|
||||
private const val SHARED_PREF_REQUIRE_CHARGING_KEY = "BackgroundWorker::requireCharging"
|
||||
private const val SHARED_PREF_LOCK_KEY = "BackgroundWorker::isLocked"
|
||||
private const val SHARED_PREF_NOTIF_TITLE_KEY = "BackgroundWorker::notificationTitle"
|
||||
private const val SHARED_PREF_NOTIF_MSG_KEY = "BackgroundWorker::notificationMessage"
|
||||
|
||||
private const val DEFAULT_MIN_DELAY_SECONDS = 30L
|
||||
private const val DEFAULT_REQUIRE_CHARGING = false
|
||||
private const val DEFAULT_NOTIF_TITLE = "Uploading media"
|
||||
private const val DEFAULT_NOTIF_MSG = "Checking for new assets…"
|
||||
}
|
||||
|
||||
private val sp: SharedPreferences by lazy {
|
||||
@@ -38,6 +42,20 @@ class BackgroundWorkerPreferences(private val ctx: Context) {
|
||||
)
|
||||
}
|
||||
|
||||
fun updateNotificationConfig(title: String, message: String) {
|
||||
sp.edit {
|
||||
putString(SHARED_PREF_NOTIF_TITLE_KEY, title)
|
||||
putString(SHARED_PREF_NOTIF_MSG_KEY, message)
|
||||
}
|
||||
}
|
||||
|
||||
fun getNotificationConfig(): Pair<String, String> {
|
||||
val title =
|
||||
sp.getString(SHARED_PREF_NOTIF_TITLE_KEY, DEFAULT_NOTIF_TITLE) ?: DEFAULT_NOTIF_TITLE
|
||||
val message = sp.getString(SHARED_PREF_NOTIF_MSG_KEY, DEFAULT_NOTIF_MSG) ?: DEFAULT_NOTIF_MSG
|
||||
return Pair(title, message)
|
||||
}
|
||||
|
||||
fun setLocked(paused: Boolean) {
|
||||
sp.edit {
|
||||
putBoolean(SHARED_PREF_LOCK_KEY, paused)
|
||||
|
||||
@@ -64,7 +64,7 @@ PODS:
|
||||
- Flutter
|
||||
- integration_test (0.0.1):
|
||||
- Flutter
|
||||
- isar_flutter_libs (1.0.0):
|
||||
- isar_community_flutter_libs (1.0.0):
|
||||
- Flutter
|
||||
- local_auth_darwin (0.0.1):
|
||||
- Flutter
|
||||
@@ -149,7 +149,7 @@ DEPENDENCIES:
|
||||
- home_widget (from `.symlinks/plugins/home_widget/ios`)
|
||||
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||
- integration_test (from `.symlinks/plugins/integration_test/ios`)
|
||||
- isar_flutter_libs (from `.symlinks/plugins/isar_flutter_libs/ios`)
|
||||
- isar_community_flutter_libs (from `.symlinks/plugins/isar_community_flutter_libs/ios`)
|
||||
- local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`)
|
||||
- maplibre_gl (from `.symlinks/plugins/maplibre_gl/ios`)
|
||||
- native_video_player (from `.symlinks/plugins/native_video_player/ios`)
|
||||
@@ -210,8 +210,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/image_picker_ios/ios"
|
||||
integration_test:
|
||||
:path: ".symlinks/plugins/integration_test/ios"
|
||||
isar_flutter_libs:
|
||||
:path: ".symlinks/plugins/isar_flutter_libs/ios"
|
||||
isar_community_flutter_libs:
|
||||
:path: ".symlinks/plugins/isar_community_flutter_libs/ios"
|
||||
local_auth_darwin:
|
||||
:path: ".symlinks/plugins/local_auth_darwin/darwin"
|
||||
maplibre_gl:
|
||||
@@ -264,7 +264,7 @@ SPEC CHECKSUMS:
|
||||
home_widget: f169fc41fd807b4d46ab6615dc44d62adbf9f64f
|
||||
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
|
||||
integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
|
||||
isar_flutter_libs: bc909e72c3d756c2759f14c8776c13b5b0556e26
|
||||
isar_community_flutter_libs: bede843185a61a05ff364a05c9b23209523f7e0d
|
||||
local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391
|
||||
MapLibre: 69e572367f4ef6287e18246cfafc39c80cdcabcd
|
||||
maplibre_gl: 3c924e44725147b03dda33430ad216005b40555f
|
||||
|
||||
@@ -182,6 +182,7 @@ class BackgroundWorkerPigeonCodec: FlutterStandardMessageCodec, @unchecked Senda
|
||||
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
|
||||
protocol BackgroundWorkerFgHostApi {
|
||||
func enable() throws
|
||||
func saveNotificationMessage(title: String, body: String) throws
|
||||
func configure(settings: BackgroundWorkerSettings) throws
|
||||
func disable() throws
|
||||
}
|
||||
@@ -205,6 +206,22 @@ class BackgroundWorkerFgHostApiSetup {
|
||||
} else {
|
||||
enableChannel.setMessageHandler(nil)
|
||||
}
|
||||
let saveNotificationMessageChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.saveNotificationMessage\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
saveNotificationMessageChannel.setMessageHandler { message, reply in
|
||||
let args = message as! [Any?]
|
||||
let titleArg = args[0] as! String
|
||||
let bodyArg = args[1] as! String
|
||||
do {
|
||||
try api.saveNotificationMessage(title: titleArg, body: bodyArg)
|
||||
reply(wrapResult(nil))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
saveNotificationMessageChannel.setMessageHandler(nil)
|
||||
}
|
||||
let configureChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.configure\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
configureChannel.setMessageHandler { message, reply in
|
||||
@@ -238,7 +255,6 @@ class BackgroundWorkerFgHostApiSetup {
|
||||
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
|
||||
protocol BackgroundWorkerBgHostApi {
|
||||
func onInitialized() throws
|
||||
func showNotification(title: String, content: String) throws
|
||||
func close() throws
|
||||
}
|
||||
|
||||
@@ -261,22 +277,6 @@ class BackgroundWorkerBgHostApiSetup {
|
||||
} else {
|
||||
onInitializedChannel.setMessageHandler(nil)
|
||||
}
|
||||
let showNotificationChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.showNotification\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
showNotificationChannel.setMessageHandler { message, reply in
|
||||
let args = message as! [Any?]
|
||||
let titleArg = args[0] as! String
|
||||
let contentArg = args[1] as! String
|
||||
do {
|
||||
try api.showNotification(title: titleArg, content: contentArg)
|
||||
reply(wrapResult(nil))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
showNotificationChannel.setMessageHandler(nil)
|
||||
}
|
||||
let closeChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.close\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
closeChannel.setMessageHandler { _, reply in
|
||||
|
||||
@@ -119,10 +119,6 @@ class BackgroundWorker: BackgroundWorkerBgHostApi {
|
||||
})
|
||||
}
|
||||
|
||||
func showNotification(title: String, content: String) throws {
|
||||
// No-op on iOS for the time being
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the currently running background task, either due to timeout or external request.
|
||||
* Sends a cancel signal to the Flutter side and sets up a fallback timer to ensure
|
||||
|
||||
@@ -12,6 +12,10 @@ class BackgroundWorkerApiImpl: BackgroundWorkerFgHostApi {
|
||||
// Android only
|
||||
}
|
||||
|
||||
func saveNotificationMessage(title: String, body: String) throws {
|
||||
// Android only
|
||||
}
|
||||
|
||||
func disable() throws {
|
||||
BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: BackgroundWorkerApiImpl.refreshTaskID);
|
||||
BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: BackgroundWorkerApiImpl.processingTaskID);
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.0.0</string>
|
||||
<string>2.0.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
||||
@@ -3,27 +3,30 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
|
||||
class SearchResult {
|
||||
final List<BaseAsset> assets;
|
||||
final double scrollOffset;
|
||||
final int? nextPage;
|
||||
|
||||
const SearchResult({required this.assets, this.nextPage});
|
||||
const SearchResult({required this.assets, this.scrollOffset = 0.0, this.nextPage});
|
||||
|
||||
int get totalAssets => assets.length;
|
||||
|
||||
SearchResult copyWith({List<BaseAsset>? assets, int? nextPage}) {
|
||||
return SearchResult(assets: assets ?? this.assets, nextPage: nextPage ?? this.nextPage);
|
||||
SearchResult copyWith({List<BaseAsset>? assets, int? nextPage, double? scrollOffset}) {
|
||||
return SearchResult(
|
||||
assets: assets ?? this.assets,
|
||||
nextPage: nextPage ?? this.nextPage,
|
||||
scrollOffset: scrollOffset ?? this.scrollOffset,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => 'SearchResult(assets: $assets, nextPage: $nextPage)';
|
||||
String toString() => 'SearchResult(assets: ${assets.length}, nextPage: $nextPage, scrollOffset: $scrollOffset)';
|
||||
|
||||
@override
|
||||
bool operator ==(covariant SearchResult other) {
|
||||
if (identical(this, other)) return true;
|
||||
final listEquals = const DeepCollectionEquality().equals;
|
||||
|
||||
return listEquals(other.assets, assets) && other.nextPage == nextPage;
|
||||
return listEquals(other.assets, assets) && other.nextPage == nextPage && other.scrollOffset == scrollOffset;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => assets.hashCode ^ nextPage.hashCode;
|
||||
int get hashCode => assets.hashCode ^ nextPage.hashCode ^ scrollOffset.hashCode;
|
||||
}
|
||||
|
||||
@@ -11,8 +11,6 @@ import 'package:immich_mobile/domain/services/log.service.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/extensions/network_capability_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/generated/intl_keys.g.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart';
|
||||
import 'package:immich_mobile/platform/background_worker_api.g.dart';
|
||||
@@ -44,6 +42,9 @@ class BackgroundWorkerFgService {
|
||||
// TODO: Move this call to native side once old timeline is removed
|
||||
Future<void> enable() => _foregroundHostApi.enable();
|
||||
|
||||
Future<void> saveNotificationMessage(String title, String body) =>
|
||||
_foregroundHostApi.saveNotificationMessage(title, body);
|
||||
|
||||
Future<void> configure({int? minimumDelaySeconds, bool? requireCharging}) => _foregroundHostApi.configure(
|
||||
BackgroundWorkerSettings(
|
||||
minimumDelaySeconds:
|
||||
@@ -112,13 +113,6 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi {
|
||||
|
||||
configureFileDownloaderNotifications();
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
await _backgroundHostApi.showNotification(
|
||||
IntlKeys.uploading_media.t(),
|
||||
IntlKeys.backup_background_service_default_notification.t(),
|
||||
);
|
||||
}
|
||||
|
||||
// Notify the host that the background worker service has been initialized and is ready to use
|
||||
_backgroundHostApi.onInitialized();
|
||||
} catch (error, stack) {
|
||||
|
||||
@@ -203,7 +203,7 @@ class TimelineService {
|
||||
Future<void> dispose() async {
|
||||
await _bucketSubscription?.cancel();
|
||||
_bucketSubscription = null;
|
||||
_buffer.clear();
|
||||
_buffer = [];
|
||||
_bufferOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
2
mobile/lib/entities/album.entity.g.dart
generated
@@ -132,7 +132,7 @@ const AlbumSchema = CollectionSchema(
|
||||
getId: _albumGetId,
|
||||
getLinks: _albumGetLinks,
|
||||
attach: _albumAttach,
|
||||
version: '3.1.8',
|
||||
version: '3.3.0-dev.3',
|
||||
);
|
||||
|
||||
int _albumEstimateSize(
|
||||
|
||||
@@ -47,7 +47,7 @@ const AndroidDeviceAssetSchema = CollectionSchema(
|
||||
getId: _androidDeviceAssetGetId,
|
||||
getLinks: _androidDeviceAssetGetLinks,
|
||||
attach: _androidDeviceAssetAttach,
|
||||
version: '3.1.8',
|
||||
version: '3.3.0-dev.3',
|
||||
);
|
||||
|
||||
int _androidDeviceAssetEstimateSize(
|
||||
|
||||
2
mobile/lib/entities/asset.entity.g.dart
generated
@@ -168,7 +168,7 @@ const AssetSchema = CollectionSchema(
|
||||
getId: _assetGetId,
|
||||
getLinks: _assetGetLinks,
|
||||
attach: _assetAttach,
|
||||
version: '3.1.8',
|
||||
version: '3.3.0-dev.3',
|
||||
);
|
||||
|
||||
int _assetEstimateSize(
|
||||
|
||||
2
mobile/lib/entities/backup_album.entity.g.dart
generated
@@ -43,7 +43,7 @@ const BackupAlbumSchema = CollectionSchema(
|
||||
getId: _backupAlbumGetId,
|
||||
getLinks: _backupAlbumGetLinks,
|
||||
attach: _backupAlbumAttach,
|
||||
version: '3.1.8',
|
||||
version: '3.3.0-dev.3',
|
||||
);
|
||||
|
||||
int _backupAlbumEstimateSize(
|
||||
|
||||
@@ -32,7 +32,7 @@ const DuplicatedAssetSchema = CollectionSchema(
|
||||
getId: _duplicatedAssetGetId,
|
||||
getLinks: _duplicatedAssetGetLinks,
|
||||
attach: _duplicatedAssetAttach,
|
||||
version: '3.1.8',
|
||||
version: '3.3.0-dev.3',
|
||||
);
|
||||
|
||||
int _duplicatedAssetEstimateSize(
|
||||
|
||||
2
mobile/lib/entities/etag.entity.g.dart
generated
@@ -52,7 +52,7 @@ const ETagSchema = CollectionSchema(
|
||||
getId: _eTagGetId,
|
||||
getLinks: _eTagGetLinks,
|
||||
attach: _eTagAttach,
|
||||
version: '3.1.8',
|
||||
version: '3.3.0-dev.3',
|
||||
);
|
||||
|
||||
int _eTagEstimateSize(
|
||||
|
||||
@@ -60,7 +60,7 @@ const IOSDeviceAssetSchema = CollectionSchema(
|
||||
getId: _iOSDeviceAssetGetId,
|
||||
getLinks: _iOSDeviceAssetGetLinks,
|
||||
attach: _iOSDeviceAssetAttach,
|
||||
version: '3.1.8',
|
||||
version: '3.3.0-dev.3',
|
||||
);
|
||||
|
||||
int _iOSDeviceAssetEstimateSize(
|
||||
|
||||
@@ -65,7 +65,7 @@ const DeviceAssetEntitySchema = CollectionSchema(
|
||||
getId: _deviceAssetEntityGetId,
|
||||
getLinks: _deviceAssetEntityGetLinks,
|
||||
attach: _deviceAssetEntityAttach,
|
||||
version: '3.1.8',
|
||||
version: '3.3.0-dev.3',
|
||||
);
|
||||
|
||||
int _deviceAssetEntityEstimateSize(
|
||||
|
||||
@@ -68,7 +68,7 @@ const ExifInfoSchema = CollectionSchema(
|
||||
getId: _exifInfoGetId,
|
||||
getLinks: _exifInfoGetLinks,
|
||||
attach: _exifInfoAttach,
|
||||
version: '3.1.8',
|
||||
version: '3.3.0-dev.3',
|
||||
);
|
||||
|
||||
int _exifInfoEstimateSize(
|
||||
|
||||
@@ -37,7 +37,7 @@ const StoreValueSchema = CollectionSchema(
|
||||
getId: _storeValueGetId,
|
||||
getLinks: _storeValueGetLinks,
|
||||
attach: _storeValueAttach,
|
||||
version: '3.1.8',
|
||||
version: '3.3.0-dev.3',
|
||||
);
|
||||
|
||||
int _storeValueEstimateSize(
|
||||
|
||||
@@ -95,7 +95,7 @@ const UserSchema = CollectionSchema(
|
||||
getId: _userGetId,
|
||||
getLinks: _userGetLinks,
|
||||
attach: _userAttach,
|
||||
version: '3.1.8',
|
||||
version: '3.3.0-dev.3',
|
||||
);
|
||||
|
||||
int _userEstimateSize(
|
||||
|
||||
@@ -15,7 +15,9 @@ import 'package:immich_mobile/constants/locales.dart';
|
||||
import 'package:immich_mobile/domain/services/background_worker.service.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/generated/codegen_loader.g.dart';
|
||||
import 'package:immich_mobile/generated/intl_keys.g.dart';
|
||||
import 'package:immich_mobile/platform/background_worker_lock_api.g.dart';
|
||||
import 'package:immich_mobile/providers/app_life_cycle.provider.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/share_intent_upload.provider.dart';
|
||||
@@ -210,6 +212,14 @@ class ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserve
|
||||
if (Store.isBetaTimelineEnabled) {
|
||||
ref.read(backgroundServiceProvider).disableService();
|
||||
ref.read(backgroundWorkerFgServiceProvider).enable();
|
||||
if (Platform.isAndroid) {
|
||||
ref
|
||||
.read(backgroundWorkerFgServiceProvider)
|
||||
.saveNotificationMessage(
|
||||
IntlKeys.uploading_media.t(),
|
||||
IntlKeys.backup_background_service_default_notification.t(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
ref.read(backgroundWorkerFgServiceProvider).disable();
|
||||
ref.read(backgroundServiceProvider).resumeServiceIfEnabled();
|
||||
|
||||
46
mobile/lib/platform/background_worker_api.g.dart
generated
@@ -138,6 +138,29 @@ class BackgroundWorkerFgHostApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> saveNotificationMessage(String title, String body) async {
|
||||
final String pigeonVar_channelName =
|
||||
'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.saveNotificationMessage$pigeonVar_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||
pigeonVar_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: pigeonVar_binaryMessenger,
|
||||
);
|
||||
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[title, body]);
|
||||
final List<Object?>? pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
|
||||
if (pigeonVar_replyList == null) {
|
||||
throw _createConnectionError(pigeonVar_channelName);
|
||||
} else if (pigeonVar_replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: pigeonVar_replyList[0]! as String,
|
||||
message: pigeonVar_replyList[1] as String?,
|
||||
details: pigeonVar_replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> configure(BackgroundWorkerSettings settings) async {
|
||||
final String pigeonVar_channelName =
|
||||
'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.configure$pigeonVar_messageChannelSuffix';
|
||||
@@ -221,29 +244,6 @@ class BackgroundWorkerBgHostApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> showNotification(String title, String content) async {
|
||||
final String pigeonVar_channelName =
|
||||
'dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.showNotification$pigeonVar_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||
pigeonVar_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: pigeonVar_binaryMessenger,
|
||||
);
|
||||
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[title, content]);
|
||||
final List<Object?>? pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
|
||||
if (pigeonVar_replyList == null) {
|
||||
throw _createConnectionError(pigeonVar_channelName);
|
||||
} else if (pigeonVar_replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: pigeonVar_replyList[0]! as String,
|
||||
message: pigeonVar_replyList[1] as String?,
|
||||
details: pigeonVar_replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> close() async {
|
||||
final String pigeonVar_channelName =
|
||||
'dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.close$pigeonVar_messageChannelSuffix';
|
||||
|
||||
@@ -50,6 +50,11 @@ class DriftEditImagePage extends ConsumerWidget {
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
void _exitEditing(BuildContext context) {
|
||||
// this assumes that the only way to get to this page is from the AssetViewerRoute
|
||||
context.navigator.popUntil((route) => route.data?.name == AssetViewerRoute.name);
|
||||
}
|
||||
|
||||
Future<void> _saveEditedImage(BuildContext context, BaseAsset asset, Image image, WidgetRef ref) async {
|
||||
try {
|
||||
final Uint8List imageData = await _imageToUint8List(image);
|
||||
@@ -66,7 +71,7 @@ class DriftEditImagePage extends ConsumerWidget {
|
||||
}
|
||||
|
||||
ref.read(backgroundSyncProvider).syncLocal(full: true);
|
||||
context.navigator.popUntil((route) => route.isFirst);
|
||||
_exitEditing(context);
|
||||
ImmichToast.show(durationInSecond: 3, context: context, msg: 'Image Saved!');
|
||||
|
||||
if (localAsset == null) {
|
||||
@@ -91,7 +96,7 @@ class DriftEditImagePage extends ConsumerWidget {
|
||||
backgroundColor: context.scaffoldBackgroundColor,
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.close_rounded, color: context.primaryColor, size: 24),
|
||||
onPressed: () => context.navigator.popUntil((route) => route.isFirst),
|
||||
onPressed: () => _exitEditing(context),
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
|
||||
@@ -599,9 +599,9 @@ class _SearchResultGrid extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final searchResult = ref.watch(paginatedSearchProvider);
|
||||
final assets = ref.watch(paginatedSearchProvider.select((s) => s.assets));
|
||||
|
||||
if (searchResult.totalAssets == 0) {
|
||||
if (assets.isEmpty) {
|
||||
return const _SearchEmptyContent();
|
||||
}
|
||||
|
||||
@@ -615,6 +615,7 @@ class _SearchResultGrid extends ConsumerWidget {
|
||||
|
||||
if (metrics.pixels >= metrics.maxScrollExtent && isVerticalScroll && !isBottomSheetNotification) {
|
||||
onScrollEnd();
|
||||
ref.read(paginatedSearchProvider.notifier).setScrollOffset(metrics.maxScrollExtent);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -623,17 +624,18 @@ class _SearchResultGrid extends ConsumerWidget {
|
||||
child: ProviderScope(
|
||||
overrides: [
|
||||
timelineServiceProvider.overrideWith((ref) {
|
||||
final timelineService = ref.watch(timelineFactoryProvider).fromAssets(searchResult.assets);
|
||||
final timelineService = ref.watch(timelineFactoryProvider).fromAssets(assets);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
}),
|
||||
],
|
||||
child: Timeline(
|
||||
key: ValueKey(searchResult.totalAssets),
|
||||
key: ValueKey(assets.length),
|
||||
groupBy: GroupAssetsBy.none,
|
||||
appBar: null,
|
||||
bottomSheet: const GeneralBottomSheet(minChildSize: 0.20),
|
||||
snapToMonth: false,
|
||||
initialScrollOffset: ref.read(paginatedSearchProvider.select((s) => s.scrollOffset)),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -24,12 +24,20 @@ class PaginatedSearchNotifier extends StateNotifier<SearchResult> {
|
||||
return false;
|
||||
}
|
||||
|
||||
state = SearchResult(assets: [...state.assets, ...result.assets], nextPage: result.nextPage);
|
||||
state = SearchResult(
|
||||
assets: [...state.assets, ...result.assets],
|
||||
nextPage: result.nextPage,
|
||||
scrollOffset: state.scrollOffset,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void setScrollOffset(double offset) {
|
||||
state = state.copyWith(scrollOffset: offset);
|
||||
}
|
||||
|
||||
clear() {
|
||||
state = const SearchResult(assets: [], nextPage: 1);
|
||||
state = const SearchResult(assets: [], nextPage: 1, scrollOffset: 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/presentation/pages/editing/drift_edit.page.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
|
||||
class EditImageActionButton extends ConsumerWidget {
|
||||
const EditImageActionButton({super.key});
|
||||
@@ -20,12 +20,7 @@ class EditImageActionButton extends ConsumerWidget {
|
||||
}
|
||||
|
||||
final image = Image(image: getFullImageProvider(currentAsset));
|
||||
|
||||
context.navigator.push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => DriftEditImagePage(asset: currentAsset, image: image, isEdited: false),
|
||||
),
|
||||
);
|
||||
context.pushRoute(DriftEditImageRoute(asset: currentAsset, image: image, isEdited: false));
|
||||
}
|
||||
|
||||
return BaseActionButton(
|
||||
|
||||
@@ -51,7 +51,7 @@ class AssetDetailBottomSheet extends ConsumerWidget {
|
||||
isArchived: isArchived,
|
||||
isTrashEnabled: isTrashEnable,
|
||||
isInLockedView: isInLockedView,
|
||||
isStacked: asset.hasRemote && (asset as RemoteAsset).stackId != null,
|
||||
isStacked: asset is RemoteAsset && asset.stackId != null,
|
||||
currentAlbum: currentAlbum,
|
||||
advancedTroubleshooting: advancedTroubleshooting,
|
||||
source: ActionSource.viewer,
|
||||
|
||||
@@ -44,7 +44,8 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
||||
final showViewInTimelineButton =
|
||||
(previousRouteName != TabShellRoute.name || tabRoute == TabEnum.search) &&
|
||||
previousRouteName != AssetViewerRoute.name &&
|
||||
previousRouteName != null;
|
||||
previousRouteName != null &&
|
||||
previousRouteName != LocalTimelineRoute.name;
|
||||
|
||||
final isShowingSheet = ref.watch(assetViewerProvider.select((state) => state.showingBottomSheet));
|
||||
int opacity = ref.watch(assetViewerProvider.select((state) => state.backgroundOpacity));
|
||||
|
||||
@@ -322,6 +322,9 @@ class NativeVideoViewer extends HookConsumerWidget {
|
||||
removeListeners(playerController);
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
isVisible.value = _isCurrentAsset(value, asset);
|
||||
}
|
||||
final curAsset = currentAsset.value;
|
||||
if (curAsset == asset) {
|
||||
return;
|
||||
|
||||
@@ -40,6 +40,7 @@ class Timeline extends StatelessWidget {
|
||||
this.groupBy,
|
||||
this.withScrubber = true,
|
||||
this.snapToMonth = true,
|
||||
this.initialScrollOffset,
|
||||
});
|
||||
|
||||
final Widget? topSliverWidget;
|
||||
@@ -51,6 +52,7 @@ class Timeline extends StatelessWidget {
|
||||
final GroupAssetsBy? groupBy;
|
||||
final bool withScrubber;
|
||||
final bool snapToMonth;
|
||||
final double? initialScrollOffset;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -78,6 +80,7 @@ class Timeline extends StatelessWidget {
|
||||
bottomSheet: bottomSheet,
|
||||
withScrubber: withScrubber,
|
||||
snapToMonth: snapToMonth,
|
||||
initialScrollOffset: initialScrollOffset,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -93,6 +96,7 @@ class _SliverTimeline extends ConsumerStatefulWidget {
|
||||
this.bottomSheet,
|
||||
this.withScrubber = true,
|
||||
this.snapToMonth = true,
|
||||
this.initialScrollOffset,
|
||||
});
|
||||
|
||||
final Widget? topSliverWidget;
|
||||
@@ -101,6 +105,7 @@ class _SliverTimeline extends ConsumerStatefulWidget {
|
||||
final Widget? bottomSheet;
|
||||
final bool withScrubber;
|
||||
final bool snapToMonth;
|
||||
final double? initialScrollOffset;
|
||||
|
||||
@override
|
||||
ConsumerState createState() => _SliverTimelineState();
|
||||
@@ -124,7 +129,10 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_scrollController = ScrollController(onAttach: _restoreScalePosition);
|
||||
_scrollController = ScrollController(
|
||||
initialScrollOffset: widget.initialScrollOffset ?? 0.0,
|
||||
onAttach: _restoreScalePosition,
|
||||
);
|
||||
_eventSubscription = EventStream.shared.listen(_onEvent);
|
||||
|
||||
final currentTilesPerRow = ref.read(settingsProvider).get(Setting.tilesPerRow);
|
||||
|
||||
@@ -77,11 +77,14 @@ class ActionNotifier extends Notifier<void> {
|
||||
return _getAssets(source).whereType<RemoteAsset>().toIds().toList(growable: false);
|
||||
}
|
||||
|
||||
List<String> _getLocalIdsForSource(ActionSource source) {
|
||||
List<String> _getLocalIdsForSource(ActionSource source, {bool ignoreLocalOnly = false}) {
|
||||
final Set<BaseAsset> assets = _getAssets(source);
|
||||
final List<String> localIds = [];
|
||||
|
||||
for (final asset in assets) {
|
||||
if (ignoreLocalOnly && asset.storage != AssetState.merged) {
|
||||
continue;
|
||||
}
|
||||
if (asset is LocalAsset) {
|
||||
localIds.add(asset.id);
|
||||
} else if (asset is RemoteAsset && asset.localId != null) {
|
||||
@@ -189,7 +192,7 @@ class ActionNotifier extends Notifier<void> {
|
||||
|
||||
Future<ActionResult> moveToLockFolder(ActionSource source) async {
|
||||
final ids = _getOwnedRemoteIdsForSource(source);
|
||||
final localIds = _getLocalIdsForSource(source);
|
||||
final localIds = _getLocalIdsForSource(source, ignoreLocalOnly: true);
|
||||
try {
|
||||
await _service.moveToLockFolder(ids, localIds);
|
||||
return ActionResult(count: ids.length, success: true);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
@@ -25,7 +26,28 @@ class AssetMediaRepository {
|
||||
|
||||
const AssetMediaRepository(this._assetApiRepository);
|
||||
|
||||
Future<List<String>> deleteAll(List<String> ids) => PhotoManager.editor.deleteWithIds(ids);
|
||||
Future<bool> _androidSupportsTrash() async {
|
||||
if (Platform.isAndroid) {
|
||||
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
||||
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
||||
int sdkVersion = androidInfo.version.sdkInt;
|
||||
return sdkVersion >= 31;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<List<String>> deleteAll(List<String> ids) async {
|
||||
if (CurrentPlatform.isAndroid) {
|
||||
if (await _androidSupportsTrash()) {
|
||||
return PhotoManager.editor.android.moveToTrash(
|
||||
ids.map((e) => AssetEntity(id: e, width: 1, height: 1, typeInt: 0)).toList(),
|
||||
);
|
||||
} else {
|
||||
return PhotoManager.editor.deleteWithIds(ids);
|
||||
}
|
||||
}
|
||||
return PhotoManager.editor.deleteWithIds(ids);
|
||||
}
|
||||
|
||||
Future<asset_entity.Asset?> get(String id) async {
|
||||
final entity = await AssetEntity.fromId(id);
|
||||
|
||||
@@ -121,7 +121,7 @@ class DownloadRepository {
|
||||
_dummyMetadata['part'] = LivePhotosPart.video.index;
|
||||
tasks[taskIndex++] = DownloadTask(
|
||||
taskId: livePhotoVideoId,
|
||||
url: url,
|
||||
url: getOriginalUrlForRemoteId(livePhotoVideoId),
|
||||
headers: headers,
|
||||
filename: asset.name.toUpperCase().replaceAll(RegExp(r"\.(JPG|HEIC)$"), '.MOV'),
|
||||
updates: Updates.statusAndProgress,
|
||||
|
||||
@@ -22,6 +22,8 @@ class BackgroundWorkerSettings {
|
||||
abstract class BackgroundWorkerFgHostApi {
|
||||
void enable();
|
||||
|
||||
void saveNotificationMessage(String title, String body);
|
||||
|
||||
void configure(BackgroundWorkerSettings settings);
|
||||
|
||||
void disable();
|
||||
@@ -33,8 +35,6 @@ abstract class BackgroundWorkerBgHostApi {
|
||||
// required platform channels to notify the native side to start the background upload
|
||||
void onInitialized();
|
||||
|
||||
void showNotification(String title, String content);
|
||||
|
||||
// Called from the background flutter engine to request the native side to cleanup
|
||||
void close();
|
||||
}
|
||||
|
||||
@@ -317,10 +317,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: connectivity_plus
|
||||
sha256: "04bf81bb0b77de31557b58d052b24b3eee33f09a6e7a8c68a3e247c7df19ec27"
|
||||
sha256: b5e72753cf63becce2c61fd04dfe0f1c430cc5278b53a1342dc5ad839eab29ec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.3"
|
||||
version: "6.1.5"
|
||||
connectivity_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -437,18 +437,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: device_info_plus
|
||||
sha256: "306b78788d1bb569edb7c55d622953c2414ca12445b41c9117963e03afc5c513"
|
||||
sha256: "49413c8ca514dea7633e8def233b25efdf83ec8522955cc2c0e3ad802927e7c6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.3.3"
|
||||
version: "12.1.0"
|
||||
device_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: device_info_plus_platform_interface
|
||||
sha256: "0b04e02b30791224b31969eb1b50d723498f402971bff3630bca2ba839bd1ed2"
|
||||
sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.2"
|
||||
version: "7.0.3"
|
||||
drift:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1022,25 +1022,34 @@ packages:
|
||||
isar:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: isar
|
||||
sha256: e17a9555bc7f22ff26568b8c64d019b4ffa2dc6bd4cb1c8d9b269aefd32e53ad
|
||||
url: "https://pub.isar-community.dev"
|
||||
source: hosted
|
||||
path: "packages/isar"
|
||||
ref: bb1dca40fe87a001122e5d43bc6254718cb49f3a
|
||||
resolved-ref: bb1dca40fe87a001122e5d43bc6254718cb49f3a
|
||||
url: "https://github.com/immich-app/isar"
|
||||
source: git
|
||||
version: "3.1.8"
|
||||
isar_flutter_libs:
|
||||
isar_community:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: isar_community
|
||||
sha256: "28f59e54636c45ba0bb1b3b7f2656f1c50325f740cea6efcd101900be3fba546"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.0-dev.3"
|
||||
isar_community_flutter_libs:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: isar_flutter_libs
|
||||
sha256: "78710781e658ce4bff59b3f38c5b2735e899e627f4e926e1221934e77b95231a"
|
||||
url: "https://pub.isar-community.dev"
|
||||
name: isar_community_flutter_libs
|
||||
sha256: c2934fe755bb3181cb67602fd5df0d080b3d3eb52799f98623aa4fc5acbea010
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.8"
|
||||
version: "3.3.0-dev.3"
|
||||
isar_generator:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
path: "packages/isar_generator"
|
||||
ref: v3
|
||||
resolved-ref: ad574f60ed6f39d2995cd16fc7dc3de9a646ef30
|
||||
ref: bb1dca40fe87a001122e5d43bc6254718cb49f3a
|
||||
resolved-ref: bb1dca40fe87a001122e5d43bc6254718cb49f3a
|
||||
url: "https://github.com/immich-app/isar"
|
||||
source: git
|
||||
version: "3.1.8"
|
||||
|
||||
@@ -8,8 +8,6 @@ environment:
|
||||
sdk: '>=3.8.0 <4.0.0'
|
||||
flutter: 3.35.4
|
||||
|
||||
isar_version: &isar_version 3.1.8
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
@@ -24,7 +22,7 @@ dependencies:
|
||||
connectivity_plus: ^6.1.3
|
||||
crop_image: ^1.0.16
|
||||
crypto: ^3.0.6
|
||||
device_info_plus: ^11.3.3
|
||||
device_info_plus: ^12.0.0
|
||||
dynamic_color: ^1.7.0
|
||||
easy_image_viewer: ^1.5.1
|
||||
easy_localization: ^3.0.7+1
|
||||
@@ -81,11 +79,11 @@ dependencies:
|
||||
openapi:
|
||||
path: openapi
|
||||
isar:
|
||||
version: *isar_version
|
||||
hosted: https://pub.isar-community.dev/
|
||||
isar_flutter_libs: # contains Isar Core
|
||||
version: *isar_version
|
||||
hosted: https://pub.isar-community.dev/
|
||||
git:
|
||||
url: https://github.com/immich-app/isar
|
||||
ref: 'bb1dca40fe87a001122e5d43bc6254718cb49f3a'
|
||||
path: packages/isar/
|
||||
isar_community_flutter_libs: 3.3.0-dev.3
|
||||
# DB
|
||||
drift: ^2.23.1
|
||||
drift_flutter: ^0.2.4
|
||||
@@ -101,7 +99,7 @@ dev_dependencies:
|
||||
isar_generator:
|
||||
git:
|
||||
url: https://github.com/immich-app/isar
|
||||
ref: v3
|
||||
ref: 'bb1dca40fe87a001122e5d43bc6254718cb49f3a'
|
||||
path: packages/isar_generator/
|
||||
integration_test:
|
||||
sdk: flutter
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"@oazapfts/runtime": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.18.1",
|
||||
"@types/node": "^22.18.8",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"repository": {
|
||||
|
||||
340
pnpm-lock.yaml
generated
@@ -63,11 +63,11 @@ importers:
|
||||
specifier: ^4.13.1
|
||||
version: 4.13.4
|
||||
'@types/node':
|
||||
specifier: ^22.18.1
|
||||
version: 22.18.5
|
||||
specifier: ^22.18.8
|
||||
version: 22.18.8
|
||||
'@vitest/coverage-v8':
|
||||
specifier: ^3.0.0
|
||||
version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))
|
||||
version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))
|
||||
byte-size:
|
||||
specifier: ^9.0.0
|
||||
version: 9.0.1
|
||||
@@ -109,16 +109,16 @@ importers:
|
||||
version: 8.45.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2)
|
||||
vite:
|
||||
specifier: ^7.0.0
|
||||
version: 7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
version: 7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
vite-tsconfig-paths:
|
||||
specifier: ^5.0.0
|
||||
version: 5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))
|
||||
version: 5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))
|
||||
vitest:
|
||||
specifier: ^3.0.0
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
vitest-fetch-mock:
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))
|
||||
version: 0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))
|
||||
yaml:
|
||||
specifier: ^2.3.1
|
||||
version: 2.8.1
|
||||
@@ -211,8 +211,8 @@ importers:
|
||||
specifier: ^3.4.2
|
||||
version: 3.7.1
|
||||
'@types/node':
|
||||
specifier: ^22.18.1
|
||||
version: 22.18.5
|
||||
specifier: ^22.18.8
|
||||
version: 22.18.8
|
||||
'@types/oidc-provider':
|
||||
specifier: ^9.0.0
|
||||
version: 9.5.0
|
||||
@@ -284,7 +284,7 @@ importers:
|
||||
version: 5.2.1(encoding@0.1.13)
|
||||
vitest:
|
||||
specifier: ^3.0.0
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
|
||||
open-api/typescript-sdk:
|
||||
dependencies:
|
||||
@@ -293,8 +293,8 @@ importers:
|
||||
version: 1.0.4
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^22.18.1
|
||||
version: 22.18.5
|
||||
specifier: ^22.18.8
|
||||
version: 22.18.8
|
||||
typescript:
|
||||
specifier: ^5.3.3
|
||||
version: 5.9.2
|
||||
@@ -450,7 +450,7 @@ importers:
|
||||
version: 2.0.2
|
||||
nest-commander:
|
||||
specifier: ^3.16.0
|
||||
version: 3.19.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@types/inquirer@8.2.11)(@types/node@22.18.5)(typescript@5.9.2)
|
||||
version: 3.19.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@types/inquirer@8.2.11)(@types/node@22.18.8)(typescript@5.9.2)
|
||||
nestjs-cls:
|
||||
specifier: ^5.0.0
|
||||
version: 5.4.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
@@ -462,7 +462,7 @@ importers:
|
||||
version: 7.0.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)
|
||||
nodemailer:
|
||||
specifier: ^7.0.0
|
||||
version: 7.0.6
|
||||
version: 7.0.7
|
||||
openid-client:
|
||||
specifier: ^6.3.3
|
||||
version: 6.8.0
|
||||
@@ -532,7 +532,7 @@ importers:
|
||||
version: 9.36.0
|
||||
'@nestjs/cli':
|
||||
specifier: ^11.0.2
|
||||
version: 11.0.10(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.18.5)
|
||||
version: 11.0.10(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.18.8)
|
||||
'@nestjs/schematics':
|
||||
specifier: ^11.0.0
|
||||
version: 11.0.7(chokidar@4.0.3)(typescript@5.9.2)
|
||||
@@ -582,8 +582,8 @@ importers:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
'@types/node':
|
||||
specifier: ^22.18.1
|
||||
version: 22.18.5
|
||||
specifier: ^22.18.8
|
||||
version: 22.18.8
|
||||
'@types/nodemailer':
|
||||
specifier: ^7.0.0
|
||||
version: 7.0.1
|
||||
@@ -613,7 +613,7 @@ importers:
|
||||
version: 13.15.3
|
||||
'@vitest/coverage-v8':
|
||||
specifier: ^3.0.0
|
||||
version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))
|
||||
version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))
|
||||
eslint:
|
||||
specifier: ^9.14.0
|
||||
version: 9.36.0(jiti@2.5.1)
|
||||
@@ -667,10 +667,10 @@ importers:
|
||||
version: 1.5.7(@swc/core@1.13.5(@swc/helpers@0.5.17))(rollup@4.50.1)
|
||||
vite-tsconfig-paths:
|
||||
specifier: ^5.0.0
|
||||
version: 5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))
|
||||
version: 5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))
|
||||
vitest:
|
||||
specifier: ^3.0.0
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
|
||||
web:
|
||||
dependencies:
|
||||
@@ -4519,8 +4519,8 @@ packages:
|
||||
'@types/node@20.19.2':
|
||||
resolution: {integrity: sha512-9pLGGwdzOUBDYi0GNjM97FIA+f92fqSke6joWeBjWXllfNxZBs7qeMF7tvtOIsbY45xkWkxrdwUfUf3MnQa9gA==}
|
||||
|
||||
'@types/node@22.18.5':
|
||||
resolution: {integrity: sha512-g9BpPfJvxYBXUWI9bV37j6d6LTMNQ88hPwdWWUeYZnMhlo66FIg9gCc1/DZb15QylJSKwOZjwrckvOTWpOiChg==}
|
||||
'@types/node@22.18.8':
|
||||
resolution: {integrity: sha512-pAZSHMiagDR7cARo/cch1f3rXy0AEXwsVsVH09FcyeJVAzCnGgmYis7P3JidtTUjyadhTeSo8TgRPswstghDaw==}
|
||||
|
||||
'@types/node@24.5.1':
|
||||
resolution: {integrity: sha512-/SQdmUP2xa+1rdx7VwB9yPq8PaKej8TD5cQ+XfKDPWWC+VDJU4rvVVagXqKUzhKjtFoNA8rXDJAkCxQPAe00+Q==}
|
||||
@@ -8398,8 +8398,8 @@ packages:
|
||||
node-releases@2.0.19:
|
||||
resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
|
||||
|
||||
nodemailer@7.0.6:
|
||||
resolution: {integrity: sha512-F44uVzgwo49xboqbFgBGkRaiMgtoBrBEWCVincJPK9+S9Adkzt/wXCLKbf7dxucmxfTI5gHGB+bEmdyzN6QKjw==}
|
||||
nodemailer@7.0.7:
|
||||
resolution: {integrity: sha512-jGOaRznodf62TVzdyhKt/f1Q/c3kYynk8629sgJHpRzGZj01ezbgMMWJSAjHADcwTKxco3B68/R+KHJY2T5BaA==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
nopt@1.0.10:
|
||||
@@ -11423,11 +11423,11 @@ snapshots:
|
||||
optionalDependencies:
|
||||
chokidar: 4.0.3
|
||||
|
||||
'@angular-devkit/schematics-cli@19.2.15(@types/node@22.18.5)(chokidar@4.0.3)':
|
||||
'@angular-devkit/schematics-cli@19.2.15(@types/node@22.18.8)(chokidar@4.0.3)':
|
||||
dependencies:
|
||||
'@angular-devkit/core': 19.2.15(chokidar@4.0.3)
|
||||
'@angular-devkit/schematics': 19.2.15(chokidar@4.0.3)
|
||||
'@inquirer/prompts': 7.3.2(@types/node@22.18.5)
|
||||
'@inquirer/prompts': 7.3.2(@types/node@22.18.8)
|
||||
ansi-colors: 4.1.3
|
||||
symbol-observable: 4.0.0
|
||||
yargs-parser: 21.1.1
|
||||
@@ -14019,27 +14019,27 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- '@internationalized/date'
|
||||
|
||||
'@inquirer/checkbox@4.2.1(@types/node@22.18.5)':
|
||||
'@inquirer/checkbox@4.2.1(@types/node@22.18.8)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.15(@types/node@22.18.5)
|
||||
'@inquirer/core': 10.1.15(@types/node@22.18.8)
|
||||
'@inquirer/figures': 1.0.13
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.5)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.8)
|
||||
ansi-escapes: 4.3.2
|
||||
yoctocolors-cjs: 2.1.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@inquirer/confirm@5.1.15(@types/node@22.18.5)':
|
||||
'@inquirer/confirm@5.1.15(@types/node@22.18.8)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.15(@types/node@22.18.5)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.5)
|
||||
'@inquirer/core': 10.1.15(@types/node@22.18.8)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.8)
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@inquirer/core@10.1.15(@types/node@22.18.5)':
|
||||
'@inquirer/core@10.1.15(@types/node@22.18.8)':
|
||||
dependencies:
|
||||
'@inquirer/figures': 1.0.13
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.5)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.8)
|
||||
ansi-escapes: 4.3.2
|
||||
cli-width: 4.1.0
|
||||
mute-stream: 2.0.0
|
||||
@@ -14047,115 +14047,115 @@ snapshots:
|
||||
wrap-ansi: 6.2.0
|
||||
yoctocolors-cjs: 2.1.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@inquirer/editor@4.2.17(@types/node@22.18.5)':
|
||||
'@inquirer/editor@4.2.17(@types/node@22.18.8)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.15(@types/node@22.18.5)
|
||||
'@inquirer/external-editor': 1.0.2(@types/node@22.18.5)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.5)
|
||||
'@inquirer/core': 10.1.15(@types/node@22.18.8)
|
||||
'@inquirer/external-editor': 1.0.2(@types/node@22.18.8)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.8)
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@inquirer/expand@4.0.17(@types/node@22.18.5)':
|
||||
'@inquirer/expand@4.0.17(@types/node@22.18.8)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.15(@types/node@22.18.5)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.5)
|
||||
'@inquirer/core': 10.1.15(@types/node@22.18.8)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.8)
|
||||
yoctocolors-cjs: 2.1.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@inquirer/external-editor@1.0.2(@types/node@22.18.5)':
|
||||
'@inquirer/external-editor@1.0.2(@types/node@22.18.8)':
|
||||
dependencies:
|
||||
chardet: 2.1.0
|
||||
iconv-lite: 0.7.0
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@inquirer/figures@1.0.13': {}
|
||||
|
||||
'@inquirer/input@4.2.1(@types/node@22.18.5)':
|
||||
'@inquirer/input@4.2.1(@types/node@22.18.8)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.15(@types/node@22.18.5)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.5)
|
||||
'@inquirer/core': 10.1.15(@types/node@22.18.8)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.8)
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@inquirer/number@3.0.17(@types/node@22.18.5)':
|
||||
'@inquirer/number@3.0.17(@types/node@22.18.8)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.15(@types/node@22.18.5)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.5)
|
||||
'@inquirer/core': 10.1.15(@types/node@22.18.8)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.8)
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@inquirer/password@4.0.17(@types/node@22.18.5)':
|
||||
'@inquirer/password@4.0.17(@types/node@22.18.8)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.15(@types/node@22.18.5)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.5)
|
||||
'@inquirer/core': 10.1.15(@types/node@22.18.8)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.8)
|
||||
ansi-escapes: 4.3.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@inquirer/prompts@7.3.2(@types/node@22.18.5)':
|
||||
'@inquirer/prompts@7.3.2(@types/node@22.18.8)':
|
||||
dependencies:
|
||||
'@inquirer/checkbox': 4.2.1(@types/node@22.18.5)
|
||||
'@inquirer/confirm': 5.1.15(@types/node@22.18.5)
|
||||
'@inquirer/editor': 4.2.17(@types/node@22.18.5)
|
||||
'@inquirer/expand': 4.0.17(@types/node@22.18.5)
|
||||
'@inquirer/input': 4.2.1(@types/node@22.18.5)
|
||||
'@inquirer/number': 3.0.17(@types/node@22.18.5)
|
||||
'@inquirer/password': 4.0.17(@types/node@22.18.5)
|
||||
'@inquirer/rawlist': 4.1.5(@types/node@22.18.5)
|
||||
'@inquirer/search': 3.1.0(@types/node@22.18.5)
|
||||
'@inquirer/select': 4.3.1(@types/node@22.18.5)
|
||||
'@inquirer/checkbox': 4.2.1(@types/node@22.18.8)
|
||||
'@inquirer/confirm': 5.1.15(@types/node@22.18.8)
|
||||
'@inquirer/editor': 4.2.17(@types/node@22.18.8)
|
||||
'@inquirer/expand': 4.0.17(@types/node@22.18.8)
|
||||
'@inquirer/input': 4.2.1(@types/node@22.18.8)
|
||||
'@inquirer/number': 3.0.17(@types/node@22.18.8)
|
||||
'@inquirer/password': 4.0.17(@types/node@22.18.8)
|
||||
'@inquirer/rawlist': 4.1.5(@types/node@22.18.8)
|
||||
'@inquirer/search': 3.1.0(@types/node@22.18.8)
|
||||
'@inquirer/select': 4.3.1(@types/node@22.18.8)
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@inquirer/prompts@7.8.0(@types/node@22.18.5)':
|
||||
'@inquirer/prompts@7.8.0(@types/node@22.18.8)':
|
||||
dependencies:
|
||||
'@inquirer/checkbox': 4.2.1(@types/node@22.18.5)
|
||||
'@inquirer/confirm': 5.1.15(@types/node@22.18.5)
|
||||
'@inquirer/editor': 4.2.17(@types/node@22.18.5)
|
||||
'@inquirer/expand': 4.0.17(@types/node@22.18.5)
|
||||
'@inquirer/input': 4.2.1(@types/node@22.18.5)
|
||||
'@inquirer/number': 3.0.17(@types/node@22.18.5)
|
||||
'@inquirer/password': 4.0.17(@types/node@22.18.5)
|
||||
'@inquirer/rawlist': 4.1.5(@types/node@22.18.5)
|
||||
'@inquirer/search': 3.1.0(@types/node@22.18.5)
|
||||
'@inquirer/select': 4.3.1(@types/node@22.18.5)
|
||||
'@inquirer/checkbox': 4.2.1(@types/node@22.18.8)
|
||||
'@inquirer/confirm': 5.1.15(@types/node@22.18.8)
|
||||
'@inquirer/editor': 4.2.17(@types/node@22.18.8)
|
||||
'@inquirer/expand': 4.0.17(@types/node@22.18.8)
|
||||
'@inquirer/input': 4.2.1(@types/node@22.18.8)
|
||||
'@inquirer/number': 3.0.17(@types/node@22.18.8)
|
||||
'@inquirer/password': 4.0.17(@types/node@22.18.8)
|
||||
'@inquirer/rawlist': 4.1.5(@types/node@22.18.8)
|
||||
'@inquirer/search': 3.1.0(@types/node@22.18.8)
|
||||
'@inquirer/select': 4.3.1(@types/node@22.18.8)
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@inquirer/rawlist@4.1.5(@types/node@22.18.5)':
|
||||
'@inquirer/rawlist@4.1.5(@types/node@22.18.8)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.15(@types/node@22.18.5)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.5)
|
||||
'@inquirer/core': 10.1.15(@types/node@22.18.8)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.8)
|
||||
yoctocolors-cjs: 2.1.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@inquirer/search@3.1.0(@types/node@22.18.5)':
|
||||
'@inquirer/search@3.1.0(@types/node@22.18.8)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.15(@types/node@22.18.5)
|
||||
'@inquirer/core': 10.1.15(@types/node@22.18.8)
|
||||
'@inquirer/figures': 1.0.13
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.5)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.8)
|
||||
yoctocolors-cjs: 2.1.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@inquirer/select@4.3.1(@types/node@22.18.5)':
|
||||
'@inquirer/select@4.3.1(@types/node@22.18.8)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.15(@types/node@22.18.5)
|
||||
'@inquirer/core': 10.1.15(@types/node@22.18.8)
|
||||
'@inquirer/figures': 1.0.13
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.5)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.8)
|
||||
ansi-escapes: 4.3.2
|
||||
yoctocolors-cjs: 2.1.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@inquirer/type@3.0.8(@types/node@22.18.5)':
|
||||
'@inquirer/type@3.0.8(@types/node@22.18.8)':
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@internationalized/date@3.8.2':
|
||||
dependencies:
|
||||
@@ -14193,7 +14193,7 @@ snapshots:
|
||||
'@jest/schemas': 29.6.3
|
||||
'@types/istanbul-lib-coverage': 2.0.6
|
||||
'@types/istanbul-reports': 3.0.4
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
'@types/yargs': 17.0.33
|
||||
chalk: 4.1.2
|
||||
|
||||
@@ -14425,12 +14425,12 @@ snapshots:
|
||||
bullmq: 5.58.5
|
||||
tslib: 2.8.1
|
||||
|
||||
'@nestjs/cli@11.0.10(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.18.5)':
|
||||
'@nestjs/cli@11.0.10(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.18.8)':
|
||||
dependencies:
|
||||
'@angular-devkit/core': 19.2.15(chokidar@4.0.3)
|
||||
'@angular-devkit/schematics': 19.2.15(chokidar@4.0.3)
|
||||
'@angular-devkit/schematics-cli': 19.2.15(@types/node@22.18.5)(chokidar@4.0.3)
|
||||
'@inquirer/prompts': 7.8.0(@types/node@22.18.5)
|
||||
'@angular-devkit/schematics-cli': 19.2.15(@types/node@22.18.8)(chokidar@4.0.3)
|
||||
'@inquirer/prompts': 7.8.0(@types/node@22.18.8)
|
||||
'@nestjs/schematics': 11.0.7(chokidar@4.0.3)(typescript@5.8.3)
|
||||
ansis: 4.1.0
|
||||
chokidar: 4.0.3
|
||||
@@ -15829,7 +15829,7 @@ snapshots:
|
||||
|
||||
'@types/accepts@1.3.7':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/archiver@6.0.3':
|
||||
dependencies:
|
||||
@@ -15841,16 +15841,16 @@ snapshots:
|
||||
|
||||
'@types/bcrypt@6.0.0':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/body-parser@1.19.6':
|
||||
dependencies:
|
||||
'@types/connect': 3.4.38
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/bonjour@3.5.13':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/braces@3.0.5': {}
|
||||
|
||||
@@ -15871,21 +15871,21 @@ snapshots:
|
||||
|
||||
'@types/cli-progress@3.11.6':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/compression@1.8.1':
|
||||
dependencies:
|
||||
'@types/express': 5.0.3
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/connect-history-api-fallback@1.5.4':
|
||||
dependencies:
|
||||
'@types/express-serve-static-core': 5.0.6
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/connect@3.4.38':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/content-disposition@0.5.9': {}
|
||||
|
||||
@@ -15902,11 +15902,11 @@ snapshots:
|
||||
'@types/connect': 3.4.38
|
||||
'@types/express': 5.0.3
|
||||
'@types/keygrip': 1.0.6
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/cors@2.8.19':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/debug@4.1.12':
|
||||
dependencies:
|
||||
@@ -15916,13 +15916,13 @@ snapshots:
|
||||
|
||||
'@types/docker-modem@3.0.6':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
'@types/ssh2': 1.15.5
|
||||
|
||||
'@types/dockerode@3.3.42':
|
||||
dependencies:
|
||||
'@types/docker-modem': 3.0.6
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
'@types/ssh2': 1.15.5
|
||||
|
||||
'@types/dom-to-image@2.6.7': {}
|
||||
@@ -15945,14 +15945,14 @@ snapshots:
|
||||
|
||||
'@types/express-serve-static-core@4.19.6':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
'@types/qs': 6.14.0
|
||||
'@types/range-parser': 1.2.7
|
||||
'@types/send': 0.17.5
|
||||
|
||||
'@types/express-serve-static-core@5.0.6':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
'@types/qs': 6.14.0
|
||||
'@types/range-parser': 1.2.7
|
||||
'@types/send': 0.17.5
|
||||
@@ -15978,7 +15978,7 @@ snapshots:
|
||||
|
||||
'@types/fluent-ffmpeg@2.1.27':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/geojson-vt@3.2.5':
|
||||
dependencies:
|
||||
@@ -16010,7 +16010,7 @@ snapshots:
|
||||
|
||||
'@types/http-proxy@1.17.16':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/inquirer@8.2.11':
|
||||
dependencies:
|
||||
@@ -16048,7 +16048,7 @@ snapshots:
|
||||
'@types/http-errors': 2.0.5
|
||||
'@types/keygrip': 1.0.6
|
||||
'@types/koa-compose': 3.2.8
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/leaflet@1.9.20':
|
||||
dependencies:
|
||||
@@ -16080,7 +16080,7 @@ snapshots:
|
||||
|
||||
'@types/mock-fs@4.13.4':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/ms@2.1.0': {}
|
||||
|
||||
@@ -16090,7 +16090,7 @@ snapshots:
|
||||
|
||||
'@types/node-forge@1.3.11':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/node@17.0.45': {}
|
||||
|
||||
@@ -16102,7 +16102,7 @@ snapshots:
|
||||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
|
||||
'@types/node@22.18.5':
|
||||
'@types/node@22.18.8':
|
||||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
|
||||
@@ -16114,7 +16114,7 @@ snapshots:
|
||||
'@types/nodemailer@7.0.1':
|
||||
dependencies:
|
||||
'@aws-sdk/client-sesv2': 3.890.0
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
transitivePeerDependencies:
|
||||
- aws-crt
|
||||
|
||||
@@ -16122,7 +16122,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/keygrip': 1.0.6
|
||||
'@types/koa': 3.0.0
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/parse5@5.0.3': {}
|
||||
|
||||
@@ -16132,7 +16132,7 @@ snapshots:
|
||||
|
||||
'@types/pg@8.15.5':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
pg-protocol: 1.10.3
|
||||
pg-types: 2.2.0
|
||||
|
||||
@@ -16140,13 +16140,13 @@ snapshots:
|
||||
|
||||
'@types/pngjs@6.0.5':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/prismjs@1.26.5': {}
|
||||
|
||||
'@types/qrcode@1.5.5':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/qs@6.14.0': {}
|
||||
|
||||
@@ -16175,7 +16175,7 @@ snapshots:
|
||||
|
||||
'@types/readdir-glob@1.1.5':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/retry@0.12.0': {}
|
||||
|
||||
@@ -16185,14 +16185,14 @@ snapshots:
|
||||
|
||||
'@types/sax@1.2.7':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/semver@7.7.1': {}
|
||||
|
||||
'@types/send@0.17.5':
|
||||
dependencies:
|
||||
'@types/mime': 1.3.5
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/serve-index@1.9.4':
|
||||
dependencies:
|
||||
@@ -16201,20 +16201,20 @@ snapshots:
|
||||
'@types/serve-static@1.15.8':
|
||||
dependencies:
|
||||
'@types/http-errors': 2.0.5
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
'@types/send': 0.17.5
|
||||
|
||||
'@types/sockjs@0.3.36':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/ssh2-streams@0.1.12':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/ssh2@0.5.52':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
'@types/ssh2-streams': 0.1.12
|
||||
|
||||
'@types/ssh2@1.15.5':
|
||||
@@ -16225,7 +16225,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/cookiejar': 2.1.5
|
||||
'@types/methods': 1.1.4
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
form-data: 4.0.4
|
||||
|
||||
'@types/supercluster@7.1.3':
|
||||
@@ -16239,7 +16239,7 @@ snapshots:
|
||||
|
||||
'@types/through@0.0.33':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/ua-parser-js@0.7.39': {}
|
||||
|
||||
@@ -16255,7 +16255,7 @@ snapshots:
|
||||
|
||||
'@types/ws@8.18.1':
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
|
||||
'@types/yargs-parser@21.0.3': {}
|
||||
|
||||
@@ -16358,7 +16358,7 @@ snapshots:
|
||||
|
||||
'@ungap/structured-clone@1.3.0': {}
|
||||
|
||||
'@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))':
|
||||
'@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))':
|
||||
dependencies:
|
||||
'@ampproject/remapping': 2.3.0
|
||||
'@bcoe/v8-coverage': 1.0.2
|
||||
@@ -16373,7 +16373,7 @@ snapshots:
|
||||
std-env: 3.9.0
|
||||
test-exclude: 7.0.1
|
||||
tinyrainbow: 2.0.0
|
||||
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -16404,13 +16404,13 @@ snapshots:
|
||||
chai: 5.2.0
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@vitest/mocker@3.2.4(vite@7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))':
|
||||
'@vitest/mocker@3.2.4(vite@7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))':
|
||||
dependencies:
|
||||
'@vitest/spy': 3.2.4
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.19
|
||||
optionalDependencies:
|
||||
vite: 7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
vite: 7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
|
||||
'@vitest/mocker@3.2.4(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))':
|
||||
dependencies:
|
||||
@@ -17974,7 +17974,7 @@ snapshots:
|
||||
engine.io@6.6.4:
|
||||
dependencies:
|
||||
'@types/cors': 2.8.19
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
accepts: 1.3.8
|
||||
base64id: 2.0.0
|
||||
cookie: 0.7.2
|
||||
@@ -18364,7 +18364,7 @@ snapshots:
|
||||
|
||||
eval@0.1.8:
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
require-like: 0.1.2
|
||||
|
||||
event-emitter@0.3.5:
|
||||
@@ -19343,9 +19343,9 @@ snapshots:
|
||||
|
||||
inline-style-parser@0.2.4: {}
|
||||
|
||||
inquirer@8.2.7(@types/node@22.18.5):
|
||||
inquirer@8.2.7(@types/node@22.18.8):
|
||||
dependencies:
|
||||
'@inquirer/external-editor': 1.0.2(@types/node@22.18.5)
|
||||
'@inquirer/external-editor': 1.0.2(@types/node@22.18.8)
|
||||
ansi-escapes: 4.3.2
|
||||
chalk: 4.1.2
|
||||
cli-cursor: 3.1.0
|
||||
@@ -19549,7 +19549,7 @@ snapshots:
|
||||
jest-util@29.7.0:
|
||||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
chalk: 4.1.2
|
||||
ci-info: 3.9.0
|
||||
graceful-fs: 4.2.11
|
||||
@@ -19557,13 +19557,13 @@ snapshots:
|
||||
|
||||
jest-worker@27.5.1:
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
merge-stream: 2.0.0
|
||||
supports-color: 8.1.1
|
||||
|
||||
jest-worker@29.7.0:
|
||||
dependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
jest-util: 29.7.0
|
||||
merge-stream: 2.0.0
|
||||
supports-color: 8.1.1
|
||||
@@ -20774,7 +20774,7 @@ snapshots:
|
||||
|
||||
neo-async@2.6.2: {}
|
||||
|
||||
nest-commander@3.19.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@types/inquirer@8.2.11)(@types/node@22.18.5)(typescript@5.9.2):
|
||||
nest-commander@3.19.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@types/inquirer@8.2.11)(@types/node@22.18.8)(typescript@5.9.2):
|
||||
dependencies:
|
||||
'@fig/complete-commander': 3.2.0(commander@11.1.0)
|
||||
'@golevelup/nestjs-discovery': 4.0.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)
|
||||
@@ -20783,7 +20783,7 @@ snapshots:
|
||||
'@types/inquirer': 8.2.11
|
||||
commander: 11.1.0
|
||||
cosmiconfig: 8.3.6(typescript@5.9.2)
|
||||
inquirer: 8.2.7(@types/node@22.18.5)
|
||||
inquirer: 8.2.7(@types/node@22.18.8)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- typescript
|
||||
@@ -20873,7 +20873,7 @@ snapshots:
|
||||
|
||||
node-releases@2.0.19: {}
|
||||
|
||||
nodemailer@7.0.6: {}
|
||||
nodemailer@7.0.7: {}
|
||||
|
||||
nopt@1.0.10:
|
||||
dependencies:
|
||||
@@ -21871,7 +21871,7 @@ snapshots:
|
||||
'@protobufjs/path': 1.1.2
|
||||
'@protobufjs/pool': 1.1.0
|
||||
'@protobufjs/utf8': 1.1.0
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
long: 5.3.2
|
||||
|
||||
protocol-buffers-schema@3.6.0: {}
|
||||
@@ -23737,13 +23737,13 @@ snapshots:
|
||||
- rollup
|
||||
- supports-color
|
||||
|
||||
vite-node@3.2.4(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1):
|
||||
vite-node@3.2.4(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1):
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
debug: 4.4.3
|
||||
es-module-lexer: 1.7.0
|
||||
pathe: 2.0.3
|
||||
vite: 7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
vite: 7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- jiti
|
||||
@@ -23779,18 +23779,18 @@ snapshots:
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
vite-tsconfig-paths@5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)):
|
||||
vite-tsconfig-paths@5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)):
|
||||
dependencies:
|
||||
debug: 4.4.3
|
||||
globrex: 0.1.2
|
||||
tsconfck: 3.1.6(typescript@5.9.2)
|
||||
optionalDependencies:
|
||||
vite: 7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
vite: 7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
vite@7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1):
|
||||
vite@7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1):
|
||||
dependencies:
|
||||
esbuild: 0.25.9
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
@@ -23799,7 +23799,7 @@ snapshots:
|
||||
rollup: 4.50.1
|
||||
tinyglobby: 0.2.15
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
fsevents: 2.3.3
|
||||
jiti: 2.5.1
|
||||
lightningcss: 1.30.1
|
||||
@@ -23826,15 +23826,15 @@ snapshots:
|
||||
optionalDependencies:
|
||||
vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
|
||||
vitest-fetch-mock@0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)):
|
||||
vitest-fetch-mock@0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)):
|
||||
dependencies:
|
||||
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
|
||||
vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1):
|
||||
vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1):
|
||||
dependencies:
|
||||
'@types/chai': 5.2.2
|
||||
'@vitest/expect': 3.2.4
|
||||
'@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))
|
||||
'@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))
|
||||
'@vitest/pretty-format': 3.2.4
|
||||
'@vitest/runner': 3.2.4
|
||||
'@vitest/snapshot': 3.2.4
|
||||
@@ -23852,12 +23852,12 @@ snapshots:
|
||||
tinyglobby: 0.2.15
|
||||
tinypool: 1.1.1
|
||||
tinyrainbow: 2.0.0
|
||||
vite: 7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
vite-node: 3.2.4(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
vite: 7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
vite-node: 3.2.4(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
'@types/debug': 4.1.12
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
happy-dom: 18.0.1
|
||||
jsdom: 26.1.0(canvas@2.11.2(encoding@0.1.13))
|
||||
transitivePeerDependencies:
|
||||
@@ -23874,11 +23874,11 @@ snapshots:
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1):
|
||||
vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1):
|
||||
dependencies:
|
||||
'@types/chai': 5.2.2
|
||||
'@vitest/expect': 3.2.4
|
||||
'@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))
|
||||
'@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))
|
||||
'@vitest/pretty-format': 3.2.4
|
||||
'@vitest/runner': 3.2.4
|
||||
'@vitest/snapshot': 3.2.4
|
||||
@@ -23896,12 +23896,12 @@ snapshots:
|
||||
tinyglobby: 0.2.15
|
||||
tinypool: 1.1.1
|
||||
tinyrainbow: 2.0.0
|
||||
vite: 7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
vite-node: 3.2.4(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
vite: 7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
vite-node: 3.2.4(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
'@types/debug': 4.1.12
|
||||
'@types/node': 22.18.5
|
||||
'@types/node': 22.18.8
|
||||
happy-dom: 18.0.1
|
||||
jsdom: 26.1.0(canvas@2.11.2)
|
||||
transitivePeerDependencies:
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
<a href="README_zh_CN.md">简体中文</a>
|
||||
<a href="README_zh_TW.md">正體中文</a>
|
||||
<a href="README_uk_UA.md">Українська</a>
|
||||
<a href="README_ru_RU.md">Русский</a>
|
||||
<a href="README_pt_BR.md">Português Brasileiro</a>
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
<a href="README_zh_CN.md">简体中文</a>
|
||||
<a href="README_zh_TW.md">正體中文</a>
|
||||
<a href="README_uk_UA.md">Українська</a>
|
||||
<a href="README_ru_RU.md">Русский</a>
|
||||
<a href="README_pt_BR.md">Português Brasileiro</a>
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
<a href="README_ko_KR.md">한국어</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
<a href="README_zh_CN.md">简体中文</a>
|
||||
<a href="README_zh_TW.md">正體中文</a>
|
||||
<a href="README_uk_UA.md">Українська</a>
|
||||
<a href="README_ru_RU.md">Русский</a>
|
||||
<a href="README_pt_BR.md">Português Brasileiro</a>
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
<a href="README_zh_CN.md">简体中文</a>
|
||||
<a href="README_zh_TW.md">正體中文</a>
|
||||
<a href="README_uk_UA.md">Українська</a>
|
||||
<a href="README_ru_RU.md">Русский</a>
|
||||
<a href="README_pt_BR.md">Português Brasileiro</a>
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
<a href="README_zh_CN.md">简体中文</a>
|
||||
<a href="README_zh_TW.md">正體中文</a>
|
||||
<a href="README_uk_UA.md">Українська</a>
|
||||
<a href="README_ru_RU.md">Русский</a>
|
||||
<a href="README_pt_BR.md">Português Brasileiro</a>
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
<a href="README_zh_CN.md">简体中文</a>
|
||||
<a href="README_zh_TW.md">正體中文</a>
|
||||
<a href="README_uk_UA.md">Українська</a>
|
||||
<a href="README_ru_RU.md">Русский</a>
|
||||
<a href="README_pt_BR.md">Português Brasileiro</a>
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
<a href="README_zh_CN.md">简体中文</a>
|
||||
<a href="README_zh_TW.md">正體中文</a>
|
||||
<a href="README_ru_RU.md">Русский</a>
|
||||
<a href="README_pt_BR.md">Português Brasileiro</a>
|
||||
<a href="README_sv_SE.md">Svenska</a>
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
<a href="README_zh_CN.md">简体中文</a>
|
||||
<a href="README_zh_TW.md">正體中文</a>
|
||||
<a href="README_uk_UA.md">Українська</a>
|
||||
<a href="README_ru_RU.md">Русский</a>
|
||||
<a href="README_pt_BR.md">Português Brasileiro</a>
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
<a href="README_ko_KR.md">한국어</a>
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
<a href="README_zh_CN.md">简体中文</a>
|
||||
<a href="README_zh_TW.md">正體中文</a>
|
||||
<a href="README_uk_UA.md">Українська</a>
|
||||
<a href="README_ru_RU.md">Русский</a>
|
||||
<a href="README_pt_BR.md">Português Brasileiro</a>
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
<a href="README_zh_CN.md">简体中文</a>
|
||||
<a href="README_zh_TW.md">正體中文</a>
|
||||
<a href="README_uk_UA.md">Українська</a>
|
||||
<a href="README_ru_RU.md">Русский</a>
|
||||
<a href="README_sv_SE.md">Svenska</a>
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
<a href="README_zh_CN.md">简体中文</a>
|
||||
<a href="README_zh_TW.md">正體中文</a>
|
||||
<a href="README_uk_UA.md">Українська</a>
|
||||
<a href="README_pt_BR.md">Português Brasileiro</a>
|
||||
<a href="README_sv_SE.md">Svenska</a>
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
<a href="README_zh_CN.md">简体中文</a>
|
||||
<a href="README_zh_TW.md">正體中文</a>
|
||||
<a href="README_uk_UA.md">Українська</a>
|
||||
<a href="README_ru_RU.md">Русский</a>
|
||||
<a href="README_pt_BR.md">Português Brasileiro</a>
|
||||
|
||||
@@ -31,7 +31,8 @@
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
<a href="README_zh_CN.md">简体中文</a>
|
||||
<a href="README_zh_TW.md">正體中文</a>
|
||||
<a href="README_uk_UA.md">Українська</a>
|
||||
<a href="README_ru_RU.md">Русский</a>
|
||||
<a href="README_pt_BR.md">Português Brasileiro</a>
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
<a href="README_ko_KR.md">한국어</a>
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
<a href="README_zh_CN.md">简体中文</a>
|
||||
<a href="README_zh_TW.md">正體中文</a>
|
||||
<a href="README_uk_UA.md">Українська</a>
|
||||
<a href="README_ru_RU.md">Русский</a>
|
||||
<a href="README_pt_BR.md">Português Brasileiro</a>
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
<a href="README_zh_CN.md">简体中文</a>
|
||||
<a href="README_zh_TW.md">正體中文</a>
|
||||
<a href="README_ru_RU.md">Русский</a>
|
||||
<a href="README_pt_BR.md">Português Brasileiro</a>
|
||||
<a href="README_sv_SE.md">Svenska</a>
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
<a href="README_zh_CN.md">简体中文</a>
|
||||
<a href="README_zh_TW.md">正體中文</a>
|
||||
<a href="README_uk_UA.md">Українська</a>
|
||||
<a href="README_ru_RU.md">Русский</a>
|
||||
<a href="README_pt_BR.md">Português Brasileiro</a>
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_TW.md">正體中文</a>
|
||||
<a href="README_uk_UA.md">Українська</a>
|
||||
<a href="README_ru_RU.md">Русский</a>
|
||||
<a href="README_pt_BR.md">Português Brasileiro</a>
|
||||
|
||||
132
readme_i18n/README_zh_TW.md
Normal file
@@ -0,0 +1,132 @@
|
||||
<p align="center">
|
||||
<br/>
|
||||
<a href="https://opensource.org/license/agpl-v3"><img src="https://img.shields.io/badge/License-AGPL_v3-blue.svg?color=3F51B5&style=for-the-badge&label=授權條款&logoColor=000000&labelColor=ececec" alt="授權條款:AGPLv3"></a>
|
||||
<a href="https://discord.immich.app">
|
||||
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" alt="Discord"/>
|
||||
</a>
|
||||
<br/>
|
||||
<br/>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="../design/immich-logo-stacked-light.svg" width="300" title="以自訂 URL 登入">
|
||||
</p>
|
||||
<h3 align="center">高效能的自架照片和影片管理解決方案</h3>
|
||||
<br/>
|
||||
<a href="https://immich.app">
|
||||
<img src="../design/immich-screenshots.png" title="主要螢幕截圖">
|
||||
</a>
|
||||
<br/>
|
||||
|
||||
<p align="center">
|
||||
<a href="../README.md">English</a>
|
||||
<a href="README_ca_ES.md">Català</a>
|
||||
<a href="README_es_ES.md">Español</a>
|
||||
<a href="README_fr_FR.md">Français</a>
|
||||
<a href="README_it_IT.md">Italiano</a>
|
||||
<a href="README_ja_JP.md">日本語</a>
|
||||
<a href="README_ko_KR.md">한국어</a>
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">简体中文</a>
|
||||
<a href="README_zh_TW.md">正體中文</a>
|
||||
<a href="README_uk_UA.md">Українська</a>
|
||||
<a href="README_ru_RU.md">Русский</a>
|
||||
<a href="README_pt_BR.md">Português Brasileiro</a>
|
||||
<a href="README_sv_SE.md">Svenska</a>
|
||||
<a href="README_ar_JO.md">العربية</a>
|
||||
<a href="README_vi_VN.md">Tiếng Việt</a>
|
||||
<a href="README_th_TH.md">ภาษาไทย</a>
|
||||
</p>
|
||||
|
||||
> [!WARNING]
|
||||
> ⚠️ 請務必遵循 [3-2-1 備份原則](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/),守護珍貴的照片與影片!
|
||||
>
|
||||
|
||||
> [!NOTE]
|
||||
> 主要說明文件(包含安裝指南)可於 https://immich.app/ 取得。
|
||||
|
||||
## 連結
|
||||
|
||||
- [說明文件](https://docs.immich.app/)
|
||||
- [關於](https://docs.immich.app/overview/introduction)
|
||||
- [安裝](https://docs.immich.app/install/requirements)
|
||||
- [發展藍圖](https://immich.app/roadmap)
|
||||
- [線上體驗](#線上體驗)
|
||||
- [功能](#功能)
|
||||
- [翻譯](https://docs.immich.app/developer/translations)
|
||||
- [貢獻指南](https://docs.immich.app/overview/support-the-project)
|
||||
|
||||
## 線上體驗
|
||||
|
||||
請前往 [Demoo 網站](https://demo.immich.app) 立即體驗 Immich。若要在手機 App 試用,請在 `伺服器端點 URL` 欄位輸入 `https://demo.immich.app`。
|
||||
|
||||
### 登入資訊
|
||||
|
||||
| 電子郵件 | 密碼 |
|
||||
| --------------- | ------ |
|
||||
| demo@immich.app | demo |
|
||||
|
||||
## 功能
|
||||
|
||||
| 功能 | 手機版 | 網頁版 |
|
||||
| :----------------------------------------- | ------ | ------ |
|
||||
| 上傳與檢視照片與影片 | 是 | 是 |
|
||||
| 開啟 App 時自動備份 | 是 | 不適用 |
|
||||
| 避免重複媒體 | 是 | 是 |
|
||||
| 選擇要備份的相簿 | 是 | 不適用 |
|
||||
| 下載照片與影片到本機裝置 | 是 | 是 |
|
||||
| 多使用者支援 | 是 | 是 |
|
||||
| 相簿與共享相簿 | 是 | 是 |
|
||||
| 可拖曳的捲軸 | 是 | 是 |
|
||||
| 支援 RAW 格式 | 是 | 是 |
|
||||
| 中繼資料檢視(EXIF、地圖) | 是 | 是 |
|
||||
| 依中繼資料、物件、臉孔與 CLIP 搜尋 | 是 | 是 |
|
||||
| 管理功能(使用者管理) | 否 | 是 |
|
||||
| 背景備份 | 是 | 不適用 |
|
||||
| 虛擬滾動 | 是 | 是 |
|
||||
| 支援 OAuth | 是 | 是 |
|
||||
| API 金鑰 | 不適用 | 是 |
|
||||
| Live Photo/動態照片備份與播放 | 是 | 是 |
|
||||
| 支援 360 度全景照片顯示 | 否 | 是 |
|
||||
| 使用者自訂儲存結構 | 是 | 是 |
|
||||
| 公開分享 | 是 | 是 |
|
||||
| 封存與收藏 | 是 | 是 |
|
||||
| 世界地圖 | 是 | 是 |
|
||||
| 親朋好友分享 | 是 | 是 |
|
||||
| 臉部辨識與分群 | 是 | 是 |
|
||||
| 回憶(x 年前) | 是 | 是 |
|
||||
| 離線支援 | 是 | 否 |
|
||||
| 唯讀媒體庫 | 是 | 是 |
|
||||
| 照片堆疊 | 是 | 是 |
|
||||
| 標籤 | 否 | 是 |
|
||||
| 資料夾檢視 | 是 | 是 |
|
||||
|
||||
## 翻譯
|
||||
|
||||
更多翻譯相關資訊請見 [此處](https://docs.immich.app/developer/translations)。
|
||||
|
||||
<a href="https://hosted.weblate.org/engage/immich/">
|
||||
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="翻譯狀態" />
|
||||
</a>
|
||||
|
||||
## 專案活躍度
|
||||
|
||||

|
||||
|
||||
## Star 數量歷史紀錄
|
||||
|
||||
<a href="https://star-history.com/#immich-app/immich&Date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=immich-app/immich&type=Date&theme=dark" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=immich-app/immich&type=Date" />
|
||||
<img alt="Star 歷史紀錄圖表" src="https://api.star-history.com/svg?repos=immich-app/immich&type=Date" width="100%" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## 貢獻者
|
||||
|
||||
<a href="https://github.com/alextran1502/immich/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=immich-app/immich" width="100%"/>
|
||||
</a>
|
||||
@@ -1,35 +1,50 @@
|
||||
FROM ghcr.io/immich-app/base-server-dev:202509210934@sha256:b5ce2d7eaf379d4cf15efd4bab180d8afc8a80d20b36c9800f4091aca6ae267e AS builder
|
||||
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
|
||||
CI=1 \
|
||||
COREPACK_HOME=/tmp
|
||||
COREPACK_HOME=/tmp \
|
||||
PNPM_HOME=/buildcache/pnpm-store \
|
||||
PATH="/buildcache/pnpm-store:$PATH"
|
||||
|
||||
RUN npm install --global corepack@latest && \
|
||||
corepack enable pnpm
|
||||
corepack enable pnpm && \
|
||||
pnpm config set store-dir "$PNPM_HOME"
|
||||
|
||||
FROM builder AS server
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
COPY ./package* ./pnpm* .pnpmfile.cjs ./
|
||||
COPY ./server ./server/
|
||||
RUN SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile build && \
|
||||
RUN --mount=type=cache,id=pnpm-server,target=/buildcache/pnpm-store \
|
||||
--mount=type=bind,source=package.json,target=package.json \
|
||||
--mount=type=bind,source=.pnpmfile.cjs,target=.pnpmfile.cjs \
|
||||
--mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
|
||||
--mount=type=bind,source=pnpm-workspace.yaml,target=pnpm-workspace.yaml \
|
||||
SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile build && \
|
||||
SHARP_FORCE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile --prod --no-optional deploy /output/server-pruned
|
||||
|
||||
FROM builder AS web
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
COPY ./package* ./pnpm* .pnpmfile.cjs ./
|
||||
COPY ./web ./web/
|
||||
COPY ./i18n ./i18n/
|
||||
COPY ./open-api ./open-api/
|
||||
RUN SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter @immich/sdk --filter immich-web --frozen-lockfile --force install && \
|
||||
RUN --mount=type=cache,id=pnpm-web,target=/buildcache/pnpm-store \
|
||||
--mount=type=bind,source=package.json,target=package.json \
|
||||
--mount=type=bind,source=.pnpmfile.cjs,target=.pnpmfile.cjs \
|
||||
--mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
|
||||
--mount=type=bind,source=pnpm-workspace.yaml,target=pnpm-workspace.yaml \
|
||||
SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter @immich/sdk --filter immich-web --frozen-lockfile --force install && \
|
||||
pnpm --filter @immich/sdk --filter immich-web build
|
||||
|
||||
FROM builder AS cli
|
||||
|
||||
COPY ./package* ./pnpm* .pnpmfile.cjs ./
|
||||
COPY ./cli ./cli/
|
||||
COPY ./open-api ./open-api/
|
||||
RUN pnpm --filter @immich/sdk --filter @immich/cli --frozen-lockfile install && \
|
||||
RUN --mount=type=cache,id=pnpm-cli,target=/buildcache/pnpm-store \
|
||||
--mount=type=bind,source=package.json,target=package.json \
|
||||
--mount=type=bind,source=.pnpmfile.cjs,target=.pnpmfile.cjs \
|
||||
--mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
|
||||
--mount=type=bind,source=pnpm-workspace.yaml,target=pnpm-workspace.yaml \
|
||||
pnpm --filter @immich/sdk --filter @immich/cli --frozen-lockfile install && \
|
||||
pnpm --filter @immich/sdk --filter @immich/cli build && \
|
||||
pnpm --filter @immich/cli --prod --no-optional deploy /output/cli-pruned
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ ENTRYPOINT ["tini", "--", "/bin/bash", "-c"]
|
||||
FROM dev AS dev-container-server
|
||||
|
||||
RUN apt-get update --allow-releaseinfo-change && \
|
||||
apt-get install sudo inetutils-ping openjdk-11-jre-headless \
|
||||
vim nano \
|
||||
apt-get install sudo inetutils-ping openjdk-21-jre-headless \
|
||||
vim nano curl \
|
||||
-y --no-install-recommends --fix-missing
|
||||
|
||||
RUN usermod -aG sudo node && \
|
||||
@@ -44,13 +44,18 @@ FROM dev-container-server AS dev-container-mobile
|
||||
USER root
|
||||
# Enable multiarch for arm64 if necessary
|
||||
RUN if [ "$(dpkg --print-architecture)" = "arm64" ]; then \
|
||||
dpkg --add-architecture amd64 && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
qemu-user-static \
|
||||
libc6:amd64 \
|
||||
libstdc++6:amd64 \
|
||||
libgcc1:amd64; \
|
||||
dpkg --add-architecture amd64 && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
gnupg \
|
||||
qemu-user-static \
|
||||
libc6:amd64 \
|
||||
libstdc++6:amd64 \
|
||||
libgcc1:amd64; \
|
||||
else \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
gnupg; \
|
||||
fi
|
||||
|
||||
# Flutter SDK
|
||||
@@ -65,11 +70,11 @@ RUN mkdir -p ${FLUTTER_HOME} \
|
||||
&& curl -C - --output flutter.tar.xz https://storage.googleapis.com/flutter_infra_release/releases/${FLUTTER_CHANNEL}/linux/flutter_linux_${FLUTTER_VERSION}-${FLUTTER_CHANNEL}.tar.xz \
|
||||
&& tar -xf flutter.tar.xz --strip-components=1 -C ${FLUTTER_HOME} \
|
||||
&& rm flutter.tar.xz \
|
||||
&& chown -R node ${FLUTTER_HOME}
|
||||
&& chown -R node ${FLUTTER_HOME} \
|
||||
&& git config --global --add safe.directory ${FLUTTER_HOME}
|
||||
|
||||
|
||||
RUN apt-get update \
|
||||
&& wget -qO- https://dcm.dev/pgp-key.public | gpg --dearmor -o /usr/share/keyrings/dcm.gpg \
|
||||
RUN wget -qO- https://dcm.dev/pgp-key.public | gpg --dearmor -o /usr/share/keyrings/dcm.gpg \
|
||||
&& echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | tee /etc/apt/sources.list.d/dart_stable.list \
|
||||
&& apt-get update \
|
||||
&& apt-get install dcm -y
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
"@types/luxon": "^3.6.2",
|
||||
"@types/mock-fs": "^4.13.1",
|
||||
"@types/multer": "^2.0.0",
|
||||
"@types/node": "^22.18.1",
|
||||
"@types/node": "^22.18.8",
|
||||
"@types/nodemailer": "^7.0.0",
|
||||
"@types/picomatch": "^4.0.0",
|
||||
"@types/pngjs": "^6.0.5",
|
||||
|
||||
@@ -19,6 +19,7 @@ import { ConfigRepository } from 'src/repositories/config.repository';
|
||||
import { EventRepository } from 'src/repositories/event.repository';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import { teardownTelemetry, TelemetryRepository } from 'src/repositories/telemetry.repository';
|
||||
import { UserRepository } from 'src/repositories/user.repository';
|
||||
import { services } from 'src/services';
|
||||
import { AuthService } from 'src/services/auth.service';
|
||||
import { CliService } from 'src/services/cli.service';
|
||||
@@ -55,6 +56,7 @@ class BaseModule implements OnModuleInit, OnModuleDestroy {
|
||||
private jobService: JobService,
|
||||
private telemetryRepository: TelemetryRepository,
|
||||
private authService: AuthService,
|
||||
private userRepository: UserRepository,
|
||||
) {
|
||||
logger.setAppName(this.worker);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { SemVer } from 'semver';
|
||||
import { DatabaseExtension, ExifOrientation, VectorIndex } from 'src/enum';
|
||||
|
||||
export const POSTGRES_VERSION_RANGE = '>=14.0.0';
|
||||
export const VECTORCHORD_VERSION_RANGE = '>=0.3 <0.5';
|
||||
export const VECTORCHORD_VERSION_RANGE = '>=0.3 <0.6';
|
||||
export const VECTORS_VERSION_RANGE = '>=0.2 <0.4';
|
||||
export const VECTOR_VERSION_RANGE = '>=0.5 <1';
|
||||
|
||||
|
||||
@@ -363,6 +363,14 @@ group by
|
||||
order by
|
||||
"user"."createdAt" asc
|
||||
|
||||
-- UserRepository.getCount
|
||||
select
|
||||
count(*) as "count"
|
||||
from
|
||||
"user"
|
||||
where
|
||||
"user"."deletedAt" is null
|
||||
|
||||
-- UserRepository.updateUsage
|
||||
update "user"
|
||||
set
|
||||
|
||||
@@ -286,6 +286,16 @@ export class UserRepository {
|
||||
.execute();
|
||||
}
|
||||
|
||||
@GenerateSql()
|
||||
async getCount(): Promise<number> {
|
||||
const result = await this.db
|
||||
.selectFrom('user')
|
||||
.select((eb) => eb.fn.countAll().as('count'))
|
||||
.where('user.deletedAt', 'is', null)
|
||||
.executeTakeFirstOrThrow();
|
||||
return Number(result.count);
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.NUMBER] })
|
||||
async updateUsage(id: string, delta: number): Promise<void> {
|
||||
await this.db
|
||||
|
||||