diff --git a/.dockerignore b/.dockerignore index 87d63b5942..f7947202cd 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,30 +1,31 @@ .vscode/ +.github/ +.git/ design/ docker/ docs/ +e2e/ fastlane/ machine-learning/ misc/ mobile/ -server/node_modules/ +cli/coverage/ +cli/dist/ +cli/node_modules/ + +open-api/typescript-sdk/build/ +open-api/typescript-sdk/node_modules/ + server/coverage/ -server/.reverse-geocoding-dump/ +server/node_modules/ server/upload/ server/dist/ +server/www/ +server/test/assets/ web/node_modules/ web/coverage/ web/.svelte-kit web/build/ - -cli/node_modules/ -cli/.reverse-geocoding-dump/ -cli/upload/ -cli/dist/ - -e2e/ - -open-api/typescript-sdk/node_modules/ -open-api/typescript-sdk/build/ diff --git a/.editorconfig b/.editorconfig index 64cb414065..43e1c061c1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -16,4 +16,4 @@ max_line_length = off trim_trailing_whitespace = false [*.{yml,yaml}] -quote_type = double +quote_type = single diff --git a/.gitattributes b/.gitattributes index b0a7f5ccc4..f89b54de52 100644 --- a/.gitattributes +++ b/.gitattributes @@ -8,8 +8,6 @@ mobile/openapi/.openapi-generator/FILES linguist-generated=true mobile/lib/**/*.g.dart -diff -merge mobile/lib/**/*.g.dart linguist-generated=true -open-api/typescript-sdk/axios-client/**/* -diff -merge -open-api/typescript-sdk/axios-client/**/* linguist-generated=true open-api/typescript-sdk/fetch-client.ts -diff -merge open-api/typescript-sdk/fetch-client.ts linguist-generated=true diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 87170e5d59..07e07f422a 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -58,7 +58,7 @@ jobs: uses: docker/setup-qemu-action@v3.0.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.0.0 + uses: docker/setup-buildx-action@v3.1.0 - name: Login to GitHub Container Registry uses: docker/login-action@v3 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index a6a6835882..dd1c53468a 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -66,7 +66,7 @@ jobs: uses: docker/setup-qemu-action@v3.0.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.0.0 + uses: docker/setup-buildx-action@v3.1.0 # Workaround to fix error: # failed to push: failed to copy: io: read/write on closed pipe # See https://github.com/docker/build-push-action/issues/761 diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml index a6de171fb8..c2b633c4b0 100644 --- a/.github/workflows/static_analysis.yml +++ b/.github/workflows/static_analysis.yml @@ -32,7 +32,11 @@ jobs: - name: Run dart analyze run: dart analyze --fatal-infos working-directory: ./mobile - + + - name: Run dart format + run: dart format lib/ --set-exit-if-changed + working-directory: ./mobile + # Enable after riverpod generator migration is completed # - name: Run dart custom lint # run: dart run custom_lint diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1a23bd351d..b6b17774ed 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ concurrency: jobs: server-e2e-api: name: Server (e2e-api) - runs-on: mich + runs-on: ubuntu-latest defaults: run: working-directory: ./server @@ -29,13 +29,13 @@ jobs: server-e2e-jobs: name: Server (e2e-jobs) - runs-on: mich + runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 with: - submodules: "recursive" + submodules: 'recursive' - name: Run e2e tests run: make server-e2e-jobs @@ -135,38 +135,6 @@ jobs: run: npm run test:cov if: ${{ !cancelled() }} - cli-e2e-tests: - name: CLI (e2e) - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./cli - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: "recursive" - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: 20 - - - name: Run setup typescript-sdk - run: npm ci && npm run build - working-directory: ./open-api/typescript-sdk - - - name: Run npm install (cli) - run: npm ci - - - name: Run npm install (server) - run: npm ci && npm run build - working-directory: ./server - - - name: Run e2e tests - run: npm run test:e2e - web-unit-tests: name: Web runs-on: ubuntu-latest @@ -205,8 +173,8 @@ jobs: run: npm run test:cov if: ${{ !cancelled() }} - web-e2e-tests: - name: Web (e2e) + e2e-tests: + name: End-to-End Tests runs-on: ubuntu-latest defaults: run: @@ -215,23 +183,51 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + with: + submodules: 'recursive' + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 - name: Run setup typescript-sdk run: npm ci && npm run build working-directory: ./open-api/typescript-sdk + if: ${{ !cancelled() }} + + - name: Run setup cli + run: npm ci && npm run build + working-directory: ./cli + if: ${{ !cancelled() }} - name: Install dependencies run: npm ci + if: ${{ !cancelled() }} + + - name: Run linter + run: npm run lint + if: ${{ !cancelled() }} + + - name: Run formatter + run: npm run format + if: ${{ !cancelled() }} - name: Install Playwright Browsers - run: npx playwright install --with-deps + run: npx playwright install --with-deps chromium + if: ${{ !cancelled() }} - name: Docker build - run: docker compose -f docker/docker-compose.e2e.yml build - working-directory: ./ + run: docker compose build + if: ${{ !cancelled() }} - - name: Run e2e tests + - name: Run e2e tests (api & cli) + run: npm run test + if: ${{ !cancelled() }} + + - name: Run e2e tests (web) run: npx playwright test + if: ${{ !cancelled() }} mobile-unit-tests: name: Mobile @@ -241,8 +237,8 @@ jobs: - name: Setup Flutter SDK uses: subosito/flutter-action@v2 with: - channel: "stable" - flutter-version: "3.16.9" + channel: 'stable' + flutter-version: '3.16.9' - name: Run tests working-directory: ./mobile run: flutter test -j 1 @@ -260,7 +256,7 @@ jobs: - uses: actions/setup-python@v5 with: python-version: 3.11 - cache: "poetry" + cache: 'poetry' - name: Install dependencies run: | poetry install --with dev --with cpu @@ -277,6 +273,19 @@ jobs: run: | poetry run pytest app --cov=app --cov-report term-missing + shellcheck: + name: ShellCheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run ShellCheck + uses: ludeeus/action-shellcheck@master + with: + ignore_paths: >- + **/open-api/** + **/openapi/** + **/node_modules/** + generated-api-up-to-date: name: OpenAPI Clients runs-on: ubuntu-latest @@ -285,7 +294,7 @@ jobs: - name: Run API generation run: make open-api - name: Find file changes - uses: tj-actions/verify-changed-files@v13.1 + uses: tj-actions/verify-changed-files@v19 id: verify-changed-files with: files: | @@ -340,7 +349,7 @@ jobs: run: npm run typeorm:migrations:generate ./src/infra/migrations/TestMigration - name: Find file changes - uses: tj-actions/verify-changed-files@v13.1 + uses: tj-actions/verify-changed-files@v19 id: verify-changed-files with: files: | @@ -358,7 +367,7 @@ jobs: DB_URL: postgres://postgres:postgres@localhost:5432/immich - name: Find file changes - uses: tj-actions/verify-changed-files@v13.1 + uses: tj-actions/verify-changed-files@v19 id: verify-changed-sql-files with: files: | diff --git a/Makefile b/Makefile index e28232bef8..b455e2656b 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ server-e2e-api: .PHONY: e2e e2e: - docker compose -f ./docker/docker-compose.e2e.yml up --build -V --remove-orphans + docker compose -f ./e2e/docker-compose.yml up --build -V --remove-orphans prod: docker compose -f ./docker/docker-compose.prod.yml up --build -V --remove-orphans diff --git a/README.md b/README.md index 3cbb545fa4..ca8b1e3970 100644 --- a/README.md +++ b/README.md @@ -128,3 +128,9 @@ If you feel like this is the right cause and the app is something you are seeing + +## Star History + + + Star History Chart + diff --git a/cli/.eslintrc.cjs b/cli/.eslintrc.cjs index ca36d31bf7..33ee3bd1e8 100644 --- a/cli/.eslintrc.cjs +++ b/cli/.eslintrc.cjs @@ -10,7 +10,6 @@ module.exports = { root: true, env: { node: true, - jest: true, }, ignorePatterns: ['.eslintrc.js'], rules: { @@ -22,11 +21,6 @@ module.exports = { 'unicorn/prefer-module': 'off', curly: 2, 'prettier/prettier': 0, - 'unicorn/prevent-abbreviations': [ - 'error', - { - ignore: ['\\.e2e-spec$', /^ignore/i], - }, - ], + 'unicorn/prevent-abbreviations': 'error', }, }; diff --git a/cli/.npmignore b/cli/.npmignore index 1d0d005a94..42809f8e80 100644 --- a/cli/.npmignore +++ b/cli/.npmignore @@ -1,5 +1,4 @@ **/*.spec.js -test/** upload/** .editorconfig .eslintignore diff --git a/cli/.prettierrc b/cli/.prettierrc index 7cebf3813c..cde9f154ce 100644 --- a/cli/.prettierrc +++ b/cli/.prettierrc @@ -2,5 +2,7 @@ "singleQuote": true, "trailingComma": "all", "printWidth": 120, - "semi": true + "semi": true, + "plugins": ["prettier-plugin-organize-imports"], + "organizeImportsSkipDestructiveCodeActions": true } diff --git a/cli/Dockerfile b/cli/Dockerfile index 02fffca397..cb0383a009 100644 --- a/cli/Dockerfile +++ b/cli/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20-alpine3.19 as core +FROM node:20-alpine3.19@sha256:c0a3badbd8a0a760de903e00cedbca94588e609299820557e72cba2a53dbaa2c as core WORKDIR /usr/src/open-api/typescript-sdk COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./ diff --git a/cli/package-lock.json b/cli/package-lock.json index 39425c37d9..760d9863ea 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -8,6 +8,9 @@ "name": "@immich/cli", "version": "2.0.8", "license": "GNU Affero General Public License version 3", + "dependencies": { + "lodash-es": "^4.17.21" + }, "bin": { "immich": "dist/index.js" }, @@ -16,10 +19,11 @@ "@testcontainers/postgresql": "^10.7.1", "@types/byte-size": "^8.1.0", "@types/cli-progress": "^3.11.0", + "@types/lodash-es": "^4.17.12", "@types/mock-fs": "^4.13.1", "@types/node": "^20.3.1", - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", + "@typescript-eslint/eslint-plugin": "^7.0.0", + "@typescript-eslint/parser": "^7.0.0", "@vitest/coverage-v8": "^1.2.2", "byte-size": "^8.1.1", "cli-progress": "^3.12.0", @@ -29,8 +33,9 @@ "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^51.0.0", "glob": "^10.3.1", - "immich": "file:../server", "mock-fs": "^5.2.0", + "prettier": "^3.2.5", + "prettier-plugin-organize-imports": "^3.2.4", "typescript": "^5.3.3", "vite": "^5.0.12", "vitest": "^1.2.2", @@ -49,20 +54,12 @@ "@oazapfts/runtime": "^1.0.0", "@types/node": "^20.11.0", "typescript": "^5.3.3" - }, - "peerDependencies": { - "axios": "^1.6.7" - }, - "peerDependenciesMeta": { - "axios": { - "optional": true - } } }, "../server": { "name": "immich", "version": "1.94.1", - "dev": true, + "extraneous": true, "license": "GNU Affero General Public License version 3", "dependencies": { "@babel/runtime": "^7.22.11", @@ -132,8 +129,8 @@ "@types/sharp": "^0.31.1", "@types/supertest": "^6.0.0", "@types/ua-parser-js": "^0.7.36", - "@typescript-eslint/eslint-plugin": "^6.4.1", - "@typescript-eslint/parser": "^6.4.1", + "@typescript-eslint/eslint-plugin": "^7.0.0", + "@typescript-eslint/parser": "^7.0.0", "dotenv": "^16.3.1", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", @@ -1295,6 +1292,21 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/lodash": { + "version": "4.14.202", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", + "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==", + "dev": true + }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/mock-fs": { "version": "4.13.4", "resolved": "https://registry.npmjs.org/@types/mock-fs/-/mock-fs-4.13.4.tgz", @@ -1305,9 +1317,9 @@ } }, "node_modules/@types/node": { - "version": "20.11.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", - "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", + "version": "20.11.20", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.20.tgz", + "integrity": "sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -1320,9 +1332,9 @@ "dev": true }, "node_modules/@types/semver": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", - "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "node_modules/@types/ssh2": { @@ -1353,16 +1365,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.0.2.tgz", + "integrity": "sha512-/XtVZJtbaphtdrWjr+CJclaCVGPtOdBpFEnvtNf/jRV0IiEemRrL0qABex/nEt8isYcnFacm3nPHYQwL+Wb7qg==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/scope-manager": "7.0.2", + "@typescript-eslint/type-utils": "7.0.2", + "@typescript-eslint/utils": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -1378,8 +1390,8 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -1388,15 +1400,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.0.2.tgz", + "integrity": "sha512-GdwfDglCxSmU+QTS9vhz2Sop46ebNCXpPPvsByK7hu0rFGRHL+AusKQJ7SoN+LbLh6APFpQwHKmDSwN35Z700Q==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/scope-manager": "7.0.2", + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/typescript-estree": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2", "debug": "^4.3.4" }, "engines": { @@ -1407,7 +1419,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -1416,13 +1428,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.0.2.tgz", + "integrity": "sha512-l6sa2jF3h+qgN2qUMjVR3uCNGjWw4ahGfzIYsCtFrQJCjhbrDPdiihYT8FnnqFwsWX+20hK592yX9I2rxKTP4g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1433,13 +1445,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.0.2.tgz", + "integrity": "sha512-IKKDcFsKAYlk8Rs4wiFfEwJTQlHcdn8CLwLaxwd6zb8HNiMcQIFX9sWax2k4Cjj7l7mGS5N1zl7RCHOVwHq2VQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/typescript-estree": "7.0.2", + "@typescript-eslint/utils": "7.0.2", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -1451,7 +1463,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -1460,9 +1472,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.0.2.tgz", + "integrity": "sha512-ZzcCQHj4JaXFjdOql6adYV4B/oFOFjPOC9XYwCaZFRvqN8Llfvv4gSxrkQkd2u4Ci62i2c6W6gkDwQJDaRc4nA==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1473,13 +1485,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.2.tgz", + "integrity": "sha512-3AMc8khTcELFWcKcPc0xiLviEvvfzATpdPj/DXuOGIdQIIFybf4DMT1vKRbuAEOFMwhWt7NFLXRkbjsvKZQyvw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1525,17 +1537,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.0.2.tgz", + "integrity": "sha512-PZPIONBIB/X684bhT1XlrkjNZJIEevwkKDsdwfiu1WeqBxYEEdIgVDgm8/bbKHVu+6YOpeRqcfImTdImx/4Bsw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/scope-manager": "7.0.2", + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/typescript-estree": "7.0.2", "semver": "^7.5.4" }, "engines": { @@ -1546,16 +1558,16 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.2.tgz", + "integrity": "sha512-8Y+YiBmqPighbm5xA2k4wKTxRzx9EkBu7Rlw+WHqMvRJ3RPz/BMBO9b2ru0LUNmXg120PHUXD5+SWFy2R8DqlQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/types": "7.0.2", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -1573,9 +1585,9 @@ "dev": true }, "node_modules/@vitest/coverage-v8": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.2.2.tgz", - "integrity": "sha512-IHyKnDz18SFclIEEAHb9Y4Uxx0sPKC2VO1kdDCs1BF6Ip4S8rQprs971zIsooLUn7Afs71GRxWMWpkCGZpRMhw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.3.1.tgz", + "integrity": "sha512-UuBnkSJUNE9rdHjDCPyJ4fYuMkoMtnghes1XohYa4At0MS3OQSAo97FrbwSLRshYsXThMZy1+ybD/byK5llyIg==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.1", @@ -1596,17 +1608,17 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "^1.0.0" + "vitest": "1.3.1" } }, "node_modules/@vitest/expect": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.2.2.tgz", - "integrity": "sha512-3jpcdPAD7LwHUUiT2pZTj2U82I2Tcgg2oVPvKxhn6mDI2On6tfvPQTjAI4628GUGDZrCm4Zna9iQHm5cEexOAg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.3.1.tgz", + "integrity": "sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==", "dev": true, "dependencies": { - "@vitest/spy": "1.2.2", - "@vitest/utils": "1.2.2", + "@vitest/spy": "1.3.1", + "@vitest/utils": "1.3.1", "chai": "^4.3.10" }, "funding": { @@ -1614,12 +1626,12 @@ } }, "node_modules/@vitest/runner": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.2.2.tgz", - "integrity": "sha512-JctG7QZ4LSDXr5CsUweFgcpEvrcxOV1Gft7uHrvkQ+fsAVylmWQvnaAr/HDp3LAH1fztGMQZugIheTWjaGzYIg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.3.1.tgz", + "integrity": "sha512-5FzF9c3jG/z5bgCnjr8j9LNq/9OxV2uEBAITOXfoe3rdZJTdO7jzThth7FXv/6b+kdY65tpRQB7WaKhNZwX+Kg==", "dev": true, "dependencies": { - "@vitest/utils": "1.2.2", + "@vitest/utils": "1.3.1", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -1655,9 +1667,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.2.2.tgz", - "integrity": "sha512-SmGY4saEw1+bwE1th6S/cZmPxz/Q4JWsl7LvbQIky2tKE35US4gd0Mjzqfr84/4OD0tikGWaWdMja/nWL5NIPA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.3.1.tgz", + "integrity": "sha512-EF++BZbt6RZmOlE3SuTPu/NfwBF6q4ABS37HHXzs2LUVPBLx2QoY/K0fKpRChSo8eLiuxcbCVfqKgx/dplCDuQ==", "dev": true, "dependencies": { "magic-string": "^0.30.5", @@ -1669,9 +1681,9 @@ } }, "node_modules/@vitest/spy": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.2.2.tgz", - "integrity": "sha512-k9Gcahssw8d7X3pSLq3e3XEu/0L78mUkCjivUqCQeXJm9clfXR/Td8+AP+VC1O6fKPIDLcHDTAmBOINVuv6+7g==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.3.1.tgz", + "integrity": "sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig==", "dev": true, "dependencies": { "tinyspy": "^2.2.0" @@ -1681,9 +1693,9 @@ } }, "node_modules/@vitest/utils": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.2.2.tgz", - "integrity": "sha512-WKITBHLsBHlpjnDQahr+XK6RE7MiAsgrIkr0pGhQ9ygoxBfUeG0lUG5iLlzqjmKSlBv3+j5EGsriBzh+C3Tq9g==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.3.1.tgz", + "integrity": "sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==", "dev": true, "dependencies": { "diff-sequences": "^29.6.3", @@ -1695,15 +1707,6 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/utils/node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "dependencies": { - "@types/estree": "^1.0.0" - } - }, "node_modules/acorn": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", @@ -2794,6 +2797,15 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -2803,6 +2815,29 @@ "node": ">=0.10.0" } }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2999,6 +3034,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/glob": { "version": "10.3.10", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", @@ -3137,6 +3184,15 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -3166,10 +3222,6 @@ "node": ">= 4" } }, - "node_modules/immich": { - "resolved": "../server", - "link": true - }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -3301,6 +3353,18 @@ "node": ">=8" } }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -3542,6 +3606,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "node_modules/lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", @@ -3662,6 +3731,18 @@ "node": ">=8.6" } }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -3824,6 +3905,33 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -3833,6 +3941,21 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -4068,11 +4191,10 @@ } }, "node_modules/prettier": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", - "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", "dev": true, - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -4095,6 +4217,26 @@ "node": ">=6.0.0" } }, + "node_modules/prettier-plugin-organize-imports": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.2.4.tgz", + "integrity": "sha512-6m8WBhIp0dfwu0SkgfOxJqh+HpdyfqSSLfKKRZSFbDuEQXDDndb8fTpRWkUrX/uBenkex3MgnVk0J3b3Y5byog==", + "dev": true, + "peerDependencies": { + "@volar/vue-language-plugin-pug": "^1.0.4", + "@volar/vue-typescript": "^1.0.4", + "prettier": ">=2.0", + "typescript": ">=2.9" + }, + "peerDependenciesMeta": { + "@volar/vue-language-plugin-pug": { + "optional": true + }, + "@volar/vue-typescript": { + "optional": true + } + } + }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -4808,6 +4950,18 @@ "node": ">=8" } }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -4833,17 +4987,23 @@ } }, "node_modules/strip-literal": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.3.0.tgz", - "integrity": "sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.0.0.tgz", + "integrity": "sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==", "dev": true, "dependencies": { - "acorn": "^8.10.0" + "js-tokens": "^8.0.2" }, "funding": { "url": "https://github.com/sponsors/antfu" } }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-8.0.3.tgz", + "integrity": "sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==", + "dev": true + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -5001,9 +5161,9 @@ } }, "node_modules/tinyspy": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.0.tgz", - "integrity": "sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", "dev": true, "engines": { "node": ">=14.0.0" @@ -5200,9 +5360,9 @@ } }, "node_modules/vite": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.0.tgz", - "integrity": "sha512-STmSFzhY4ljuhz14bg9LkMTk3d98IO6DIArnTY6MeBwiD1Za2StcQtz7fzOUnRCqrHSD5+OS2reg4HOz1eoLnw==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.4.tgz", + "integrity": "sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==", "dev": true, "dependencies": { "esbuild": "^0.19.3", @@ -5255,9 +5415,9 @@ } }, "node_modules/vite-node": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.2.2.tgz", - "integrity": "sha512-1as4rDTgVWJO3n1uHmUYqq7nsFgINQ9u+mRcXpjeOMJUmviqNKjcZB7UfRZrlM7MjYXMKpuWp5oGkjaFLnjawg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.3.1.tgz", + "integrity": "sha512-azbRrqRxlWTJEVbzInZCTchx0X69M/XPTCz4H+TLvlTcR/xH/3hkRqhOakT41fMJCMzXTu4UvegkZiEoJAWvng==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -5277,18 +5437,17 @@ } }, "node_modules/vitest": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.2.2.tgz", - "integrity": "sha512-d5Ouvrnms3GD9USIK36KG8OZ5bEvKEkITFtnGv56HFaSlbItJuYr7hv2Lkn903+AvRAgSixiamozUVfORUekjw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.3.1.tgz", + "integrity": "sha512-/1QJqXs8YbCrfv/GPQ05wAZf2eakUPLPa18vkJAKE7RXOKfVHqMZZ1WlTjiwl6Gcn65M5vpNUB6EFLnEdRdEXQ==", "dev": true, "dependencies": { - "@vitest/expect": "1.2.2", - "@vitest/runner": "1.2.2", - "@vitest/snapshot": "1.2.2", - "@vitest/spy": "1.2.2", - "@vitest/utils": "1.2.2", + "@vitest/expect": "1.3.1", + "@vitest/runner": "1.3.1", + "@vitest/snapshot": "1.3.1", + "@vitest/spy": "1.3.1", + "@vitest/utils": "1.3.1", "acorn-walk": "^8.3.2", - "cac": "^6.7.14", "chai": "^4.3.10", "debug": "^4.3.4", "execa": "^8.0.1", @@ -5297,11 +5456,11 @@ "pathe": "^1.1.1", "picocolors": "^1.0.0", "std-env": "^3.5.0", - "strip-literal": "^1.3.0", + "strip-literal": "^2.0.0", "tinybench": "^2.5.1", "tinypool": "^0.8.2", "vite": "^5.0.0", - "vite-node": "1.2.2", + "vite-node": "1.3.1", "why-is-node-running": "^2.2.2" }, "bin": { @@ -5316,8 +5475,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "^1.0.0", - "@vitest/ui": "^1.0.0", + "@vitest/browser": "1.3.1", + "@vitest/ui": "1.3.1", "happy-dom": "*", "jsdom": "*" }, @@ -5342,128 +5501,6 @@ } } }, - "node_modules/vitest/node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/vitest/node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/vitest/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/npm-run-path": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", - "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", - "dev": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -6416,6 +6453,21 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "@types/lodash": { + "version": "4.14.202", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", + "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==", + "dev": true + }, + "@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, "@types/mock-fs": { "version": "4.13.4", "resolved": "https://registry.npmjs.org/@types/mock-fs/-/mock-fs-4.13.4.tgz", @@ -6426,9 +6478,9 @@ } }, "@types/node": { - "version": "20.11.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", - "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", + "version": "20.11.20", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.20.tgz", + "integrity": "sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==", "dev": true, "requires": { "undici-types": "~5.26.4" @@ -6441,9 +6493,9 @@ "dev": true }, "@types/semver": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", - "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "@types/ssh2": { @@ -6476,16 +6528,16 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.0.2.tgz", + "integrity": "sha512-/XtVZJtbaphtdrWjr+CJclaCVGPtOdBpFEnvtNf/jRV0IiEemRrL0qABex/nEt8isYcnFacm3nPHYQwL+Wb7qg==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/scope-manager": "7.0.2", + "@typescript-eslint/type-utils": "7.0.2", + "@typescript-eslint/utils": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -6495,54 +6547,54 @@ } }, "@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.0.2.tgz", + "integrity": "sha512-GdwfDglCxSmU+QTS9vhz2Sop46ebNCXpPPvsByK7hu0rFGRHL+AusKQJ7SoN+LbLh6APFpQwHKmDSwN35Z700Q==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/scope-manager": "7.0.2", + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/typescript-estree": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.0.2.tgz", + "integrity": "sha512-l6sa2jF3h+qgN2qUMjVR3uCNGjWw4ahGfzIYsCtFrQJCjhbrDPdiihYT8FnnqFwsWX+20hK592yX9I2rxKTP4g==", "dev": true, "requires": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2" } }, "@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.0.2.tgz", + "integrity": "sha512-IKKDcFsKAYlk8Rs4wiFfEwJTQlHcdn8CLwLaxwd6zb8HNiMcQIFX9sWax2k4Cjj7l7mGS5N1zl7RCHOVwHq2VQ==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/typescript-estree": "7.0.2", + "@typescript-eslint/utils": "7.0.2", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.0.2.tgz", + "integrity": "sha512-ZzcCQHj4JaXFjdOql6adYV4B/oFOFjPOC9XYwCaZFRvqN8Llfvv4gSxrkQkd2u4Ci62i2c6W6gkDwQJDaRc4nA==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.2.tgz", + "integrity": "sha512-3AMc8khTcELFWcKcPc0xiLviEvvfzATpdPj/DXuOGIdQIIFybf4DMT1vKRbuAEOFMwhWt7NFLXRkbjsvKZQyvw==", "dev": true, "requires": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -6572,27 +6624,27 @@ } }, "@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.0.2.tgz", + "integrity": "sha512-PZPIONBIB/X684bhT1XlrkjNZJIEevwkKDsdwfiu1WeqBxYEEdIgVDgm8/bbKHVu+6YOpeRqcfImTdImx/4Bsw==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/scope-manager": "7.0.2", + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/typescript-estree": "7.0.2", "semver": "^7.5.4" } }, "@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.2.tgz", + "integrity": "sha512-8Y+YiBmqPighbm5xA2k4wKTxRzx9EkBu7Rlw+WHqMvRJ3RPz/BMBO9b2ru0LUNmXg120PHUXD5+SWFy2R8DqlQ==", "dev": true, "requires": { - "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/types": "7.0.2", "eslint-visitor-keys": "^3.4.1" } }, @@ -6603,9 +6655,9 @@ "dev": true }, "@vitest/coverage-v8": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.2.2.tgz", - "integrity": "sha512-IHyKnDz18SFclIEEAHb9Y4Uxx0sPKC2VO1kdDCs1BF6Ip4S8rQprs971zIsooLUn7Afs71GRxWMWpkCGZpRMhw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.3.1.tgz", + "integrity": "sha512-UuBnkSJUNE9rdHjDCPyJ4fYuMkoMtnghes1XohYa4At0MS3OQSAo97FrbwSLRshYsXThMZy1+ybD/byK5llyIg==", "dev": true, "requires": { "@ampproject/remapping": "^2.2.1", @@ -6624,23 +6676,23 @@ } }, "@vitest/expect": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.2.2.tgz", - "integrity": "sha512-3jpcdPAD7LwHUUiT2pZTj2U82I2Tcgg2oVPvKxhn6mDI2On6tfvPQTjAI4628GUGDZrCm4Zna9iQHm5cEexOAg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.3.1.tgz", + "integrity": "sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==", "dev": true, "requires": { - "@vitest/spy": "1.2.2", - "@vitest/utils": "1.2.2", + "@vitest/spy": "1.3.1", + "@vitest/utils": "1.3.1", "chai": "^4.3.10" } }, "@vitest/runner": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.2.2.tgz", - "integrity": "sha512-JctG7QZ4LSDXr5CsUweFgcpEvrcxOV1Gft7uHrvkQ+fsAVylmWQvnaAr/HDp3LAH1fztGMQZugIheTWjaGzYIg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.3.1.tgz", + "integrity": "sha512-5FzF9c3jG/z5bgCnjr8j9LNq/9OxV2uEBAITOXfoe3rdZJTdO7jzThth7FXv/6b+kdY65tpRQB7WaKhNZwX+Kg==", "dev": true, "requires": { - "@vitest/utils": "1.2.2", + "@vitest/utils": "1.3.1", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -6663,9 +6715,9 @@ } }, "@vitest/snapshot": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.2.2.tgz", - "integrity": "sha512-SmGY4saEw1+bwE1th6S/cZmPxz/Q4JWsl7LvbQIky2tKE35US4gd0Mjzqfr84/4OD0tikGWaWdMja/nWL5NIPA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.3.1.tgz", + "integrity": "sha512-EF++BZbt6RZmOlE3SuTPu/NfwBF6q4ABS37HHXzs2LUVPBLx2QoY/K0fKpRChSo8eLiuxcbCVfqKgx/dplCDuQ==", "dev": true, "requires": { "magic-string": "^0.30.5", @@ -6674,35 +6726,24 @@ } }, "@vitest/spy": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.2.2.tgz", - "integrity": "sha512-k9Gcahssw8d7X3pSLq3e3XEu/0L78mUkCjivUqCQeXJm9clfXR/Td8+AP+VC1O6fKPIDLcHDTAmBOINVuv6+7g==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.3.1.tgz", + "integrity": "sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig==", "dev": true, "requires": { "tinyspy": "^2.2.0" } }, "@vitest/utils": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.2.2.tgz", - "integrity": "sha512-WKITBHLsBHlpjnDQahr+XK6RE7MiAsgrIkr0pGhQ9ygoxBfUeG0lUG5iLlzqjmKSlBv3+j5EGsriBzh+C3Tq9g==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.3.1.tgz", + "integrity": "sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==", "dev": true, "requires": { "diff-sequences": "^29.6.3", "estree-walker": "^3.0.3", "loupe": "^2.3.7", "pretty-format": "^29.7.0" - }, - "dependencies": { - "estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "requires": { - "@types/estree": "^1.0.0" - } - } } }, "acorn": { @@ -7496,12 +7537,38 @@ } } }, + "estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0" + } + }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + } + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -7657,6 +7724,12 @@ "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", "dev": true }, + "get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true + }, "glob": { "version": "10.3.10", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", @@ -7761,6 +7834,12 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true + }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -7773,99 +7852,6 @@ "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true }, - "immich": { - "version": "file:../server", - "requires": { - "@babel/runtime": "^7.22.11", - "@immich/cli": "^2.0.7", - "@nestjs/bullmq": "^10.0.1", - "@nestjs/cli": "^10.1.16", - "@nestjs/common": "^10.2.2", - "@nestjs/config": "^3.0.0", - "@nestjs/core": "^10.2.2", - "@nestjs/platform-express": "^10.2.2", - "@nestjs/platform-socket.io": "^10.2.2", - "@nestjs/schedule": "^4.0.0", - "@nestjs/schematics": "^10.0.2", - "@nestjs/swagger": "^7.1.8", - "@nestjs/testing": "^10.2.2", - "@nestjs/typeorm": "^10.0.0", - "@nestjs/websockets": "^10.2.2", - "@socket.io/postgres-adapter": "^0.3.1", - "@testcontainers/postgresql": "^10.2.1", - "@types/archiver": "^6.0.0", - "@types/async-lock": "^1.4.2", - "@types/bcrypt": "^5.0.0", - "@types/cookie-parser": "^1.4.3", - "@types/express": "^4.17.17", - "@types/fluent-ffmpeg": "^2.1.21", - "@types/imagemin": "^8.0.1", - "@types/jest": "29.5.12", - "@types/jest-when": "^3.5.2", - "@types/lodash": "^4.14.197", - "@types/mock-fs": "^4.13.1", - "@types/multer": "^1.4.7", - "@types/node": "^20.5.7", - "@types/picomatch": "^2.3.3", - "@types/sharp": "^0.31.1", - "@types/supertest": "^6.0.0", - "@types/ua-parser-js": "^0.7.36", - "@typescript-eslint/eslint-plugin": "^6.4.1", - "@typescript-eslint/parser": "^6.4.1", - "archiver": "^6.0.0", - "async-lock": "^1.4.0", - "bcrypt": "^5.1.1", - "bullmq": "^4.8.0", - "chokidar": "^3.5.3", - "class-transformer": "^0.5.1", - "class-validator": "^0.14.0", - "cookie-parser": "^1.4.6", - "dotenv": "^16.3.1", - "eslint": "^8.56.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^51.0.0", - "exiftool-vendored": "~24.4.0", - "exiftool-vendored.pl": "12.73", - "fluent-ffmpeg": "^2.1.2", - "geo-tz": "^8.0.0", - "glob": "^10.3.3", - "handlebars": "^4.7.8", - "i18n-iso-countries": "^7.6.0", - "ioredis": "^5.3.2", - "jest": "^29.6.4", - "jest-when": "^3.6.0", - "joi": "^17.10.0", - "lodash": "^4.17.21", - "luxon": "^3.4.2", - "mock-fs": "^5.2.0", - "nest-commander": "^3.11.1", - "node-addon-api": "^7.0.0", - "openid-client": "^5.4.3", - "pg": "^8.11.3", - "picomatch": "^4.0.0", - "prettier": "^3.0.2", - "prettier-plugin-organize-imports": "^3.2.3", - "reflect-metadata": "^0.1.13", - "rimraf": "^5.0.1", - "rxjs": "^7.8.1", - "sanitize-filename": "^1.6.3", - "sharp": "^0.33.0", - "source-map-support": "^0.5.21", - "sql-formatter": "^15.0.0", - "supertest": "^6.3.3", - "testcontainers": "^10.2.1", - "thumbhash": "^0.1.1", - "ts-jest": "^29.1.1", - "ts-loader": "^9.4.4", - "ts-node": "^10.9.1", - "tsconfig-paths": "^4.2.0", - "typeorm": "^0.3.17", - "typescript": "^5.3.3", - "ua-parser-js": "^1.0.35", - "utimes": "^5.2.1" - } - }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -7961,6 +7947,12 @@ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -8157,6 +8149,11 @@ "p-locate": "^5.0.0" } }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", @@ -8259,6 +8256,12 @@ "picomatch": "^2.3.1" } }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true + }, "min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -8376,6 +8379,23 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "requires": { + "path-key": "^4.0.0" + }, + "dependencies": { + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + } + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -8385,6 +8405,15 @@ "wrappy": "1" } }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "requires": { + "mimic-fn": "^4.0.0" + } + }, "optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -8543,11 +8572,10 @@ "dev": true }, "prettier": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", - "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", - "dev": true, - "peer": true + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "dev": true }, "prettier-linter-helpers": { "version": "1.0.0", @@ -8558,6 +8586,13 @@ "fast-diff": "^1.1.2" } }, + "prettier-plugin-organize-imports": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.2.4.tgz", + "integrity": "sha512-6m8WBhIp0dfwu0SkgfOxJqh+HpdyfqSSLfKKRZSFbDuEQXDDndb8fTpRWkUrX/uBenkex3MgnVk0J3b3Y5byog==", + "dev": true, + "requires": {} + }, "pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -9096,6 +9131,12 @@ "ansi-regex": "^5.0.1" } }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + }, "strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -9112,12 +9153,20 @@ "dev": true }, "strip-literal": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.3.0.tgz", - "integrity": "sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.0.0.tgz", + "integrity": "sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==", "dev": true, "requires": { - "acorn": "^8.10.0" + "js-tokens": "^8.0.2" + }, + "dependencies": { + "js-tokens": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-8.0.3.tgz", + "integrity": "sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==", + "dev": true + } } }, "supports-color": { @@ -9251,9 +9300,9 @@ "dev": true }, "tinyspy": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.0.tgz", - "integrity": "sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", "dev": true }, "tmp": { @@ -9391,9 +9440,9 @@ } }, "vite": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.0.tgz", - "integrity": "sha512-STmSFzhY4ljuhz14bg9LkMTk3d98IO6DIArnTY6MeBwiD1Za2StcQtz7fzOUnRCqrHSD5+OS2reg4HOz1eoLnw==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.4.tgz", + "integrity": "sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==", "dev": true, "requires": { "esbuild": "^0.19.3", @@ -9403,9 +9452,9 @@ } }, "vite-node": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.2.2.tgz", - "integrity": "sha512-1as4rDTgVWJO3n1uHmUYqq7nsFgINQ9u+mRcXpjeOMJUmviqNKjcZB7UfRZrlM7MjYXMKpuWp5oGkjaFLnjawg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.3.1.tgz", + "integrity": "sha512-azbRrqRxlWTJEVbzInZCTchx0X69M/XPTCz4H+TLvlTcR/xH/3hkRqhOakT41fMJCMzXTu4UvegkZiEoJAWvng==", "dev": true, "requires": { "cac": "^6.7.14", @@ -9416,18 +9465,17 @@ } }, "vitest": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.2.2.tgz", - "integrity": "sha512-d5Ouvrnms3GD9USIK36KG8OZ5bEvKEkITFtnGv56HFaSlbItJuYr7hv2Lkn903+AvRAgSixiamozUVfORUekjw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.3.1.tgz", + "integrity": "sha512-/1QJqXs8YbCrfv/GPQ05wAZf2eakUPLPa18vkJAKE7RXOKfVHqMZZ1WlTjiwl6Gcn65M5vpNUB6EFLnEdRdEXQ==", "dev": true, "requires": { - "@vitest/expect": "1.2.2", - "@vitest/runner": "1.2.2", - "@vitest/snapshot": "1.2.2", - "@vitest/spy": "1.2.2", - "@vitest/utils": "1.2.2", + "@vitest/expect": "1.3.1", + "@vitest/runner": "1.3.1", + "@vitest/snapshot": "1.3.1", + "@vitest/spy": "1.3.1", + "@vitest/utils": "1.3.1", "acorn-walk": "^8.3.2", - "cac": "^6.7.14", "chai": "^4.3.10", "debug": "^4.3.4", "execa": "^8.0.1", @@ -9436,85 +9484,12 @@ "pathe": "^1.1.1", "picocolors": "^1.0.0", "std-env": "^3.5.0", - "strip-literal": "^1.3.0", + "strip-literal": "^2.0.0", "tinybench": "^2.5.1", "tinypool": "^0.8.2", "vite": "^5.0.0", - "vite-node": "1.2.2", + "vite-node": "1.3.1", "why-is-node-running": "^2.2.2" - }, - "dependencies": { - "execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - } - }, - "get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true - }, - "human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true - }, - "is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true - }, - "mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true - }, - "npm-run-path": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", - "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", - "dev": true, - "requires": { - "path-key": "^4.0.0" - } - }, - "onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "requires": { - "mimic-fn": "^4.0.0" - } - }, - "path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true - }, - "strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true - } } }, "webidl-conversions": { diff --git a/cli/package.json b/cli/package.json index c5a90057c0..de36dfcf66 100644 --- a/cli/package.json +++ b/cli/package.json @@ -17,10 +17,11 @@ "@testcontainers/postgresql": "^10.7.1", "@types/byte-size": "^8.1.0", "@types/cli-progress": "^3.11.0", + "@types/lodash-es": "^4.17.12", "@types/mock-fs": "^4.13.1", "@types/node": "^20.3.1", - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", + "@typescript-eslint/eslint-plugin": "^7.0.0", + "@typescript-eslint/parser": "^7.0.0", "@vitest/coverage-v8": "^1.2.2", "byte-size": "^8.1.1", "cli-progress": "^3.12.0", @@ -30,8 +31,9 @@ "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^51.0.0", "glob": "^10.3.1", - "immich": "file:../server", "mock-fs": "^5.2.0", + "prettier": "^3.2.5", + "prettier-plugin-organize-imports": "^3.2.4", "typescript": "^5.3.3", "vite": "^5.0.12", "vitest": "^1.2.2", @@ -39,15 +41,14 @@ }, "scripts": { "build": "vite build", - "lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\" --max-warnings 0", + "lint": "eslint \"src/**/*.ts\" --max-warnings 0", "lint:fix": "npm run lint -- --fix", "prepack": "npm run build", "test": "vitest", "test:cov": "vitest --coverage", "format": "prettier --check .", "format:fix": "prettier --write .", - "check": "tsc --noEmit", - "test:e2e": "vitest --config test/e2e/vitest.config.ts" + "check": "tsc --noEmit" }, "repository": { "type": "git", @@ -56,5 +57,8 @@ }, "engines": { "node": ">=20.0.0" + }, + "dependencies": { + "lodash-es": "^4.17.21" } } diff --git a/cli/src/commands/base-command.ts b/cli/src/commands/base-command.ts index cc76f4a7ea..2ecb3fef2d 100644 --- a/cli/src/commands/base-command.ts +++ b/cli/src/commands/base-command.ts @@ -1,6 +1,6 @@ import { ServerVersionResponseDto, UserResponseDto } from '@immich/sdk'; -import { SessionService } from '../services/session.service'; import { ImmichApi } from 'src/services/api.service'; +import { SessionService } from '../services/session.service'; export abstract class BaseCommand { protected sessionService!: SessionService; diff --git a/cli/src/commands/upload.command.ts b/cli/src/commands/upload.command.ts index f26f297a29..8029b1313f 100644 --- a/cli/src/commands/upload.command.ts +++ b/cli/src/commands/upload.command.ts @@ -1,23 +1,33 @@ +import { AssetBulkUploadCheckResult } from '@immich/sdk'; import byteSize from 'byte-size'; import cliProgress from 'cli-progress'; +import { chunk, zip } from 'lodash-es'; +import { createHash } from 'node:crypto'; import fs, { createReadStream } from 'node:fs'; +import { access, constants, stat, unlink } from 'node:fs/promises'; +import os from 'node:os'; +import { basename } from 'node:path'; +import { ImmichApi } from 'src/services/api.service'; import { CrawlService } from '../services/crawl.service'; import { BaseCommand } from './base-command'; -import { basename } from 'node:path'; -import { access, constants, stat, unlink } from 'node:fs/promises'; -import { createHash } from 'node:crypto'; -import os from 'node:os'; -import { ImmichApi } from 'src/services/api.service'; + +const zipDefined = zip as (a: T[], b: U[]) => [T, U][]; + +enum CheckResponseStatus { + ACCEPT = 'accept', + REJECT = 'reject', + DUPLICATE = 'duplicate', +} class Asset { readonly path: string; - readonly deviceId!: string; + id?: string; deviceAssetId?: string; fileCreatedAt?: Date; fileModifiedAt?: Date; sidecarPath?: string; - fileSize!: number; + fileSize?: number; albumName?: string; constructor(path: string) { @@ -105,17 +115,141 @@ export class UploadOptionsDto { album? = false; albumName? = ''; includeHidden? = false; + concurrency? = 4; } export class UploadCommand extends BaseCommand { - uploadLength!: number; + api!: ImmichApi; public async run(paths: string[], options: UploadOptionsDto): Promise { - const api = await this.connect(); + this.api = await this.connect(); - const formatResponse = await api.getSupportedMediaTypes(); - const crawlService = new CrawlService(formatResponse.image, formatResponse.video); + console.log('Crawling for assets...'); + const files = await this.getFiles(paths, options); + if (files.length === 0) { + console.log('No assets found, exiting'); + return; + } + + const assetsToCheck = files.map((path) => new Asset(path)); + + const { newAssets, duplicateAssets } = await this.checkAssets(assetsToCheck, options.concurrency ?? 4); + + const totalSizeUploaded = await this.upload(newAssets, options); + const messageStart = options.dryRun ? 'Would have' : 'Successfully'; + if (newAssets.length === 0) { + console.log('All assets were already uploaded, nothing to do.'); + } else { + console.log( + `${messageStart} uploaded ${newAssets.length} asset${newAssets.length === 1 ? '' : 's'} (${byteSize(totalSizeUploaded)})`, + ); + } + + if (options.album || options.albumName) { + const { createdAlbumCount, updatedAssetCount } = await this.updateAlbums( + [...newAssets, ...duplicateAssets], + options, + ); + console.log(`${messageStart} created ${createdAlbumCount} new album${createdAlbumCount === 1 ? '' : 's'}`); + console.log(`${messageStart} updated ${updatedAssetCount} asset${updatedAssetCount === 1 ? '' : 's'}`); + } + + if (!options.delete) { + return; + } + + if (options.dryRun) { + console.log(`Would now have deleted assets, but skipped due to dry run`); + return; + } + + console.log('Deleting assets that have been uploaded...'); + + await this.deleteAssets(newAssets, options); + } + + public async checkAssets( + assetsToCheck: Asset[], + concurrency: number, + ): Promise<{ newAssets: Asset[]; duplicateAssets: Asset[]; rejectedAssets: Asset[] }> { + for (const assets of chunk(assetsToCheck, concurrency)) { + await Promise.all(assets.map((asset: Asset) => asset.prepare())); + } + + const checkProgress = new cliProgress.SingleBar( + { format: 'Checking assets | {bar} | {percentage}% | ETA: {eta}s | {value}/{total} assets' }, + cliProgress.Presets.shades_classic, + ); + checkProgress.start(assetsToCheck.length, 0); + + const newAssets = []; + const duplicateAssets = []; + const rejectedAssets = []; + try { + for (const assets of chunk(assetsToCheck, concurrency)) { + const checkedAssets = await this.getStatus(assets); + for (const checked of checkedAssets) { + if (checked.status === CheckResponseStatus.ACCEPT) { + newAssets.push(checked.asset); + } else if (checked.status === CheckResponseStatus.DUPLICATE) { + duplicateAssets.push(checked.asset); + } else { + rejectedAssets.push(checked.asset); + } + checkProgress.increment(); + } + } + } finally { + checkProgress.stop(); + } + + return { newAssets, duplicateAssets, rejectedAssets }; + } + + public async upload(assetsToUpload: Asset[], options: UploadOptionsDto): Promise { + let totalSize = 0; + + // Compute total size first + for (const asset of assetsToUpload) { + totalSize += asset.fileSize ?? 0; + } + + if (options.dryRun) { + return totalSize; + } + + const uploadProgress = new cliProgress.SingleBar( + { + format: 'Uploading assets | {bar} | {percentage}% | ETA: {eta_formatted} | {value_formatted}/{total_formatted}', + }, + cliProgress.Presets.shades_classic, + ); + uploadProgress.start(totalSize, 0); + uploadProgress.update({ value_formatted: 0, total_formatted: byteSize(totalSize) }); + + let totalSizeUploaded = 0; + try { + for (const assets of chunk(assetsToUpload, options.concurrency)) { + const ids = await this.uploadAssets(assets); + for (const [asset, id] of zipDefined(assets, ids)) { + asset.id = id; + if (asset.fileSize) { + totalSizeUploaded += asset.fileSize ?? 0; + } else { + console.log(`Could not determine file size for ${asset.path}`); + } + } + uploadProgress.update(totalSizeUploaded, { value_formatted: byteSize(totalSizeUploaded) }); + } + } finally { + uploadProgress.stop(); + } + + return totalSizeUploaded; + } + + public async getFiles(paths: string[], options: UploadOptionsDto): Promise { const inputFiles: string[] = []; for (const pathArgument of paths) { const fileStat = await fs.promises.lstat(pathArgument); @@ -124,151 +258,187 @@ export class UploadCommand extends BaseCommand { } } - const files: string[] = await crawlService.crawl({ + const files: string[] = await this.crawl(paths, options); + files.push(...inputFiles); + return files; + } + + public async getAlbums(): Promise> { + const existingAlbums = await this.api.getAllAlbums(); + + const albumMapping = new Map(); + for (const album of existingAlbums) { + albumMapping.set(album.albumName, album.id); + } + + return albumMapping; + } + + public async updateAlbums( + assets: Asset[], + options: UploadOptionsDto, + ): Promise<{ createdAlbumCount: number; updatedAssetCount: number }> { + if (options.albumName) { + for (const asset of assets) { + asset.albumName = options.albumName; + } + } + + const existingAlbums = await this.getAlbums(); + const assetsToUpdate = assets.filter( + (asset): asset is Asset & { albumName: string; id: string } => !!(asset.albumName && asset.id), + ); + + const newAlbumsSet: Set = new Set(); + for (const asset of assetsToUpdate) { + if (!existingAlbums.has(asset.albumName)) { + newAlbumsSet.add(asset.albumName); + } + } + + const newAlbums = [...newAlbumsSet]; + + if (options.dryRun) { + return { createdAlbumCount: newAlbums.length, updatedAssetCount: assetsToUpdate.length }; + } + + const albumCreationProgress = new cliProgress.SingleBar( + { + format: 'Creating albums | {bar} | {percentage}% | ETA: {eta}s | {value}/{total} albums', + }, + cliProgress.Presets.shades_classic, + ); + albumCreationProgress.start(newAlbums.length, 0); + + try { + for (const albumNames of chunk(newAlbums, options.concurrency)) { + const newAlbumIds = await Promise.all( + albumNames.map((albumName: string) => this.api.createAlbum({ albumName }).then((r) => r.id)), + ); + + for (const [albumName, albumId] of zipDefined(albumNames, newAlbumIds)) { + existingAlbums.set(albumName, albumId); + } + + albumCreationProgress.increment(albumNames.length); + } + } finally { + albumCreationProgress.stop(); + } + + const albumToAssets = new Map(); + for (const asset of assetsToUpdate) { + const albumId = existingAlbums.get(asset.albumName); + if (albumId) { + if (!albumToAssets.has(albumId)) { + albumToAssets.set(albumId, []); + } + albumToAssets.get(albumId)?.push(asset.id); + } + } + + const albumUpdateProgress = new cliProgress.SingleBar( + { + format: 'Adding assets to albums | {bar} | {percentage}% | ETA: {eta}s | {value}/{total} assets', + }, + cliProgress.Presets.shades_classic, + ); + albumUpdateProgress.start(assetsToUpdate.length, 0); + + try { + for (const [albumId, assets] of albumToAssets.entries()) { + for (const assetBatch of chunk(assets, Math.min(1000 * (options.concurrency ?? 4), 65_000))) { + await this.api.addAssetsToAlbum(albumId, { ids: assetBatch }); + albumUpdateProgress.increment(assetBatch.length); + } + } + } finally { + albumUpdateProgress.stop(); + } + + return { createdAlbumCount: newAlbums.length, updatedAssetCount: assetsToUpdate.length }; + } + + public async deleteAssets(assets: Asset[], options: UploadOptionsDto): Promise { + const deletionProgress = new cliProgress.SingleBar( + { + format: 'Deleting local assets | {bar} | {percentage}% | ETA: {eta}s | {value}/{total} assets', + }, + cliProgress.Presets.shades_classic, + ); + deletionProgress.start(assets.length, 0); + + try { + for (const assetBatch of chunk(assets, options.concurrency)) { + await Promise.all(assetBatch.map((asset: Asset) => asset.delete())); + deletionProgress.update(assetBatch.length); + } + } finally { + deletionProgress.stop(); + } + } + + private async getStatus(assets: Asset[]): Promise<{ asset: Asset; status: CheckResponseStatus }[]> { + const checkResponse = await this.checkHashes(assets); + + const responses = []; + for (const [check, asset] of zipDefined(checkResponse, assets)) { + if (check.assetId) { + asset.id = check.assetId; + } + + if (check.action === 'accept') { + responses.push({ asset, status: CheckResponseStatus.ACCEPT }); + } else if (check.reason === 'duplicate') { + responses.push({ asset, status: CheckResponseStatus.DUPLICATE }); + } else { + responses.push({ asset, status: CheckResponseStatus.REJECT }); + } + } + + return responses; + } + + private async checkHashes(assetsToCheck: Asset[]): Promise { + const checksums = await Promise.all(assetsToCheck.map((asset) => asset.hash())); + const assetBulkUploadCheckDto = { + assets: zipDefined(assetsToCheck, checksums).map(([asset, checksum]) => ({ id: asset.path, checksum })), + }; + const checkResponse = await this.api.checkBulkUpload(assetBulkUploadCheckDto); + return checkResponse.results; + } + + private async uploadAssets(assets: Asset[]): Promise { + const fileRequests = await Promise.all(assets.map((asset) => asset.getUploadFormData())); + return Promise.all(fileRequests.map((request) => this.uploadAsset(request).then((response) => response.id))); + } + + private async crawl(paths: string[], options: UploadOptionsDto): Promise { + const formatResponse = await this.api.getSupportedMediaTypes(); + const crawlService = new CrawlService(formatResponse.image, formatResponse.video); + + return crawlService.crawl({ pathsToCrawl: paths, recursive: options.recursive, exclusionPatterns: options.exclusionPatterns, includeHidden: options.includeHidden, }); - - files.push(...inputFiles); - - if (files.length === 0) { - console.log('No assets found, exiting'); - return; - } - - const assetsToUpload = files.map((path) => new Asset(path)); - - const uploadProgress = new cliProgress.SingleBar( - { - format: '{bar} | {percentage}% | ETA: {eta_formatted} | {value_formatted}/{total_formatted}: {filename}', - }, - cliProgress.Presets.shades_classic, - ); - - let totalSize = 0; - let sizeSoFar = 0; - - let totalSizeUploaded = 0; - let uploadCounter = 0; - - for (const asset of assetsToUpload) { - // Compute total size first - await asset.prepare(); - totalSize += asset.fileSize; - - if (options.albumName) { - asset.albumName = options.albumName; - } - } - - const existingAlbums = await api.getAllAlbums(); - - uploadProgress.start(totalSize, 0); - uploadProgress.update({ value_formatted: 0, total_formatted: byteSize(totalSize) }); - - try { - for (const asset of assetsToUpload) { - uploadProgress.update({ - filename: asset.path, - }); - - let skipUpload = false; - - let skipAsset = false; - let existingAssetId: string | undefined = undefined; - - if (!options.skipHash) { - const assetBulkUploadCheckDto = { assets: [{ id: asset.path, checksum: await asset.hash() }] }; - - const checkResponse = await api.checkBulkUpload(assetBulkUploadCheckDto); - - skipUpload = checkResponse.results[0].action === 'reject'; - - const isDuplicate = checkResponse.results[0].reason === 'duplicate'; - if (isDuplicate) { - existingAssetId = checkResponse.results[0].assetId; - } - - skipAsset = skipUpload && !isDuplicate; - } - - if (!skipAsset && !options.dryRun) { - if (!skipUpload) { - const formData = await asset.getUploadFormData(); - const response = await this.uploadAsset(api, formData); - const json = await response.json(); - existingAssetId = json.id; - uploadCounter++; - totalSizeUploaded += asset.fileSize; - } - - if ((options.album || options.albumName) && asset.albumName !== undefined) { - let album = existingAlbums.find((album) => album.albumName === asset.albumName); - if (!album) { - const response = await api.createAlbum({ albumName: asset.albumName }); - album = response; - existingAlbums.push(album); - } - - if (existingAssetId) { - await api.addAssetsToAlbum(album.id, { - ids: [existingAssetId], - }); - } - } - } - - sizeSoFar += asset.fileSize; - - uploadProgress.update(sizeSoFar, { value_formatted: byteSize(sizeSoFar) }); - } - } finally { - uploadProgress.stop(); - } - - const messageStart = options.dryRun ? 'Would have' : 'Successfully'; - - if (uploadCounter === 0) { - console.log('All assets were already uploaded, nothing to do.'); - } else { - console.log(`${messageStart} uploaded ${uploadCounter} assets (${byteSize(totalSizeUploaded)})`); - } - if (options.delete) { - if (options.dryRun) { - console.log(`Would now have deleted assets, but skipped due to dry run`); - } else { - console.log('Deleting assets that have been uploaded...'); - const deletionProgress = new cliProgress.SingleBar(cliProgress.Presets.shades_classic); - deletionProgress.start(files.length, 0); - - for (const asset of assetsToUpload) { - if (!options.dryRun) { - await asset.delete(); - } - deletionProgress.increment(); - } - deletionProgress.stop(); - console.log('Deletion complete'); - } - } } - private async uploadAsset(api: ImmichApi, data: FormData): Promise { - const url = api.instanceUrl + '/asset/upload'; + private async uploadAsset(data: FormData): Promise<{ id: string }> { + const url = this.api.instanceUrl + '/asset/upload'; const response = await fetch(url, { method: 'post', redirect: 'error', headers: { - 'x-api-key': api.apiKey, + 'x-api-key': this.api.apiKey, }, body: data, }); if (response.status !== 200 && response.status !== 201) { throw new Error(await response.text()); } - return response; + return response.json(); } } diff --git a/cli/src/index.ts b/cli/src/index.ts index eead2b0272..1aab0386a1 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -1,7 +1,7 @@ #! /usr/bin/env node import { Command, Option } from 'commander'; -import path from 'node:path'; import os from 'node:os'; +import path from 'node:path'; import { version } from '../package.json'; import { LoginCommand } from './commands/login.command'; import { LogoutCommand } from './commands/logout.command'; @@ -15,7 +15,7 @@ const program = new Command() .version(version) .description('Command line interface for Immich') .addOption( - new Option('-d, --config-directory', 'Configuration directory where auth.yml will be stored') + new Option('-d, --config-directory ', 'Configuration directory where auth.yml will be stored') .env('IMMICH_CONFIG_DIR') .default(defaultConfigDirectory), ); @@ -43,6 +43,11 @@ program .env('IMMICH_DRY_RUN') .default(false), ) + .addOption( + new Option('-c, --concurrency', 'Number of assets to upload at the same time') + .env('IMMICH_UPLOAD_CONCURRENCY') + .default(4), + ) .addOption(new Option('--delete', 'Delete local assets after upload').env('IMMICH_DELETE_ASSETS')) .argument('[paths...]', 'One or more paths to assets to be uploaded') .action(async (paths, options) => { @@ -60,10 +65,10 @@ program program .command('login-key') .description('Login using an API key') - .argument('[instanceUrl]') - .argument('[apiKey]') - .action(async (paths, options) => { - await new LoginCommand(program.opts()).run(paths, options); + .argument('url') + .argument('key') + .action(async (url, key) => { + await new LoginCommand(program.opts()).run(url, key); }); program diff --git a/cli/src/services/api.service.ts b/cli/src/services/api.service.ts index ef76a75441..089eda1201 100644 --- a/cli/src/services/api.service.ts +++ b/cli/src/services/api.service.ts @@ -1,4 +1,11 @@ import { + ApiKeyCreateDto, + AssetBulkUploadCheckDto, + BulkIdsDto, + CreateAlbumDto, + CreateAssetDto, + LoginCredentialDto, + SignUpDto, addAssetsToAlbum, checkBulkUpload, createAlbum, @@ -13,13 +20,6 @@ import { pingServer, signUpAdmin, uploadFile, - ApiKeyCreateDto, - AssetBulkUploadCheckDto, - BulkIdsDto, - CreateAlbumDto, - CreateAssetDto, - LoginCredentialDto, - SignUpDto, } from '@immich/sdk'; /** diff --git a/cli/src/services/crawl.service.spec.ts b/cli/src/services/crawl.service.spec.ts index 003935944a..93879f21e0 100644 --- a/cli/src/services/crawl.service.spec.ts +++ b/cli/src/services/crawl.service.spec.ts @@ -1,5 +1,5 @@ import mockfs from 'mock-fs'; -import { CrawlService, CrawlOptions } from './crawl.service'; +import { CrawlOptions, CrawlService } from './crawl.service'; interface Test { test: string; diff --git a/cli/src/services/session.service.spec.ts b/cli/src/services/session.service.spec.ts index f6ef709bfc..c217ab4e6a 100644 --- a/cli/src/services/session.service.spec.ts +++ b/cli/src/services/session.service.spec.ts @@ -1,16 +1,40 @@ -import { SessionService } from './session.service'; import fs from 'node:fs'; +import path from 'node:path'; import yaml from 'yaml'; -import { - TEST_AUTH_FILE, - TEST_CONFIG_DIR, - TEST_IMMICH_API_KEY, - TEST_IMMICH_INSTANCE_URL, - createTestAuthFile, - deleteAuthFile, - readTestAuthFile, - spyOnConsole, -} from '../../test/cli-test-utils'; +import { SessionService } from './session.service'; + +const TEST_CONFIG_DIR = '/tmp/immich/'; +const TEST_AUTH_FILE = path.join(TEST_CONFIG_DIR, 'auth.yml'); +const TEST_IMMICH_INSTANCE_URL = 'https://test/api'; +const TEST_IMMICH_API_KEY = 'pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg'; + +const spyOnConsole = () => vi.spyOn(console, 'log').mockImplementation(() => {}); + +const createTestAuthFile = async (contents: string) => { + if (!fs.existsSync(TEST_CONFIG_DIR)) { + // Create config folder if it doesn't exist + const created = await fs.promises.mkdir(TEST_CONFIG_DIR, { recursive: true }); + if (!created) { + throw new Error(`Failed to create config folder ${TEST_CONFIG_DIR}`); + } + } + + fs.writeFileSync(TEST_AUTH_FILE, contents); +}; + +const readTestAuthFile = async (): Promise => { + return await fs.promises.readFile(TEST_AUTH_FILE, 'utf8'); +}; + +const deleteAuthFile = () => { + try { + fs.unlinkSync(TEST_AUTH_FILE); + } catch (error: any) { + if (error.code !== 'ENOENT') { + throw error; + } + } +}; const mocks = vi.hoisted(() => { return { diff --git a/cli/test/cli-test-utils.ts b/cli/test/cli-test-utils.ts deleted file mode 100644 index cc3e29d27d..0000000000 --- a/cli/test/cli-test-utils.ts +++ /dev/null @@ -1,52 +0,0 @@ -import fs from 'node:fs'; -import path from 'node:path'; -import { ImmichApi } from 'src/services/api.service'; - -export const TEST_CONFIG_DIR = '/tmp/immich/'; -export const TEST_AUTH_FILE = path.join(TEST_CONFIG_DIR, 'auth.yml'); -export const TEST_IMMICH_INSTANCE_URL = 'https://test/api'; -export const TEST_IMMICH_API_KEY = 'pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg'; - -export const CLI_BASE_OPTIONS = { configDirectory: TEST_CONFIG_DIR }; - -export const setup = async () => { - const api = new ImmichApi(process.env.IMMICH_INSTANCE_URL as string, ''); - await api.signUpAdmin({ email: 'cli@immich.app', password: 'password', name: 'Administrator' }); - const admin = await api.login({ email: 'cli@immich.app', password: 'password' }); - const apiKey = await api.createApiKey( - { name: 'CLI Test' }, - { headers: { Authorization: `Bearer ${admin.accessToken}` } }, - ); - - api.setApiKey(apiKey.secret); - - return api; -}; - -export const spyOnConsole = () => vi.spyOn(console, 'log').mockImplementation(() => {}); - -export const createTestAuthFile = async (contents: string) => { - if (!fs.existsSync(TEST_CONFIG_DIR)) { - // Create config folder if it doesn't exist - const created = await fs.promises.mkdir(TEST_CONFIG_DIR, { recursive: true }); - if (!created) { - throw new Error(`Failed to create config folder ${TEST_CONFIG_DIR}`); - } - } - - fs.writeFileSync(TEST_AUTH_FILE, contents); -}; - -export const readTestAuthFile = async (): Promise => { - return await fs.promises.readFile(TEST_AUTH_FILE, 'utf8'); -}; - -export const deleteAuthFile = () => { - try { - fs.unlinkSync(TEST_AUTH_FILE); - } catch (error: any) { - if (error.code !== 'ENOENT') { - throw error; - } - } -}; diff --git a/cli/test/e2e/login-key.e2e-spec.ts b/cli/test/e2e/login-key.e2e-spec.ts deleted file mode 100644 index acbce5c244..0000000000 --- a/cli/test/e2e/login-key.e2e-spec.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { restoreTempFolder, testApp } from '@test-utils'; -import { CLI_BASE_OPTIONS, TEST_AUTH_FILE, deleteAuthFile, setup, spyOnConsole } from 'test/cli-test-utils'; -import { readFile, stat } from 'node:fs/promises'; -import { LoginCommand } from '../../src/commands/login.command'; -import yaml from 'yaml'; - -describe(`login-key (e2e)`, () => { - let apiKey: string; - let instanceUrl: string; - - spyOnConsole(); - - beforeAll(async () => { - await testApp.create(); - if (process.env.IMMICH_INSTANCE_URL) { - instanceUrl = process.env.IMMICH_INSTANCE_URL; - } else { - throw new Error('IMMICH_INSTANCE_URL environment variable not set'); - } - }); - - afterAll(async () => { - await testApp.teardown(); - await restoreTempFolder(); - deleteAuthFile(); - }); - - beforeEach(async () => { - await testApp.reset(); - await restoreTempFolder(); - - const api = await setup(); - apiKey = api.apiKey; - - deleteAuthFile(); - }); - - it('should error when providing an invalid API key', async () => { - await expect(new LoginCommand(CLI_BASE_OPTIONS).run(instanceUrl, 'invalid')).rejects.toThrow( - `Failed to connect to server ${instanceUrl}: Error: 401`, - ); - }); - - it('should log in when providing the correct API key', async () => { - await new LoginCommand(CLI_BASE_OPTIONS).run(instanceUrl, apiKey); - }); - - it('should create an auth file when logging in', async () => { - await new LoginCommand(CLI_BASE_OPTIONS).run(instanceUrl, apiKey); - - const data: string = await readFile(TEST_AUTH_FILE, 'utf8'); - const parsedConfig = yaml.parse(data); - - expect(parsedConfig).toEqual(expect.objectContaining({ instanceUrl, apiKey })); - }); - - it('should create an auth file with chmod 600', async () => { - await new LoginCommand(CLI_BASE_OPTIONS).run(instanceUrl, apiKey); - - const stats = await stat(TEST_AUTH_FILE); - const mode = (stats.mode & 0o777).toString(8); - - expect(mode).toEqual('600'); - }); -}); diff --git a/cli/test/e2e/server-info.e2e-spec.ts b/cli/test/e2e/server-info.e2e-spec.ts deleted file mode 100644 index c0b10813a6..0000000000 --- a/cli/test/e2e/server-info.e2e-spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { restoreTempFolder, testApp } from '@test-utils'; -import { CLI_BASE_OPTIONS, setup, spyOnConsole } from 'test/cli-test-utils'; -import { ServerInfoCommand } from '../../src/commands/server-info.command'; - -describe(`server-info (e2e)`, () => { - const consoleSpy = spyOnConsole(); - - beforeAll(async () => { - await testApp.create(); - }); - - afterAll(async () => { - await testApp.teardown(); - await restoreTempFolder(); - }); - - beforeEach(async () => { - await testApp.reset(); - await restoreTempFolder(); - const api = await setup(); - process.env.IMMICH_API_KEY = api.apiKey; - }); - - it('should show server version', async () => { - await new ServerInfoCommand(CLI_BASE_OPTIONS).run(); - - expect(consoleSpy.mock.calls).toEqual([ - [expect.stringMatching(new RegExp('Server Version: \\d+.\\d+.\\d+'))], - [expect.stringMatching('Image Types: .*')], - [expect.stringMatching('Video Types: .*')], - ['Statistics:\n Images: 0\n Videos: 0\n Total: 0'], - ]); - }); -}); diff --git a/cli/test/e2e/setup.ts b/cli/test/e2e/setup.ts deleted file mode 100644 index fb1d939eba..0000000000 --- a/cli/test/e2e/setup.ts +++ /dev/null @@ -1,42 +0,0 @@ -import path from 'node:path'; -import { PostgreSqlContainer } from '@testcontainers/postgresql'; -import { access } from 'node:fs/promises'; - -export const directoryExists = (directory: string) => - access(directory) - .then(() => true) - .catch(() => false); - -export default async () => { - let IMMICH_TEST_ASSET_PATH: string = ''; - - if (process.env.IMMICH_TEST_ASSET_PATH === undefined) { - IMMICH_TEST_ASSET_PATH = path.normalize(`${__dirname}/../../../server/test/assets/`); - process.env.IMMICH_TEST_ASSET_PATH = IMMICH_TEST_ASSET_PATH; - } else { - IMMICH_TEST_ASSET_PATH = process.env.IMMICH_TEST_ASSET_PATH; - } - - if (!(await directoryExists(`${IMMICH_TEST_ASSET_PATH}/albums`))) { - throw new Error( - `Test assets not found. Please checkout https://github.com/immich-app/test-assets into ${IMMICH_TEST_ASSET_PATH} before testing`, - ); - } - - if (process.env.DB_HOSTNAME === undefined) { - // DB hostname not set which likely means we're not running e2e through docker compose. Start a local postgres container. - const pg = await new PostgreSqlContainer('tensorchord/pgvecto-rs:pg14-v0.2.0') - .withExposedPorts(5432) - .withDatabase('immich') - .withUsername('postgres') - .withPassword('postgres') - .withReuse() - .start(); - - process.env.DB_URL = pg.getConnectionUri(); - } - - process.env.NODE_ENV = 'development'; - process.env.IMMICH_CONFIG_FILE = path.normalize(`${__dirname}/../../../server/e2e/jobs/immich-e2e-config.json`); - process.env.TZ = 'Z'; -}; diff --git a/cli/test/e2e/upload.e2e-spec.ts b/cli/test/e2e/upload.e2e-spec.ts deleted file mode 100644 index 81b20ad749..0000000000 --- a/cli/test/e2e/upload.e2e-spec.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { IMMICH_TEST_ASSET_PATH, restoreTempFolder, testApp } from '@test-utils'; -import { CLI_BASE_OPTIONS, setup, spyOnConsole } from 'test/cli-test-utils'; -import { UploadCommand } from '../../src/commands/upload.command'; -import { ImmichApi } from 'src/services/api.service'; - -describe(`upload (e2e)`, () => { - let api: ImmichApi; - - spyOnConsole(); - - beforeAll(async () => { - await testApp.create(); - }); - - afterAll(async () => { - await testApp.teardown(); - await restoreTempFolder(); - }); - - beforeEach(async () => { - await testApp.reset(); - await restoreTempFolder(); - api = await setup(); - process.env.IMMICH_API_KEY = api.apiKey; - }); - - it('should upload a folder recursively', async () => { - await new UploadCommand(CLI_BASE_OPTIONS).run([`${IMMICH_TEST_ASSET_PATH}/albums/nature/`], { recursive: true }); - const assets = await api.getAllAssets(); - expect(assets.length).toBeGreaterThan(4); - }); - - it('should not create a new album', async () => { - await new UploadCommand(CLI_BASE_OPTIONS).run([`${IMMICH_TEST_ASSET_PATH}/albums/nature/`], { recursive: true }); - const albums = await api.getAllAlbums(); - expect(albums.length).toEqual(0); - }); - - it('should create album from folder name', async () => { - await new UploadCommand(CLI_BASE_OPTIONS).run([`${IMMICH_TEST_ASSET_PATH}/albums/nature/`], { - recursive: true, - album: true, - }); - - const albums = await api.getAllAlbums(); - expect(albums.length).toEqual(1); - const natureAlbum = albums[0]; - expect(natureAlbum.albumName).toEqual('nature'); - }); - - it('should add existing assets to album', async () => { - await new UploadCommand(CLI_BASE_OPTIONS).run([`${IMMICH_TEST_ASSET_PATH}/albums/nature/`], { - recursive: true, - }); - - // upload again, but this time add to album - await new UploadCommand(CLI_BASE_OPTIONS).run([`${IMMICH_TEST_ASSET_PATH}/albums/nature/`], { - recursive: true, - album: true, - }); - - const albums = await api.getAllAlbums(); - expect(albums.length).toEqual(1); - const natureAlbum = albums[0]; - expect(natureAlbum.albumName).toEqual('nature'); - }); - - it('should upload to the specified album name', async () => { - await new UploadCommand(CLI_BASE_OPTIONS).run([`${IMMICH_TEST_ASSET_PATH}/albums/nature/`], { - recursive: true, - albumName: 'testAlbum', - }); - - const albums = await api.getAllAlbums(); - expect(albums.length).toEqual(1); - const testAlbum = albums[0]; - expect(testAlbum.albumName).toEqual('testAlbum'); - }); -}); diff --git a/cli/test/e2e/vitest.config.ts b/cli/test/e2e/vitest.config.ts deleted file mode 100644 index 1657938765..0000000000 --- a/cli/test/e2e/vitest.config.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { defineConfig } from 'vitest/config'; - -export default defineConfig({ - resolve: { - alias: { - '@test-utils': new URL('../../../server/dist/test-utils/utils.js', import.meta.url).pathname, - }, - }, - test: { - include: ['**/*.e2e-spec.ts'], - globals: true, - globalSetup: 'test/e2e/setup.ts', - pool: 'forks', - poolOptions: { - forks: { - maxForks: 1, - minForks: 1, - }, - }, - testTimeout: 10_000, - }, -}); diff --git a/cli/test/global-setup.js b/cli/test/global-setup.js deleted file mode 100644 index 6e1fbf41d0..0000000000 --- a/cli/test/global-setup.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = async () => { - process.env.TZ = 'UTC'; -}; diff --git a/cli/testSetup.js b/cli/testSetup.js deleted file mode 100644 index 23a5890d18..0000000000 --- a/cli/testSetup.js +++ /dev/null @@ -1,3 +0,0 @@ -// add all jest-extended matchers -import * as matchers from 'jest-extended'; -expect.extend(matchers); diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 188b8c1234..d344329a50 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -2,7 +2,7 @@ # - https://immich.app/docs/developer/setup # - https://immich.app/docs/developer/troubleshooting -version: "3.8" +version: '3.8' name: immich-dev @@ -30,7 +30,7 @@ x-server-build: &server-common services: immich-server: container_name: immich_server - command: [ "/usr/src/app/bin/immich-dev", "immich" ] + command: ['/usr/src/app/bin/immich-dev', 'immich'] <<: *server-common ports: - 3001:3001 @@ -41,7 +41,7 @@ services: immich-microservices: container_name: immich_microservices - command: [ "/usr/src/app/bin/immich-dev", "microservices" ] + command: ['/usr/src/app/bin/immich-dev', 'microservices'] <<: *server-common # extends: # file: hwaccel.transcoding.yml @@ -57,7 +57,7 @@ services: image: immich-web-dev:latest build: context: ../web - command: "/usr/src/app/bin/immich-web" + command: ['/usr/src/app/bin/immich-web'] env_file: - .env ports: diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index 48a526c4c1..b0a19274d0 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -1,4 +1,4 @@ -version: "3.8" +version: '3.8' name: immich-prod @@ -17,7 +17,7 @@ x-server-build: &server-common services: immich-server: container_name: immich_server - command: [ "./start-server.sh" ] + command: ['start.sh', 'immich'] <<: *server-common ports: - 2283:3001 @@ -27,7 +27,7 @@ services: immich-microservices: container_name: immich_microservices - command: [ "./start-microservices.sh" ] + command: ['start.sh', 'microservices'] <<: *server-common # extends: # file: hwaccel.transcoding.yml diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index f1d16f7e6b..46b4a44a82 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,4 +1,4 @@ -version: "3.8" +version: '3.8' # # WARNING: Make sure to use the docker-compose.yml of the current release: @@ -14,7 +14,7 @@ services: immich-server: container_name: immich_server image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release} - command: [ "start.sh", "immich" ] + command: ['start.sh', 'immich'] volumes: - ${UPLOAD_LOCATION}:/usr/src/app/upload - /etc/localtime:/etc/localtime:ro @@ -33,7 +33,7 @@ services: # extends: # uncomment this section for hardware acceleration - see https://immich.app/docs/features/hardware-transcoding # file: hwaccel.transcoding.yml # service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding - command: [ "start.sh", "microservices" ] + command: ['start.sh', 'microservices'] volumes: - ${UPLOAD_LOCATION}:/usr/src/app/upload - /etc/localtime:/etc/localtime:ro @@ -60,12 +60,12 @@ services: redis: container_name: immich_redis - image: redis:6.2-alpine@sha256:51d6c56749a4243096327e3fb964a48ed92254357108449cb6e23999c37773c5 + image: registry.hub.docker.com/library/redis:6.2-alpine@sha256:51d6c56749a4243096327e3fb964a48ed92254357108449cb6e23999c37773c5 restart: always database: container_name: immich_postgres - image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0 + image: registry.hub.docker.com/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0 environment: POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_USER: ${DB_USERNAME} diff --git a/docker/hwaccel.transcoding.yml b/docker/hwaccel.transcoding.yml index f5585d815a..2f6ae3ebde 100644 --- a/docker/hwaccel.transcoding.yml +++ b/docker/hwaccel.transcoding.yml @@ -38,12 +38,6 @@ services: - /dev/dri:/dev/dri - /dev/dma_heap:/dev/dma_heap - /dev/mpp_service:/dev/mpp_service - volumes: - - /usr/bin/ffmpeg:/usr/bin/ffmpeg_mpp:ro - - /lib/aarch64-linux-gnu:/lib/ffmpeg-mpp:ro - - /lib/aarch64-linux-gnu/libblas.so.3:/lib/ffmpeg-mpp/libblas.so.3:ro # symlink is resolved by mounting - - /lib/aarch64-linux-gnu/liblapack.so.3:/lib/ffmpeg-mpp/liblapack.so.3:ro # symlink is resolved by mounting - - /lib/aarch64-linux-gnu/pulseaudio/libpulsecommon-15.99.so:/lib/ffmpeg-mpp/libpulsecommon-15.99.so:ro vaapi: devices: diff --git a/docs/docs/administration/oauth.md b/docs/docs/administration/oauth.md index c1ac3c5770..66a752f0b5 100644 --- a/docs/docs/administration/oauth.md +++ b/docs/docs/administration/oauth.md @@ -67,9 +67,11 @@ Once you have a new OAuth client application configured, Immich can be configure | Client Secret | string | (required) | Required. Client Secret (previous step) | | Scope | string | openid email profile | Full list of scopes to send with the request (space delimited) | | Signing Algorithm | string | RS256 | The algorithm used to sign the id token (examples: RS256, HS256) | +| Storage Label Claim | string | preferred_username | Claim mapping for the user's storage label | +| Storage Quota Claim | string | immich_quota | Claim mapping for the user's storage | +| Default Storage Quota (GiB) | number | 0 | Default quota for user without storage quota claim (Enter 0 for unlimited quota) | | Button Text | string | Login with OAuth | Text for the OAuth button on the web | | Auto Register | boolean | true | When true, will automatically register a user the first time they sign in | -| Storage Claim | string | preferred_username | Claim mapping for the user's storage label | | [Auto Launch](#auto-launch) | boolean | false | When true, will skip the login page and automatically start the OAuth login process | | [Mobile Redirect URI Override](#mobile-redirect-uri) | URL | (empty) | Http(s) alternative mobile redirect URI | diff --git a/docs/docs/administration/reverse-proxy.md b/docs/docs/administration/reverse-proxy.md index 66f4d7b9f2..24919347f0 100644 --- a/docs/docs/administration/reverse-proxy.md +++ b/docs/docs/administration/reverse-proxy.md @@ -44,22 +44,13 @@ Below is an example config for Apache2 site configuration. ``` - ServerName + ServerName + ProxyRequests Off + ProxyPass / http://127.0.0.1:2283/ timeout=600 upgrade=websocket + ProxyPassReverse / http://127.0.0.1:2283/ + ProxyPreserveHost On - ProxyRequests off - ProxyVia on - - RewriteEngine On - RewriteCond %{REQUEST_URI} ^/api/socket.io [NC] - RewriteCond %{QUERY_STRING} transport=websocket [NC] - RewriteRule /(.*) ws://localhost:2283/$1 [P,L] - - ProxyPass /api/socket.io ws://localhost:2283/api/socket.io - ProxyPassReverse /api/socket.io ws://localhost:2283/api/socket.io - - - ProxyPass http://localhost:2283/ - ProxyPassReverse http://localhost:2283/ - ``` + +**timeout:** is measured in seconds, and it is particularly useful when long operations are triggered (i.e. Repair), so the server doesn't return an error. diff --git a/docs/docs/developer/pr-checklist.md b/docs/docs/developer/pr-checklist.md index 59aaa4fc75..32d9b03507 100644 --- a/docs/docs/developer/pr-checklist.md +++ b/docs/docs/developer/pr-checklist.md @@ -7,7 +7,7 @@ When contributing code through a pull request, please check the following: - [ ] `npm run lint` (linting via ESLint) - [ ] `npm run format` (formatting via Prettier) - [ ] `npm run check:svelte` (Type checking via SvelteKit) -- [ ] `npm test` (Tests via Jest) +- [ ] `npm test` (unit tests) :::tip Run all web checks with `npm run check:all` @@ -18,7 +18,7 @@ Run all web checks with `npm run check:all` - [ ] `npm run lint` (linting via ESLint) - [ ] `npm run format` (formatting via Prettier) - [ ] `npm run check` (Type checking via `tsc`) -- [ ] `npm test` (Tests via Jest) +- [ ] `npm test` (unit tests) :::tip Run all server checks with `npm run check:all` diff --git a/docs/docs/features/libraries.md b/docs/docs/features/libraries.md index 6dcb9982bf..58dd707ea0 100644 --- a/docs/docs/features/libraries.md +++ b/docs/docs/features/libraries.md @@ -42,23 +42,24 @@ Finally, files can be deleted from Immich via the `Remove Offline Files` job. Th ### Import Paths -External libraries use import paths to determine which files to scan. Each library can have multiple import paths so that files from different locations can be added to the same library. Import paths are scanned recursively, and if a file is in multiple import paths, it will only be added once. If the import paths are edited in a way that an external file is no longer in any import path, it will be removed from the library in the same way a deleted file would. If the file is moved back to an import path, it will be added again as if it was a new file. +External libraries use import paths to determine which files to scan. Each library can have multiple import paths so that files from different locations can be added to the same library. Import paths are scanned recursively, and if a file is in multiple import paths, it will only be added once. Each import file must be a readable directory that exists on the filesystem; the import path dialog will alert you of any paths that are not accessible. + +If the import paths are edited in a way that an external file is no longer in any import path, it will be removed from the library in the same way a deleted file would. If the file is moved back to an import path, it will be added again as if it was a new file. ### Troubleshooting -Sometimes, an external library will not scan correctly. This can happen if the immich_server or immich_microservices can't access the files. Here are some things to check: +Sometimes, an external library will not scan correctly. This can happen if immich_server or immich_microservices can't access the files. Here are some things to check: -- Is the external path set correctly? +- Is the external path set correctly? Each import path must be contained in the external path. +- Make sure the external path does not contain spaces - In the docker-compose file, are the volumes mounted correctly? - Are the volumes identical between the `server` and `microservices` container? - Are the import paths set correctly, and do they match the path set in docker-compose file? -- Are you using symbolic link in your import library? +- Make sure you don't use symlinks in your import libraries, and that you aren't linking across docker mounts. - Are the permissions set correctly? -- Are you using forward slashes everywhere? (`/`) -- Are you using symlink across docker mounts? -- Are you using [spaces in the internal path](/docs/features/libraries#:~:text=can%20be%20accessed.-,NOTE,-Spaces%20in%20the)? +- Make sure you are using forward slashes (`/`) and not backward slashes. -If all else fails, you can always start a shell inside the container and check if the path is accessible. For example, `docker exec -it immich_microservices /bin/bash` will start a bash shell. If your import path, for instance, is `/data/import/photos`, you can check if the files are accessible by running `ls /data/import/photos`. Also check the `immich_server` container in the same way. +To validate that Immich can reach your external library, start a shell inside the container. Run `docker exec -it immich_microservices /bin/bash` to a bash shell. If your import path is `/data/import/photos`, check it with `ls /data/import/photos`. Do the same check for the `immich_server` container. If you cannot access this directory in both the `microservices` and `server` containers, Immich won't be able to import files. ### Security Considerations @@ -87,10 +88,7 @@ Some basic examples: This feature - currently hidden in the config file - is considered experimental and for advanced users only. If enabled, it will allow automatic watching of the filesystem which means new assets are automatically imported to Immich without needing to rescan. Deleted assets are, as always, marked as offline and can be removed with the "Remove offline files" button. -If your photos are on a network drive you will likely have to enable filesystem polling. The performance hit for polling large libraries is currently unknown, feel free to test this feature and report back. In addition to the boolean feature flag, the configuration file allows customization of the following parameters, please see the [chokidar documentation](https://github.com/paulmillr/chokidar?tab=readme-ov-file#performance) for reference. - -- `usePolling` (default: `false`). -- `interval`. (default: 10000). When using polling, this is how often (in milliseconds) the filesystem is polled. +If your photos are on a network drive, automatic file watching likely won't work. In that case, you will have to rely on a periodic library refresh to pull in your changes. ### Nightly job diff --git a/docs/docs/install/config-file.md b/docs/docs/install/config-file.md index 755722d9e7..8a7776a420 100644 --- a/docs/docs/install/config-file.md +++ b/docs/docs/install/config-file.md @@ -95,13 +95,16 @@ The default configuration looks like this: "issuerUrl": "", "clientId": "", "clientSecret": "", - "mobileOverrideEnabled": false, - "mobileRedirectUri": "", "scope": "openid email profile", + "signingAlgorithm": "RS256", "storageLabelClaim": "preferred_username", + "storageQuotaClaim": "immich_quota", + "defaultStorageQuota": 0, "buttonText": "Login with OAuth", "autoRegister": true, - "autoLaunch": false + "autoLaunch": false, + "mobileOverrideEnabled": false, + "mobileRedirectUri": "" }, "passwordLogin": { "enabled": true diff --git a/docs/docs/install/environment-variables.md b/docs/docs/install/environment-variables.md index 09fadbf707..2849c60549 100644 --- a/docs/docs/install/environment-variables.md +++ b/docs/docs/install/environment-variables.md @@ -60,14 +60,17 @@ These environment variables are used by the `docker-compose.yml` file and do **N ## Database -| Variable | Description | Default | Services | -| :------------ | :---------------- | :---------: | :-------------------- | -| `DB_URL` | Database URL | | server, microservices | -| `DB_HOSTNAME` | Database Host | `localhost` | server, microservices | -| `DB_PORT` | Database Port | `5432` | server, microservices | -| `DB_USERNAME` | Database User | `postgres` | server, microservices | -| `DB_PASSWORD` | Database Password | `postgres` | server, microservices | -| `DB_DATABASE` | Database Name | `immich` | server, microservices | +| Variable | Description | Default | Services | +| :---------------------------------- | :------------------------------------------------------------ | :----------: | :-------------------- | +| `DB_URL` | Database URL | | server, microservices | +| `DB_HOSTNAME` | Database Host | `localhost` | server, microservices | +| `DB_PORT` | Database Port | `5432` | server, microservices | +| `DB_USERNAME` | Database User | `postgres` | server, microservices | +| `DB_PASSWORD` | Database Password | `postgres` | server, microservices | +| `DB_DATABASE_NAME` | Database Name | `immich` | server, microservices | +| `DB_VECTOR_EXTENSION`\*1 | Database Vector Extension (one of [`pgvector`, `pgvecto.rs`]) | `pgvecto.rs` | server, microservices | + +\*1: This setting cannot be changed after the server has successfully started up :::info diff --git a/docs/package-lock.json b/docs/package-lock.json index b6912ac7e8..c78ca4b2e7 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -2964,9 +2964,9 @@ } }, "node_modules/@mdx-js/react": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.0.0.tgz", - "integrity": "sha512-nDctevR9KyYFyV+m+/+S4cpzCWHqj+iHDHq3QrsWezcC+B17uZdIWgCguESUkwFhM3n/56KxWVE3V6EokrmONQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.0.1.tgz", + "integrity": "sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==", "dependencies": { "@types/mdx": "^2.0.0" }, diff --git a/docs/src/pages/milestones.tsx b/docs/src/pages/milestones.tsx index 1670b9fc85..e672e03267 100644 --- a/docs/src/pages/milestones.tsx +++ b/docs/src/pages/milestones.tsx @@ -50,12 +50,22 @@ import { mdiVectorCombine, mdiVideo, mdiWeb, + mdiScaleBalance, } from '@mdi/js'; import Layout from '@theme/Layout'; import React from 'react'; import Timeline, { DateType, Item } from '../components/timeline'; const items: Item[] = [ + { + icon: mdiScaleBalance, + description: 'Immich switches to AGPLv3 license', + title: 'AGPL License', + release: 'v1.95.0', + tag: 'v1.95.0', + date: new Date(2024, 1, 20), + dateType: DateType.RELEASE, + }, { icon: mdiEyeRefreshOutline, description: 'Automatically import files in external libraries when the operating system detects changes.', diff --git a/e2e/.eslintrc.cjs b/e2e/.eslintrc.cjs new file mode 100644 index 0000000000..3989e86e54 --- /dev/null +++ b/e2e/.eslintrc.cjs @@ -0,0 +1,31 @@ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + project: 'tsconfig.json', + sourceType: 'module', + tsconfigRootDir: __dirname, + }, + plugins: ['@typescript-eslint/eslint-plugin'], + extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended', 'plugin:unicorn/recommended'], + root: true, + env: { + node: true, + }, + ignorePatterns: ['.eslintrc.js'], + rules: { + '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-floating-promises': 'error', + 'unicorn/prefer-module': 'off', + curly: 2, + 'prettier/prettier': 0, + 'unicorn/prevent-abbreviations': 'off', + 'unicorn/filename-case': 'off', + 'unicorn/no-null': 'off', + 'unicorn/prefer-top-level-await': 'off', + 'unicorn/prefer-event-target': 'off', + 'unicorn/no-thenable': 'off', + }, +}; diff --git a/e2e/.prettierignore b/e2e/.prettierignore new file mode 100644 index 0000000000..c5b339bcea --- /dev/null +++ b/e2e/.prettierignore @@ -0,0 +1,16 @@ +.DS_Store +node_modules +/build +/package +.env +.env.* +!.env.example +*.md +*.json +coverage +dist + +# Ignore files for PNPM, NPM and YARN +pnpm-lock.yaml +package-lock.json +yarn.lock diff --git a/e2e/.prettierrc b/e2e/.prettierrc new file mode 100644 index 0000000000..b0daf15ef7 --- /dev/null +++ b/e2e/.prettierrc @@ -0,0 +1,8 @@ +{ + "singleQuote": true, + "trailingComma": "all", + "printWidth": 120, + "semi": true, + "organizeImportsSkipDestructiveCodeActions": true, + "plugins": ["prettier-plugin-organize-imports"] +} diff --git a/docker/docker-compose.e2e.yml b/e2e/docker-compose.yml similarity index 72% rename from docker/docker-compose.e2e.yml rename to e2e/docker-compose.yml index 5584335966..6e2d4b78dd 100644 --- a/docker/docker-compose.e2e.yml +++ b/e2e/docker-compose.yml @@ -1,4 +1,4 @@ -version: "3.8" +version: '3.8' name: immich-e2e @@ -13,36 +13,39 @@ x-server-build: &server-common - DB_PASSWORD=postgres - DB_DATABASE_NAME=immich - REDIS_HOSTNAME=redis + - IMMICH_MACHINE_LEARNING_ENABLED=false volumes: - upload:/usr/src/app/upload + - ../server/test/assets:/data/assets depends_on: - redis - database services: immich-server: - command: [ "./start.sh", "immich" ] + container_name: immich-e2e-server + command: ['./start.sh', 'immich'] <<: *server-common ports: - 2283:3001 immich-microservices: - command: [ "./start.sh", "microservices" ] + container_name: immich-e2e-microservices + command: ['./start.sh', 'microservices'] <<: *server-common - redis: image: redis:6.2-alpine@sha256:51d6c56749a4243096327e3fb964a48ed92254357108449cb6e23999c37773c5 - restart: always database: image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0 + command: -c fsync=off -c shared_preload_libraries=vectors.so environment: POSTGRES_PASSWORD: postgres POSTGRES_USER: postgres POSTGRES_DB: immich ports: - - 5432:5432 + - 5433:5432 volumes: model-cache: diff --git a/e2e/package-lock.json b/e2e/package-lock.json index ab84dc995b..7d013eecac 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -9,37 +9,920 @@ "version": "1.0.0", "license": "GNU Affero General Public License version 3", "devDependencies": { + "@immich/cli": "file:../cli", "@immich/sdk": "file:../open-api/typescript-sdk", "@playwright/test": "^1.41.2", + "@types/luxon": "^3.4.2", "@types/node": "^20.11.17", "@types/pg": "^8.11.0", + "@types/pngjs": "^6.0.4", + "@types/supertest": "^6.0.2", + "@typescript-eslint/eslint-plugin": "^7.1.0", + "@typescript-eslint/parser": "^7.1.0", + "@vitest/coverage-v8": "^1.3.0", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-unicorn": "^51.0.1", + "exiftool-vendored": "^24.5.0", + "luxon": "^3.4.4", "pg": "^8.11.3", - "typescript": "^5.3.3" + "pngjs": "^7.0.0", + "prettier": "^3.2.5", + "prettier-plugin-organize-imports": "^3.2.4", + "socket.io-client": "^4.7.4", + "supertest": "^6.3.4", + "typescript": "^5.3.3", + "vitest": "^1.3.0" + } + }, + "../cli": { + "name": "@immich/cli", + "version": "2.0.8", + "dev": true, + "license": "GNU Affero General Public License version 3", + "dependencies": { + "lodash-es": "^4.17.21" + }, + "bin": { + "immich": "dist/index.js" + }, + "devDependencies": { + "@immich/sdk": "file:../open-api/typescript-sdk", + "@testcontainers/postgresql": "^10.7.1", + "@types/byte-size": "^8.1.0", + "@types/cli-progress": "^3.11.0", + "@types/lodash-es": "^4.17.12", + "@types/mock-fs": "^4.13.1", + "@types/node": "^20.3.1", + "@typescript-eslint/eslint-plugin": "^7.0.0", + "@typescript-eslint/parser": "^7.0.0", + "@vitest/coverage-v8": "^1.2.2", + "byte-size": "^8.1.1", + "cli-progress": "^3.12.0", + "commander": "^12.0.0", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-unicorn": "^51.0.0", + "glob": "^10.3.1", + "mock-fs": "^5.2.0", + "prettier": "^3.2.5", + "prettier-plugin-organize-imports": "^3.2.4", + "typescript": "^5.3.3", + "vite": "^5.0.12", + "vitest": "^1.2.2", + "yaml": "^2.3.1" + }, + "engines": { + "node": ">=20.0.0" } }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.92.1", + "version": "1.97.0", "dev": true, "license": "GNU Affero General Public License version 3", "devDependencies": { "@oazapfts/runtime": "^1.0.0", "@types/node": "^20.11.0", "typescript": "^5.3.3" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", + "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", + "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "peerDependencies": { - "axios": "^1.6.7" - }, - "peerDependenciesMeta": { - "axios": { - "optional": true - } + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true + }, + "node_modules/@immich/cli": { + "resolved": "../cli", + "link": true + }, "node_modules/@immich/sdk": { "resolved": "../open-api/typescript-sdk", "link": true }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", + "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@photostructure/tz-lookup": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@photostructure/tz-lookup/-/tz-lookup-9.0.2.tgz", + "integrity": "sha512-H8+tTt7ilJNkFyb+QgPnLEGUjQzGwiMb9n7lwRZNBgSKL3VZs9AkjI1E//FcwPjNafwAH932U92+xTqJiF3Bbw==", + "dev": true + }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@playwright/test": { "version": "1.41.2", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.2.tgz", @@ -55,19 +938,242 @@ "node": ">=16" } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz", + "integrity": "sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.0.tgz", + "integrity": "sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz", + "integrity": "sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.0.tgz", + "integrity": "sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.0.tgz", + "integrity": "sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.0.tgz", + "integrity": "sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.0.tgz", + "integrity": "sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.0.tgz", + "integrity": "sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.0.tgz", + "integrity": "sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.0.tgz", + "integrity": "sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.0.tgz", + "integrity": "sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.0.tgz", + "integrity": "sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.0.tgz", + "integrity": "sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", + "dev": true + }, + "node_modules/@types/cookiejar": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", + "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", + "dev": true + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/luxon": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz", + "integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==", + "dev": true + }, + "node_modules/@types/methods": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", + "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", + "dev": true + }, "node_modules/@types/node": { - "version": "20.11.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", - "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", + "version": "20.11.20", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.20.tgz", + "integrity": "sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==", "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, "node_modules/@types/pg": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.0.tgz", - "integrity": "sha512-sDAlRiBNthGjNFfvt0k6mtotoVYVQ63pA8R4EMWka7crawSR60waVYR0HAgmPRs/e2YaeJTD/43OoZ3PFw80pw==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.2.tgz", + "integrity": "sha512-G2Mjygf2jFMU/9hCaTYxJrwdObdcnuQde1gndooZSOHsNSaCehAuwc7EIuSA34Do8Jx2yZ19KtvW8P0j4EuUXw==", "dev": true, "dependencies": { "@types/node": "*", @@ -132,6 +1238,530 @@ "node": ">=12" } }, + "node_modules/@types/pngjs": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/pngjs/-/pngjs-6.0.4.tgz", + "integrity": "sha512-atAK9xLKOnxiuArxcHovmnOUUGBZOQ3f0vCf43FnoKs6XnqiambT1kkJWmdo71IR+BoXSh+CueeFR0GfH3dTlQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, + "node_modules/@types/superagent": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.3.tgz", + "integrity": "sha512-R/CfN6w2XsixLb1Ii8INfn+BT9sGPvw74OavfkW4SwY+jeUcAwLZv2+bXLJkndnimxjEBm0RPHgcjW9pLCa8cw==", + "dev": true, + "dependencies": { + "@types/cookiejar": "^2.1.5", + "@types/methods": "^1.1.4", + "@types/node": "*" + } + }, + "node_modules/@types/supertest": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.2.tgz", + "integrity": "sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg==", + "dev": true, + "dependencies": { + "@types/methods": "^1.1.4", + "@types/superagent": "^8.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.1.0.tgz", + "integrity": "sha512-j6vT/kCulhG5wBmGtstKeiVr1rdXE4nk+DT1k6trYkwlrvW9eOF5ZbgKnd/YR6PcM4uTEXa0h6Fcvf6X7Dxl0w==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "7.1.0", + "@typescript-eslint/type-utils": "7.1.0", + "@typescript-eslint/utils": "7.1.0", + "@typescript-eslint/visitor-keys": "7.1.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.1.0.tgz", + "integrity": "sha512-V1EknKUubZ1gWFjiOZhDSNToOjs63/9O0puCgGS8aDOgpZY326fzFu15QAUjwaXzRZjf/qdsdBrckYdv9YxB8w==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.1.0", + "@typescript-eslint/types": "7.1.0", + "@typescript-eslint/typescript-estree": "7.1.0", + "@typescript-eslint/visitor-keys": "7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.1.0.tgz", + "integrity": "sha512-6TmN4OJiohHfoOdGZ3huuLhpiUgOGTpgXNUPJgeZOZR3DnIpdSgtt83RS35OYNNXxM4TScVlpVKC9jyQSETR1A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.1.0", + "@typescript-eslint/visitor-keys": "7.1.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.1.0.tgz", + "integrity": "sha512-UZIhv8G+5b5skkcuhgvxYWHjk7FW7/JP5lPASMEUoliAPwIH/rxoUSQPia2cuOj9AmDZmwUl1usKm85t5VUMew==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "7.1.0", + "@typescript-eslint/utils": "7.1.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.1.0.tgz", + "integrity": "sha512-qTWjWieJ1tRJkxgZYXx6WUYtWlBc48YRxgY2JN1aGeVpkhmnopq+SUC8UEVGNXIvWH7XyuTjwALfG6bFEgCkQA==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.1.0.tgz", + "integrity": "sha512-k7MyrbD6E463CBbSpcOnwa8oXRdHzH1WiVzOipK3L5KSML92ZKgUBrTlehdi7PEIMT8k0bQixHUGXggPAlKnOQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.1.0", + "@typescript-eslint/visitor-keys": "7.1.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.1.0.tgz", + "integrity": "sha512-WUFba6PZC5OCGEmbweGpnNJytJiLG7ZvDBJJoUcX4qZYf1mGZ97mO2Mps6O2efxJcJdRNpqweCistDbZMwIVHw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "7.1.0", + "@typescript-eslint/types": "7.1.0", + "@typescript-eslint/typescript-estree": "7.1.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.1.0.tgz", + "integrity": "sha512-FhUqNWluiGNzlvnDZiXad4mZRhtghdoKW6e98GoEOYSu5cND+E39rG5KwJMUzeENwm1ztYBRqof8wMLP+wNPIA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.1.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/@vitest/coverage-v8": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.3.1.tgz", + "integrity": "sha512-UuBnkSJUNE9rdHjDCPyJ4fYuMkoMtnghes1XohYa4At0MS3OQSAo97FrbwSLRshYsXThMZy1+ybD/byK5llyIg==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.1", + "@bcoe/v8-coverage": "^0.2.3", + "debug": "^4.3.4", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^4.0.1", + "istanbul-reports": "^3.1.6", + "magic-string": "^0.30.5", + "magicast": "^0.3.3", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "1.3.1" + } + }, + "node_modules/@vitest/expect": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.3.1.tgz", + "integrity": "sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==", + "dev": true, + "dependencies": { + "@vitest/spy": "1.3.1", + "@vitest/utils": "1.3.1", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.3.1.tgz", + "integrity": "sha512-5FzF9c3jG/z5bgCnjr8j9LNq/9OxV2uEBAITOXfoe3rdZJTdO7jzThth7FXv/6b+kdY65tpRQB7WaKhNZwX+Kg==", + "dev": true, + "dependencies": { + "@vitest/utils": "1.3.1", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.3.1.tgz", + "integrity": "sha512-EF++BZbt6RZmOlE3SuTPu/NfwBF6q4ABS37HHXzs2LUVPBLx2QoY/K0fKpRChSo8eLiuxcbCVfqKgx/dplCDuQ==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.3.1.tgz", + "integrity": "sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig==", + "dev": true, + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.3.1.tgz", + "integrity": "sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==", + "dev": true, + "dependencies": { + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/batch-cluster": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/batch-cluster/-/batch-cluster-13.0.0.tgz", + "integrity": "sha512-EreW0Vi8TwovhYUHBXXRA5tthuU2ynGsZFlboyMJHCCUXYa2AjgwnE3ubBOJs2xJLcuXFJbi6c/8pH5+FVj8Og==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/buffer-writer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", @@ -141,6 +1771,925 @@ "node": ">=4" } }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001591", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001591.tgz", + "integrity": "sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chai": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ci-info": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", + "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/clean-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", + "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/clean-regexp/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true + }, + "node_modules/core-js-compat": { + "version": "3.36.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.0.tgz", + "integrity": "sha512-iV9Pd/PsgjNWBXeq8XRtWVSgz2tKAfhfvBs7qxYty+RlRd+OCksaWmOnc4JKrTc1cToXL1N0s3l/vwlxPtdElw==", + "dev": true, + "dependencies": { + "browserslist": "^4.22.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.687", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.687.tgz", + "integrity": "sha512-Ic85cOuXSP6h7KM0AIJ2hpJ98Bo4hyTUjc4yjMbkvD+8yTxEhfK9+8exT2KKYsSjnCn2tGsKVSZwE7ZgTORQCw==", + "dev": true + }, + "node_modules/engine.io-client": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", + "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==", + "dev": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", + "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", + "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-unicorn": { + "version": "51.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-51.0.1.tgz", + "integrity": "sha512-MuR/+9VuB0fydoI0nIn2RDA5WISRn4AsJyNSaNKLVwie9/ONvQhxOBbkfSICBPnzKrB77Fh6CZZXjgTt/4Latw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "@eslint-community/eslint-utils": "^4.4.0", + "@eslint/eslintrc": "^2.1.4", + "ci-info": "^4.0.0", + "clean-regexp": "^1.0.0", + "core-js-compat": "^3.34.0", + "esquery": "^1.5.0", + "indent-string": "^4.0.0", + "is-builtin-module": "^3.2.1", + "jsesc": "^3.0.2", + "pluralize": "^8.0.0", + "read-pkg-up": "^7.0.1", + "regexp-tree": "^0.1.27", + "regjsparser": "^0.10.0", + "semver": "^7.5.4", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" + }, + "peerDependencies": { + "eslint": ">=8.56.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exiftool-vendored": { + "version": "24.5.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-24.5.0.tgz", + "integrity": "sha512-uLGYfeshak3mYn2ucCsebXfNFdOpeAULlMb84wiJv+4B236n+ypgK/vr8bJgAcsIPSRJXFSz9WonvjjQYYqR3w==", + "dev": true, + "dependencies": { + "@photostructure/tz-lookup": "^9.0.1", + "@types/luxon": "^3.4.2", + "batch-cluster": "^13.0.0", + "he": "^1.2.0", + "luxon": "^3.4.4" + }, + "optionalDependencies": { + "exiftool-vendored.exe": "12.76.0", + "exiftool-vendored.pl": "12.76.0" + } + }, + "node_modules/exiftool-vendored.exe": { + "version": "12.76.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-12.76.0.tgz", + "integrity": "sha512-lbKPPs31qpjnhFiMRaVxJX+iNcJ+p0NrRSFLHHaX6KTsfMba6e5i6NykSvU3wMiafzUTef1Fen3XQ+8n1tjjNw==", + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/exiftool-vendored.pl": { + "version": "12.76.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-12.76.0.tgz", + "integrity": "sha512-4DxqgnvL71YziVoY27ZMgVfLAWDH3pQLljuV5+ffTnTPvz/BWeV+/bVFwRvDqCD3lkCWds0YfVcsycfJgbQ5fA==", + "dev": true, + "optional": true, + "os": [ + "!win32" + ] + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formidable": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "dev": true, + "dependencies": { + "dezalgo": "^1.0.4", + "hexoid": "^1.0.0", + "once": "^1.4.0", + "qs": "^6.11.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -155,18 +2704,1042 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/js-tokens": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-8.0.3.tgz", + "integrity": "sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "dev": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dev": true, + "dependencies": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/luxon": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz", + "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/magic-string": { + "version": "0.30.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", + "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/magicast": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.3.tgz", + "integrity": "sha512-ZbrP1Qxnpoes8sz47AM0z08U+jW6TyRgZzcWy3Ma3vDhJttwMwAFDMMQFobwdBxByBD46JYmxRzeF7w2+wJEuw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "source-map-js": "^1.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mlly": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.5.0.tgz", + "integrity": "sha512-NPVQvAY1xr1QoVeG0cy8yUYC7FQcOx6evl/RjT1wL5FvzPnzOysoqB/jmx/DhssT2dYa8nxECLAaFI/+gVLhDQ==", + "dev": true, + "dependencies": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.0.3", + "ufo": "^1.3.2" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/npm-run-path": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", + "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "dev": true }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/packet-reader": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==", "dev": true }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/pg": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", @@ -267,6 +3840,35 @@ "split2": "^4.1.0" } }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", + "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.2.0", + "mlly": "^1.2.0", + "pathe": "^1.1.0" + } + }, "node_modules/playwright": { "version": "1.41.2", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.2.tgz", @@ -297,6 +3899,52 @@ "node": ">=16" } }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", + "dev": true, + "engines": { + "node": ">=14.19.0" + } + }, + "node_modules/postcss": { + "version": "8.4.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", + "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/postgres-array": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", @@ -342,6 +3990,540 @@ "integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==", "dev": true }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/prettier-plugin-organize-imports": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.2.4.tgz", + "integrity": "sha512-6m8WBhIp0dfwu0SkgfOxJqh+HpdyfqSSLfKKRZSFbDuEQXDDndb8fTpRWkUrX/uBenkex3MgnVk0J3b3Y5byog==", + "dev": true, + "peerDependencies": { + "@volar/vue-language-plugin-pug": "^1.0.4", + "@volar/vue-typescript": "^1.0.4", + "prettier": ">=2.0", + "typescript": ">=2.9" + }, + "peerDependenciesMeta": { + "@volar/vue-language-plugin-pug": { + "optional": true + }, + "@volar/vue-typescript": { + "optional": true + } + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", + "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "dev": true, + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, + "node_modules/regjsparser": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.10.0.tgz", + "integrity": "sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.12.0.tgz", + "integrity": "sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.12.0", + "@rollup/rollup-android-arm64": "4.12.0", + "@rollup/rollup-darwin-arm64": "4.12.0", + "@rollup/rollup-darwin-x64": "4.12.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.12.0", + "@rollup/rollup-linux-arm64-gnu": "4.12.0", + "@rollup/rollup-linux-arm64-musl": "4.12.0", + "@rollup/rollup-linux-riscv64-gnu": "4.12.0", + "@rollup/rollup-linux-x64-gnu": "4.12.0", + "@rollup/rollup-linux-x64-musl": "4.12.0", + "@rollup/rollup-win32-arm64-msvc": "4.12.0", + "@rollup/rollup-win32-ia32-msvc": "4.12.0", + "@rollup/rollup-win32-x64-msvc": "4.12.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", + "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.2", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", + "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/socket.io-client": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.4.tgz", + "integrity": "sha512-wh+OkeF0rAVCrABWQBaEjLfb7DVPotMbu0cgWgyR0v6eA4EoVnAwcIeIbcdTE3GT/H3kbdLl7OoH2+asoDRIIg==", + "dev": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dev": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", + "dev": true + }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -351,6 +4533,268 @@ "node": ">= 10.x" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, + "node_modules/std-env": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", + "dev": true + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-literal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.0.0.tgz", + "integrity": "sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==", + "dev": true, + "dependencies": { + "js-tokens": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/superagent": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", + "integrity": "sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==", + "dev": true, + "dependencies": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^2.1.2", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.11.0", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=6.4.0 <13 || >=14" + } + }, + "node_modules/supertest": { + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.4.tgz", + "integrity": "sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw==", + "dev": true, + "dependencies": { + "methods": "^1.1.2", + "superagent": "^8.1.2" + }, + "engines": { + "node": ">=6.4.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/tinybench": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.6.0.tgz", + "integrity": "sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==", + "dev": true + }, + "node_modules/tinypool": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.2.tgz", + "integrity": "sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz", + "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typescript": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", @@ -364,12 +4808,304 @@ "node": ">=14.17" } }, + "node_modules/ufo": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.4.0.tgz", + "integrity": "sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==", + "dev": true + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", + "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vite": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.4.tgz", + "integrity": "sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==", + "dev": true, + "dependencies": { + "esbuild": "^0.19.3", + "postcss": "^8.4.35", + "rollup": "^4.2.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.3.1.tgz", + "integrity": "sha512-azbRrqRxlWTJEVbzInZCTchx0X69M/XPTCz4H+TLvlTcR/xH/3hkRqhOakT41fMJCMzXTu4UvegkZiEoJAWvng==", + "dev": true, + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/vitest": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.3.1.tgz", + "integrity": "sha512-/1QJqXs8YbCrfv/GPQ05wAZf2eakUPLPa18vkJAKE7RXOKfVHqMZZ1WlTjiwl6Gcn65M5vpNUB6EFLnEdRdEXQ==", + "dev": true, + "dependencies": { + "@vitest/expect": "1.3.1", + "@vitest/runner": "1.3.1", + "@vitest/snapshot": "1.3.1", + "@vitest/spy": "1.3.1", + "@vitest/utils": "1.3.1", + "acorn-walk": "^8.3.2", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.2", + "vite": "^5.0.0", + "vite-node": "1.3.1", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "1.3.1", + "@vitest/ui": "1.3.1", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -378,6 +5114,24 @@ "engines": { "node": ">=0.4" } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/e2e/package.json b/e2e/package.json index 122dde73e1..9f231c9ddd 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -5,18 +5,42 @@ "main": "index.js", "type": "module", "scripts": { - "test": "npx playwright test", - "build": "tsc" + "test": "vitest --config vitest.config.ts", + "test:web": "npx playwright test", + "start:web": "npx playwright test --ui", + "format": "prettier --check .", + "format:fix": "prettier --write .", + "lint": "eslint \"src/**/*.ts\" --max-warnings 0", + "lint:fix": "npm run lint -- --fix" }, "keywords": [], "author": "", "license": "GNU Affero General Public License version 3", "devDependencies": { + "@immich/cli": "file:../cli", "@immich/sdk": "file:../open-api/typescript-sdk", "@playwright/test": "^1.41.2", + "@types/luxon": "^3.4.2", "@types/node": "^20.11.17", "@types/pg": "^8.11.0", + "@types/pngjs": "^6.0.4", + "@types/supertest": "^6.0.2", + "@typescript-eslint/eslint-plugin": "^7.1.0", + "@typescript-eslint/parser": "^7.1.0", + "@vitest/coverage-v8": "^1.3.0", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-unicorn": "^51.0.1", + "exiftool-vendored": "^24.5.0", + "luxon": "^3.4.4", "pg": "^8.11.3", - "typescript": "^5.3.3" + "pngjs": "^7.0.0", + "prettier": "^3.2.5", + "prettier-plugin-organize-imports": "^3.2.4", + "socket.io-client": "^4.7.4", + "supertest": "^6.3.4", + "typescript": "^5.3.3", + "vitest": "^1.3.0" } } diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index 2ff2d92acf..65a9c78823 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -1,7 +1,7 @@ import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ - testDir: './specs/', + testDir: './src/web/specs', fullyParallel: false, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, @@ -53,8 +53,7 @@ export default defineConfig({ /* Run your local dev server before starting the tests */ webServer: { - command: - 'docker compose -f ../docker/docker-compose.e2e.yml up --build -V --remove-orphans', + command: 'docker compose up --build -V --remove-orphans', url: 'http://127.0.0.1:2283', reuseExistingServer: true, }, diff --git a/server/e2e/api/specs/activity.e2e-spec.ts b/e2e/src/api/specs/activity.e2e-spec.ts similarity index 56% rename from server/e2e/api/specs/activity.e2e-spec.ts rename to e2e/src/api/specs/activity.e2e-spec.ts index 47d2d7a199..365ad66dc4 100644 --- a/server/e2e/api/specs/activity.e2e-spec.ts +++ b/e2e/src/api/specs/activity.e2e-spec.ts @@ -1,79 +1,83 @@ -import { AlbumResponseDto, LoginResponseDto, ReactionType } from '@app/domain'; -import { ActivityController } from '@app/immich'; -import { AssetFileUploadResponseDto } from '@app/immich/api-v1/asset/response-dto/asset-file-upload-response.dto'; -import { ActivityEntity } from '@app/infra/entities'; -import { errorStub, userDto, uuidStub } from '@test/fixtures'; +import { + ActivityCreateDto, + AlbumResponseDto, + AssetFileUploadResponseDto, + LoginResponseDto, + ReactionType, + createActivity as create, + createAlbum, +} from '@immich/sdk'; +import { createUserDto, uuidDto } from 'src/fixtures'; +import { errorDto } from 'src/responses'; +import { apiUtils, app, asBearerAuth, dbUtils } from 'src/utils'; import request from 'supertest'; -import { api } from '../../client'; -import { testApp } from '../utils'; +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; -describe(`${ActivityController.name} (e2e)`, () => { - let server: any; +describe('/activity', () => { let admin: LoginResponseDto; + let nonOwner: LoginResponseDto; let asset: AssetFileUploadResponseDto; let album: AlbumResponseDto; - let nonOwner: LoginResponseDto; + + const createActivity = (dto: ActivityCreateDto, accessToken?: string) => + create({ activityCreateDto: dto }, { headers: asBearerAuth(accessToken || admin.accessToken) }); beforeAll(async () => { - server = (await testApp.create()).getHttpServer(); - await testApp.reset(); - await api.authApi.adminSignUp(server); - admin = await api.authApi.adminLogin(server); - asset = await api.assetApi.upload(server, admin.accessToken, 'example'); + apiUtils.setup(); + await dbUtils.reset(); - await api.userApi.create(server, admin.accessToken, userDto.user1); - nonOwner = await api.authApi.login(server, userDto.user1); - - album = await api.albumApi.create(server, admin.accessToken, { - albumName: 'Album 1', - assetIds: [asset.id], - sharedWithUserIds: [nonOwner.userId], - }); - }); - - afterAll(async () => { - await testApp.teardown(); + admin = await apiUtils.adminSetup(); + nonOwner = await apiUtils.userSetup(admin.accessToken, createUserDto.user1); + asset = await apiUtils.createAsset(admin.accessToken); + album = await createAlbum( + { + createAlbumDto: { + albumName: 'Album 1', + assetIds: [asset.id], + sharedWithUserIds: [nonOwner.userId], + }, + }, + { headers: asBearerAuth(admin.accessToken) }, + ); }); beforeEach(async () => { - await testApp.reset({ entities: [ActivityEntity] }); + await dbUtils.reset(['activity']); }); describe('GET /activity', () => { it('should require authentication', async () => { - const { status, body } = await request(server).get('/activity'); + const { status, body } = await request(app).get('/activity'); expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); + expect(body).toEqual(errorDto.unauthorized); }); it('should require an albumId', async () => { - const { status, body } = await request(server) - .get('/activity') - .set('Authorization', `Bearer ${admin.accessToken}`); + const { status, body } = await request(app).get('/activity').set('Authorization', `Bearer ${admin.accessToken}`); expect(status).toEqual(400); - expect(body).toEqual(errorStub.badRequest(expect.arrayContaining(['albumId must be a UUID']))); + expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['albumId must be a UUID']))); }); it('should reject an invalid albumId', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/activity') - .query({ albumId: uuidStub.invalid }) + .query({ albumId: uuidDto.invalid }) .set('Authorization', `Bearer ${admin.accessToken}`); expect(status).toEqual(400); - expect(body).toEqual(errorStub.badRequest(expect.arrayContaining(['albumId must be a UUID']))); + expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['albumId must be a UUID']))); }); it('should reject an invalid assetId', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/activity') - .query({ albumId: uuidStub.notFound, assetId: uuidStub.invalid }) + .query({ albumId: uuidDto.notFound, assetId: uuidDto.invalid }) .set('Authorization', `Bearer ${admin.accessToken}`); expect(status).toEqual(400); - expect(body).toEqual(errorStub.badRequest(expect.arrayContaining(['assetId must be a UUID']))); + expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['assetId must be a UUID']))); }); it('should start off empty', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/activity') .query({ albumId: album.id }) .set('Authorization', `Bearer ${admin.accessToken}`); @@ -82,22 +86,22 @@ describe(`${ActivityController.name} (e2e)`, () => { }); it('should filter by album id', async () => { - const album2 = await api.albumApi.create(server, admin.accessToken, { - albumName: 'Album 2', - assetIds: [asset.id], - }); + const album2 = await createAlbum( + { + createAlbumDto: { + albumName: 'Album 2', + assetIds: [asset.id], + }, + }, + { headers: asBearerAuth(admin.accessToken) }, + ); + const [reaction] = await Promise.all([ - api.activityApi.create(server, admin.accessToken, { - albumId: album.id, - type: ReactionType.LIKE, - }), - api.activityApi.create(server, admin.accessToken, { - albumId: album2.id, - type: ReactionType.LIKE, - }), + createActivity({ albumId: album.id, type: ReactionType.Like }), + createActivity({ albumId: album2.id, type: ReactionType.Like }), ]); - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/activity') .query({ albumId: album.id }) .set('Authorization', `Bearer ${admin.accessToken}`); @@ -108,15 +112,15 @@ describe(`${ActivityController.name} (e2e)`, () => { it('should filter by type=comment', async () => { const [reaction] = await Promise.all([ - api.activityApi.create(server, admin.accessToken, { + createActivity({ albumId: album.id, - type: ReactionType.COMMENT, + type: ReactionType.Comment, comment: 'comment', }), - api.activityApi.create(server, admin.accessToken, { albumId: album.id, type: ReactionType.LIKE }), + createActivity({ albumId: album.id, type: ReactionType.Like }), ]); - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/activity') .query({ albumId: album.id, type: 'comment' }) .set('Authorization', `Bearer ${admin.accessToken}`); @@ -127,15 +131,15 @@ describe(`${ActivityController.name} (e2e)`, () => { it('should filter by type=like', async () => { const [reaction] = await Promise.all([ - api.activityApi.create(server, admin.accessToken, { albumId: album.id, type: ReactionType.LIKE }), - api.activityApi.create(server, admin.accessToken, { + createActivity({ albumId: album.id, type: ReactionType.Like }), + createActivity({ albumId: album.id, - type: ReactionType.COMMENT, + type: ReactionType.Comment, comment: 'comment', }), ]); - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/activity') .query({ albumId: album.id, type: 'like' }) .set('Authorization', `Bearer ${admin.accessToken}`); @@ -145,19 +149,17 @@ describe(`${ActivityController.name} (e2e)`, () => { }); it('should filter by userId', async () => { - const [reaction] = await Promise.all([ - api.activityApi.create(server, admin.accessToken, { albumId: album.id, type: ReactionType.LIKE }), - ]); + const [reaction] = await Promise.all([createActivity({ albumId: album.id, type: ReactionType.Like })]); - const response1 = await request(server) + const response1 = await request(app) .get('/activity') - .query({ albumId: album.id, userId: uuidStub.notFound }) + .query({ albumId: album.id, userId: uuidDto.notFound }) .set('Authorization', `Bearer ${admin.accessToken}`); expect(response1.status).toEqual(200); expect(response1.body.length).toBe(0); - const response2 = await request(server) + const response2 = await request(app) .get('/activity') .query({ albumId: album.id, userId: admin.userId }) .set('Authorization', `Bearer ${admin.accessToken}`); @@ -169,15 +171,15 @@ describe(`${ActivityController.name} (e2e)`, () => { it('should filter by assetId', async () => { const [reaction] = await Promise.all([ - api.activityApi.create(server, admin.accessToken, { + createActivity({ albumId: album.id, assetId: asset.id, - type: ReactionType.LIKE, + type: ReactionType.Like, }), - api.activityApi.create(server, admin.accessToken, { albumId: album.id, type: ReactionType.LIKE }), + createActivity({ albumId: album.id, type: ReactionType.Like }), ]); - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/activity') .query({ albumId: album.id, assetId: asset.id }) .set('Authorization', `Bearer ${admin.accessToken}`); @@ -189,34 +191,38 @@ describe(`${ActivityController.name} (e2e)`, () => { describe('POST /activity', () => { it('should require authentication', async () => { - const { status, body } = await request(server).post('/activity'); + const { status, body } = await request(app).post('/activity'); expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); + expect(body).toEqual(errorDto.unauthorized); }); it('should require an albumId', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .post('/activity') .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ albumId: uuidStub.invalid }); + .send({ albumId: uuidDto.invalid }); expect(status).toEqual(400); - expect(body).toEqual(errorStub.badRequest(expect.arrayContaining(['albumId must be a UUID']))); + expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['albumId must be a UUID']))); }); it('should require a comment when type is comment', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .post('/activity') .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ albumId: uuidStub.notFound, type: 'comment', comment: null }); + .send({ albumId: uuidDto.notFound, type: 'comment', comment: null }); expect(status).toEqual(400); - expect(body).toEqual(errorStub.badRequest(['comment must be a string', 'comment should not be empty'])); + expect(body).toEqual(errorDto.badRequest(['comment must be a string', 'comment should not be empty'])); }); it('should add a comment to an album', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .post('/activity') .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ albumId: album.id, type: 'comment', comment: 'This is my first comment' }); + .send({ + albumId: album.id, + type: 'comment', + comment: 'This is my first comment', + }); expect(status).toEqual(201); expect(body).toEqual({ id: expect.any(String), @@ -229,7 +235,7 @@ describe(`${ActivityController.name} (e2e)`, () => { }); it('should add a like to an album', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .post('/activity') .set('Authorization', `Bearer ${admin.accessToken}`) .send({ albumId: album.id, type: 'like' }); @@ -245,11 +251,9 @@ describe(`${ActivityController.name} (e2e)`, () => { }); it('should return a 200 for a duplicate like on the album', async () => { - const reaction = await api.activityApi.create(server, admin.accessToken, { - albumId: album.id, - type: ReactionType.LIKE, - }); - const { status, body } = await request(server) + const [reaction] = await Promise.all([createActivity({ albumId: album.id, type: ReactionType.Like })]); + + const { status, body } = await request(app) .post('/activity') .set('Authorization', `Bearer ${admin.accessToken}`) .send({ albumId: album.id, type: 'like' }); @@ -258,12 +262,14 @@ describe(`${ActivityController.name} (e2e)`, () => { }); it('should not confuse an album like with an asset like', async () => { - const reaction = await api.activityApi.create(server, admin.accessToken, { - albumId: album.id, - assetId: asset.id, - type: ReactionType.LIKE, - }); - const { status, body } = await request(server) + const [reaction] = await Promise.all([ + createActivity({ + albumId: album.id, + assetId: asset.id, + type: ReactionType.Like, + }), + ]); + const { status, body } = await request(app) .post('/activity') .set('Authorization', `Bearer ${admin.accessToken}`) .send({ albumId: album.id, type: 'like' }); @@ -272,10 +278,15 @@ describe(`${ActivityController.name} (e2e)`, () => { }); it('should add a comment to an asset', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .post('/activity') .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ albumId: album.id, assetId: asset.id, type: 'comment', comment: 'This is my first comment' }); + .send({ + albumId: album.id, + assetId: asset.id, + type: 'comment', + comment: 'This is my first comment', + }); expect(status).toEqual(201); expect(body).toEqual({ id: expect.any(String), @@ -288,7 +299,7 @@ describe(`${ActivityController.name} (e2e)`, () => { }); it('should add a like to an asset', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .post('/activity') .set('Authorization', `Bearer ${admin.accessToken}`) .send({ albumId: album.id, assetId: asset.id, type: 'like' }); @@ -304,12 +315,15 @@ describe(`${ActivityController.name} (e2e)`, () => { }); it('should return a 200 for a duplicate like on an asset', async () => { - const reaction = await api.activityApi.create(server, admin.accessToken, { - albumId: album.id, - assetId: asset.id, - type: ReactionType.LIKE, - }); - const { status, body } = await request(server) + const [reaction] = await Promise.all([ + createActivity({ + albumId: album.id, + assetId: asset.id, + type: ReactionType.Like, + }), + ]); + + const { status, body } = await request(app) .post('/activity') .set('Authorization', `Bearer ${admin.accessToken}`) .send({ albumId: album.id, assetId: asset.id, type: 'like' }); @@ -320,50 +334,50 @@ describe(`${ActivityController.name} (e2e)`, () => { describe('DELETE /activity/:id', () => { it('should require authentication', async () => { - const { status, body } = await request(server).delete(`/activity/${uuidStub.notFound}`); + const { status, body } = await request(app).delete(`/activity/${uuidDto.notFound}`); expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); + expect(body).toEqual(errorDto.unauthorized); }); it('should require a valid uuid', async () => { - const { status, body } = await request(server) - .delete(`/activity/${uuidStub.invalid}`) + const { status, body } = await request(app) + .delete(`/activity/${uuidDto.invalid}`) .set('Authorization', `Bearer ${admin.accessToken}`); expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest(['id must be a UUID'])); + expect(body).toEqual(errorDto.badRequest(['id must be a UUID'])); }); it('should remove a comment from an album', async () => { - const reaction = await api.activityApi.create(server, admin.accessToken, { + const reaction = await createActivity({ albumId: album.id, - type: ReactionType.COMMENT, + type: ReactionType.Comment, comment: 'This is a test comment', }); - const { status } = await request(server) + const { status } = await request(app) .delete(`/activity/${reaction.id}`) .set('Authorization', `Bearer ${admin.accessToken}`); expect(status).toEqual(204); }); it('should remove a like from an album', async () => { - const reaction = await api.activityApi.create(server, admin.accessToken, { + const reaction = await createActivity({ albumId: album.id, - type: ReactionType.LIKE, + type: ReactionType.Like, }); - const { status } = await request(server) + const { status } = await request(app) .delete(`/activity/${reaction.id}`) .set('Authorization', `Bearer ${admin.accessToken}`); expect(status).toEqual(204); }); it('should let the owner remove a comment by another user', async () => { - const reaction = await api.activityApi.create(server, nonOwner.accessToken, { + const reaction = await createActivity({ albumId: album.id, - type: ReactionType.COMMENT, + type: ReactionType.Comment, comment: 'This is a test comment', }); - const { status } = await request(server) + const { status } = await request(app) .delete(`/activity/${reaction.id}`) .set('Authorization', `Bearer ${admin.accessToken}`); @@ -371,28 +385,31 @@ describe(`${ActivityController.name} (e2e)`, () => { }); it('should not let a user remove a comment by another user', async () => { - const reaction = await api.activityApi.create(server, admin.accessToken, { + const reaction = await createActivity({ albumId: album.id, - type: ReactionType.COMMENT, + type: ReactionType.Comment, comment: 'This is a test comment', }); - const { status, body } = await request(server) + const { status, body } = await request(app) .delete(`/activity/${reaction.id}`) .set('Authorization', `Bearer ${nonOwner.accessToken}`); expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest('Not found or no activity.delete access')); + expect(body).toEqual(errorDto.badRequest('Not found or no activity.delete access')); }); it('should let a non-owner remove their own comment', async () => { - const reaction = await api.activityApi.create(server, nonOwner.accessToken, { - albumId: album.id, - type: ReactionType.COMMENT, - comment: 'This is a test comment', - }); + const reaction = await createActivity( + { + albumId: album.id, + type: ReactionType.Comment, + comment: 'This is a test comment', + }, + nonOwner.accessToken, + ); - const { status } = await request(server) + const { status } = await request(app) .delete(`/activity/${reaction.id}`) .set('Authorization', `Bearer ${nonOwner.accessToken}`); diff --git a/server/e2e/api/specs/album.e2e-spec.ts b/e2e/src/api/specs/album.e2e-spec.ts similarity index 52% rename from server/e2e/api/specs/album.e2e-spec.ts rename to e2e/src/api/specs/album.e2e-spec.ts index 312816035c..99a50106ed 100644 --- a/server/e2e/api/specs/album.e2e-spec.ts +++ b/e2e/src/api/specs/album.e2e-spec.ts @@ -1,11 +1,15 @@ -import { AlbumResponseDto, LoginResponseDto } from '@app/domain'; -import { AlbumController } from '@app/immich'; -import { AssetFileUploadResponseDto } from '@app/immich/api-v1/asset/response-dto/asset-file-upload-response.dto'; -import { SharedLinkType } from '@app/infra/entities'; -import { errorStub, userDto, uuidStub } from '@test/fixtures'; +import { + AlbumResponseDto, + AssetFileUploadResponseDto, + LoginResponseDto, + SharedLinkType, + deleteUser, +} from '@immich/sdk'; +import { createUserDto, uuidDto } from 'src/fixtures'; +import { errorDto } from 'src/responses'; +import { apiUtils, app, asBearerAuth, dbUtils } from 'src/utils'; import request from 'supertest'; -import { api } from '../../client'; -import { testApp } from '../utils'; +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; const user1SharedUser = 'user1SharedUser'; const user1SharedLink = 'user1SharedLink'; @@ -14,193 +18,327 @@ const user2SharedUser = 'user2SharedUser'; const user2SharedLink = 'user2SharedLink'; const user2NotShared = 'user2NotShared'; -describe(`${AlbumController.name} (e2e)`, () => { - let server: any; +describe('/album', () => { let admin: LoginResponseDto; let user1: LoginResponseDto; - let user1Asset: AssetFileUploadResponseDto; + let user1Asset1: AssetFileUploadResponseDto; + let user1Asset2: AssetFileUploadResponseDto; let user1Albums: AlbumResponseDto[]; let user2: LoginResponseDto; let user2Albums: AlbumResponseDto[]; + let user3: LoginResponseDto; // deleted beforeAll(async () => { - server = (await testApp.create()).getHttpServer(); - }); + apiUtils.setup(); + await dbUtils.reset(); - afterAll(async () => { - await testApp.teardown(); - }); + admin = await apiUtils.adminSetup(); - beforeEach(async () => { - await testApp.reset(); - await api.authApi.adminSignUp(server); - admin = await api.authApi.adminLogin(server); - - await Promise.all([ - api.userApi.create(server, admin.accessToken, userDto.user1), - api.userApi.create(server, admin.accessToken, userDto.user2), + [user1, user2, user3] = await Promise.all([ + apiUtils.userSetup(admin.accessToken, createUserDto.user1), + apiUtils.userSetup(admin.accessToken, createUserDto.user2), + apiUtils.userSetup(admin.accessToken, createUserDto.user3), ]); - [user1, user2] = await Promise.all([ - api.authApi.login(server, userDto.user1), - api.authApi.login(server, userDto.user2), + [user1Asset1, user1Asset2] = await Promise.all([ + apiUtils.createAsset(user1.accessToken, { isFavorite: true }), + apiUtils.createAsset(user1.accessToken), ]); - user1Asset = await api.assetApi.upload(server, user1.accessToken, 'example'); - const albums = await Promise.all([ // user 1 - api.albumApi.create(server, user1.accessToken, { + apiUtils.createAlbum(user1.accessToken, { albumName: user1SharedUser, sharedWithUserIds: [user2.userId], - assetIds: [user1Asset.id], + assetIds: [user1Asset1.id], + }), + apiUtils.createAlbum(user1.accessToken, { + albumName: user1SharedLink, + assetIds: [user1Asset1.id], + }), + apiUtils.createAlbum(user1.accessToken, { + albumName: user1NotShared, + assetIds: [user1Asset1.id, user1Asset2.id], }), - api.albumApi.create(server, user1.accessToken, { albumName: user1SharedLink, assetIds: [user1Asset.id] }), - api.albumApi.create(server, user1.accessToken, { albumName: user1NotShared, assetIds: [user1Asset.id] }), // user 2 - api.albumApi.create(server, user2.accessToken, { + apiUtils.createAlbum(user2.accessToken, { albumName: user2SharedUser, sharedWithUserIds: [user1.userId], - assetIds: [user1Asset.id], + assetIds: [user1Asset1.id], + }), + apiUtils.createAlbum(user2.accessToken, { albumName: user2SharedLink }), + apiUtils.createAlbum(user2.accessToken, { albumName: user2NotShared }), + + // user 3 + apiUtils.createAlbum(user3.accessToken, { + albumName: 'Deleted', + sharedWithUserIds: [user1.userId], }), - api.albumApi.create(server, user2.accessToken, { albumName: user2SharedLink }), - api.albumApi.create(server, user2.accessToken, { albumName: user2NotShared }), ]); user1Albums = albums.slice(0, 3); - user2Albums = albums.slice(3); + user2Albums = albums.slice(3, 6); await Promise.all([ // add shared link to user1SharedLink album - api.sharedLinkApi.create(server, user1.accessToken, { - type: SharedLinkType.ALBUM, + apiUtils.createSharedLink(user1.accessToken, { + type: SharedLinkType.Album, albumId: user1Albums[1].id, }), - // add shared link to user2SharedLink album - api.sharedLinkApi.create(server, user2.accessToken, { - type: SharedLinkType.ALBUM, + apiUtils.createSharedLink(user2.accessToken, { + type: SharedLinkType.Album, albumId: user2Albums[1].id, }), ]); + + await deleteUser({ id: user3.userId }, { headers: asBearerAuth(admin.accessToken) }); }); describe('GET /album', () => { it('should require authentication', async () => { - const { status, body } = await request(server).get('/album'); + const { status, body } = await request(app).get('/album'); expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); + expect(body).toEqual(errorDto.unauthorized); }); it('should reject an invalid shared param', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/album?shared=invalid') .set('Authorization', `Bearer ${user1.accessToken}`); expect(status).toEqual(400); - expect(body).toEqual(errorStub.badRequest(['shared must be a boolean value'])); + expect(body).toEqual(errorDto.badRequest(['shared must be a boolean value'])); }); it('should reject an invalid assetId param', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/album?assetId=invalid') .set('Authorization', `Bearer ${user1.accessToken}`); expect(status).toEqual(400); - expect(body).toEqual(errorStub.badRequest(['assetId must be a UUID'])); + expect(body).toEqual(errorDto.badRequest(['assetId must be a UUID'])); + }); + + it("should not show other users' favorites", async () => { + const { status, body } = await request(app) + .get(`/album/${user1Albums[0].id}?withoutAssets=false`) + .set('Authorization', `Bearer ${user2.accessToken}`); + expect(status).toEqual(200); + expect(body).toEqual({ + ...user1Albums[0], + assets: [expect.objectContaining({ isFavorite: false })], + }); }); it('should not return shared albums with a deleted owner', async () => { - await api.userApi.delete(server, admin.accessToken, user1.userId); - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/album?shared=true') - .set('Authorization', `Bearer ${user2.accessToken}`); + .set('Authorization', `Bearer ${user1.accessToken}`); expect(status).toBe(200); - expect(body).toHaveLength(1); + expect(body).toHaveLength(3); expect(body).toEqual( expect.arrayContaining([ - expect.objectContaining({ ownerId: user2.userId, albumName: user2SharedLink, shared: true }), + expect.objectContaining({ + ownerId: user1.userId, + albumName: user1SharedLink, + shared: true, + }), + expect.objectContaining({ + ownerId: user1.userId, + albumName: user1SharedUser, + shared: true, + }), + expect.objectContaining({ + ownerId: user2.userId, + albumName: user2SharedUser, + shared: true, + }), ]), ); }); it('should return the album collection including owned and shared', async () => { - const { status, body } = await request(server).get('/album').set('Authorization', `Bearer ${user1.accessToken}`); + const { status, body } = await request(app).get('/album').set('Authorization', `Bearer ${user1.accessToken}`); expect(status).toBe(200); expect(body).toHaveLength(3); expect(body).toEqual( expect.arrayContaining([ - expect.objectContaining({ ownerId: user1.userId, albumName: user1SharedUser, shared: true }), - expect.objectContaining({ ownerId: user1.userId, albumName: user1SharedLink, shared: true }), - expect.objectContaining({ ownerId: user1.userId, albumName: user1NotShared, shared: false }), + expect.objectContaining({ + ownerId: user1.userId, + albumName: user1SharedUser, + shared: true, + }), + expect.objectContaining({ + ownerId: user1.userId, + albumName: user1SharedLink, + shared: true, + }), + expect.objectContaining({ + ownerId: user1.userId, + albumName: user1NotShared, + shared: false, + }), ]), ); }); it('should return the album collection filtered by shared', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/album?shared=true') .set('Authorization', `Bearer ${user1.accessToken}`); expect(status).toBe(200); expect(body).toHaveLength(3); expect(body).toEqual( expect.arrayContaining([ - expect.objectContaining({ ownerId: user1.userId, albumName: user1SharedUser, shared: true }), - expect.objectContaining({ ownerId: user1.userId, albumName: user1SharedLink, shared: true }), - expect.objectContaining({ ownerId: user2.userId, albumName: user2SharedUser, shared: true }), + expect.objectContaining({ + ownerId: user1.userId, + albumName: user1SharedUser, + shared: true, + }), + expect.objectContaining({ + ownerId: user1.userId, + albumName: user1SharedLink, + shared: true, + }), + expect.objectContaining({ + ownerId: user2.userId, + albumName: user2SharedUser, + shared: true, + }), ]), ); }); it('should return the album collection filtered by NOT shared', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/album?shared=false') .set('Authorization', `Bearer ${user1.accessToken}`); expect(status).toBe(200); expect(body).toHaveLength(1); expect(body).toEqual( expect.arrayContaining([ - expect.objectContaining({ ownerId: user1.userId, albumName: user1NotShared, shared: false }), + expect.objectContaining({ + ownerId: user1.userId, + albumName: user1NotShared, + shared: false, + }), ]), ); }); it('should return the album collection filtered by assetId', async () => { - const asset = await api.assetApi.upload(server, user1.accessToken, 'example2'); - await api.albumApi.addAssets(server, user1.accessToken, user1Albums[0].id, { ids: [asset.id] }); - const { status, body } = await request(server) - .get(`/album?assetId=${asset.id}`) + const { status, body } = await request(app) + .get(`/album?assetId=${user1Asset2.id}`) .set('Authorization', `Bearer ${user1.accessToken}`); expect(status).toBe(200); expect(body).toHaveLength(1); }); it('should return the album collection filtered by assetId and ignores shared=true', async () => { - const { status, body } = await request(server) - .get(`/album?shared=true&assetId=${user1Asset.id}`) + const { status, body } = await request(app) + .get(`/album?shared=true&assetId=${user1Asset1.id}`) .set('Authorization', `Bearer ${user1.accessToken}`); expect(status).toBe(200); expect(body).toHaveLength(4); }); it('should return the album collection filtered by assetId and ignores shared=false', async () => { - const { status, body } = await request(server) - .get(`/album?shared=false&assetId=${user1Asset.id}`) + const { status, body } = await request(app) + .get(`/album?shared=false&assetId=${user1Asset1.id}`) .set('Authorization', `Bearer ${user1.accessToken}`); expect(status).toBe(200); expect(body).toHaveLength(4); }); }); + describe('GET /album/:id', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).get(`/album/${user1Albums[0].id}`); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should return album info for own album', async () => { + const { status, body } = await request(app) + .get(`/album/${user1Albums[0].id}?withoutAssets=false`) + .set('Authorization', `Bearer ${user1.accessToken}`); + + expect(status).toBe(200); + expect(body).toEqual({ + ...user1Albums[0], + assets: [expect.objectContaining({ id: user1Albums[0].assets[0].id })], + }); + }); + + it('should return album info for shared album', async () => { + const { status, body } = await request(app) + .get(`/album/${user2Albums[0].id}?withoutAssets=false`) + .set('Authorization', `Bearer ${user1.accessToken}`); + + expect(status).toBe(200); + expect(body).toEqual({ + ...user2Albums[0], + assets: [expect.objectContaining({ id: user2Albums[0].assets[0].id })], + }); + }); + + it('should return album info with assets when withoutAssets is undefined', async () => { + const { status, body } = await request(app) + .get(`/album/${user1Albums[0].id}`) + .set('Authorization', `Bearer ${user1.accessToken}`); + + expect(status).toBe(200); + expect(body).toEqual({ + ...user1Albums[0], + assets: [expect.objectContaining({ id: user1Albums[0].assets[0].id })], + }); + }); + + it('should return album info without assets when withoutAssets is true', async () => { + const { status, body } = await request(app) + .get(`/album/${user1Albums[0].id}?withoutAssets=true`) + .set('Authorization', `Bearer ${user1.accessToken}`); + + expect(status).toBe(200); + expect(body).toEqual({ + ...user1Albums[0], + assets: [], + assetCount: 1, + }); + }); + }); + + describe('GET /album/count', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).get('/album/count'); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should return total count of albums the user has access to', async () => { + const { status, body } = await request(app) + .get('/album/count') + .set('Authorization', `Bearer ${user1.accessToken}`); + + expect(status).toBe(200); + expect(body).toEqual({ owned: 3, shared: 3, notShared: 1 }); + }); + }); + describe('POST /album', () => { it('should require authentication', async () => { - const { status, body } = await request(server).post('/album').send({ albumName: 'New album' }); + const { status, body } = await request(app).post('/album').send({ albumName: 'New album' }); expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); + expect(body).toEqual(errorDto.unauthorized); }); it('should create an album', async () => { - const body = await api.albumApi.create(server, user1.accessToken, { albumName: 'New album' }); + const { status, body } = await request(app) + .post('/album') + .send({ albumName: 'New album' }) + .set('Authorization', `Bearer ${user1.accessToken}`); + expect(status).toBe(201); expect(body).toEqual({ id: expect.any(String), createdAt: expect.any(String), @@ -220,81 +358,16 @@ describe(`${AlbumController.name} (e2e)`, () => { }); }); - describe('GET /album/count', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).get('/album/count'); - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it('should return total count of albums the user has access to', async () => { - const { status, body } = await request(server) - .get('/album/count') - .set('Authorization', `Bearer ${user1.accessToken}`); - - expect(status).toBe(200); - expect(body).toEqual({ owned: 3, shared: 3, notShared: 1 }); - }); - }); - - describe('GET /album/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).get(`/album/${user1Albums[0].id}`); - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it('should return album info for own album', async () => { - const { status, body } = await request(server) - .get(`/album/${user1Albums[0].id}?withoutAssets=false`) - .set('Authorization', `Bearer ${user1.accessToken}`); - - expect(status).toBe(200); - expect(body).toEqual({ ...user1Albums[0], assets: [expect.objectContaining(user1Albums[0].assets[0])] }); - }); - - it('should return album info for shared album', async () => { - const { status, body } = await request(server) - .get(`/album/${user2Albums[0].id}?withoutAssets=false`) - .set('Authorization', `Bearer ${user1.accessToken}`); - - expect(status).toBe(200); - expect(body).toEqual({ ...user2Albums[0], assets: [expect.objectContaining(user2Albums[0].assets[0])] }); - }); - - it('should return album info with assets when withoutAssets is undefined', async () => { - const { status, body } = await request(server) - .get(`/album/${user1Albums[0].id}`) - .set('Authorization', `Bearer ${user1.accessToken}`); - - expect(status).toBe(200); - expect(body).toEqual({ ...user1Albums[0], assets: [expect.objectContaining(user1Albums[0].assets[0])] }); - }); - - it('should return album info without assets when withoutAssets is true', async () => { - const { status, body } = await request(server) - .get(`/album/${user1Albums[0].id}?withoutAssets=true`) - .set('Authorization', `Bearer ${user1.accessToken}`); - - expect(status).toBe(200); - expect(body).toEqual({ - ...user1Albums[0], - assets: [], - assetCount: 1, - }); - }); - }); - describe('PUT /album/:id/assets', () => { it('should require authentication', async () => { - const { status, body } = await request(server).put(`/album/${user1Albums[0].id}/assets`); + const { status, body } = await request(app).put(`/album/${user1Albums[0].id}/assets`); expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); + expect(body).toEqual(errorDto.unauthorized); }); it('should be able to add own asset to own album', async () => { - const asset = await api.assetApi.upload(server, user1.accessToken, 'example1'); - const { status, body } = await request(server) + const asset = await apiUtils.createAsset(user1.accessToken); + const { status, body } = await request(app) .put(`/album/${user1Albums[0].id}/assets`) .set('Authorization', `Bearer ${user1.accessToken}`) .send({ ids: [asset.id] }); @@ -304,8 +377,8 @@ describe(`${AlbumController.name} (e2e)`, () => { }); it('should be able to add own asset to shared album', async () => { - const asset = await api.assetApi.upload(server, user1.accessToken, 'example1'); - const { status, body } = await request(server) + const asset = await apiUtils.createAsset(user1.accessToken); + const { status, body } = await request(app) .put(`/album/${user2Albums[0].id}/assets`) .set('Authorization', `Bearer ${user1.accessToken}`) .send({ ids: [asset.id] }); @@ -317,16 +390,18 @@ describe(`${AlbumController.name} (e2e)`, () => { describe('PATCH /album/:id', () => { it('should require authentication', async () => { - const { status, body } = await request(server) - .patch(`/album/${uuidStub.notFound}`) + const { status, body } = await request(app) + .patch(`/album/${uuidDto.notFound}`) .send({ albumName: 'New album name' }); expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); + expect(body).toEqual(errorDto.unauthorized); }); it('should update an album', async () => { - const album = await api.albumApi.create(server, user1.accessToken, { albumName: 'New album' }); - const { status, body } = await request(server) + const album = await apiUtils.createAlbum(user1.accessToken, { + albumName: 'New album', + }); + const { status, body } = await request(app) .patch(`/album/${album.id}`) .set('Authorization', `Bearer ${user1.accessToken}`) .send({ @@ -345,52 +420,64 @@ describe(`${AlbumController.name} (e2e)`, () => { describe('DELETE /album/:id/assets', () => { it('should require authentication', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .delete(`/album/${user1Albums[0].id}/assets`) - .send({ ids: [user1Asset.id] }); + .send({ ids: [user1Asset1.id] }); expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it('should be able to remove own asset from own album', async () => { - const { status, body } = await request(server) - .delete(`/album/${user1Albums[0].id}/assets`) - .set('Authorization', `Bearer ${user1.accessToken}`) - .send({ ids: [user1Asset.id] }); - - expect(status).toBe(200); - expect(body).toEqual([expect.objectContaining({ id: user1Asset.id, success: true })]); - }); - - it('should be able to remove own asset from shared album', async () => { - const { status, body } = await request(server) - .delete(`/album/${user2Albums[0].id}/assets`) - .set('Authorization', `Bearer ${user1.accessToken}`) - .send({ ids: [user1Asset.id] }); - - expect(status).toBe(200); - expect(body).toEqual([expect.objectContaining({ id: user1Asset.id, success: true })]); + expect(body).toEqual(errorDto.unauthorized); }); it('should not be able to remove foreign asset from own album', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .delete(`/album/${user2Albums[0].id}/assets`) .set('Authorization', `Bearer ${user2.accessToken}`) - .send({ ids: [user1Asset.id] }); + .send({ ids: [user1Asset1.id] }); expect(status).toBe(200); - expect(body).toEqual([expect.objectContaining({ id: user1Asset.id, success: false, error: 'no_permission' })]); + expect(body).toEqual([ + expect.objectContaining({ + id: user1Asset1.id, + success: false, + error: 'no_permission', + }), + ]); }); it('should not be able to remove foreign asset from foreign album', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .delete(`/album/${user1Albums[0].id}/assets`) .set('Authorization', `Bearer ${user2.accessToken}`) - .send({ ids: [user1Asset.id] }); + .send({ ids: [user1Asset1.id] }); expect(status).toBe(200); - expect(body).toEqual([expect.objectContaining({ id: user1Asset.id, success: false, error: 'no_permission' })]); + expect(body).toEqual([ + expect.objectContaining({ + id: user1Asset1.id, + success: false, + error: 'no_permission', + }), + ]); + }); + + it('should be able to remove own asset from own album', async () => { + const { status, body } = await request(app) + .delete(`/album/${user1Albums[0].id}/assets`) + .set('Authorization', `Bearer ${user1.accessToken}`) + .send({ ids: [user1Asset1.id] }); + + expect(status).toBe(200); + expect(body).toEqual([expect.objectContaining({ id: user1Asset1.id, success: true })]); + }); + + it('should be able to remove own asset from shared album', async () => { + const { status, body } = await request(app) + .delete(`/album/${user2Albums[0].id}/assets`) + .set('Authorization', `Bearer ${user1.accessToken}`) + .send({ ids: [user1Asset1.id] }); + + expect(status).toBe(200); + expect(body).toEqual([expect.objectContaining({ id: user1Asset1.id, success: true })]); }); }); @@ -398,51 +485,55 @@ describe(`${AlbumController.name} (e2e)`, () => { let album: AlbumResponseDto; beforeEach(async () => { - album = await api.albumApi.create(server, user1.accessToken, { albumName: 'testAlbum' }); + album = await apiUtils.createAlbum(user1.accessToken, { + albumName: 'testAlbum', + }); }); it('should require authentication', async () => { - const { status, body } = await request(server) - .put(`/album/${user1Albums[0].id}/users`) - .send({ sharedUserIds: [] }); + const { status, body } = await request(app).put(`/album/${user1Albums[0].id}/users`).send({ sharedUserIds: [] }); expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); + expect(body).toEqual(errorDto.unauthorized); }); it('should be able to add user to own album', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .put(`/album/${album.id}/users`) .set('Authorization', `Bearer ${user1.accessToken}`) .send({ sharedUserIds: [user2.userId] }); expect(status).toBe(200); - expect(body).toEqual(expect.objectContaining({ sharedUsers: [expect.objectContaining({ id: user2.userId })] })); + expect(body).toEqual( + expect.objectContaining({ + sharedUsers: [expect.objectContaining({ id: user2.userId })], + }), + ); }); it('should not be able to share album with owner', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .put(`/album/${album.id}/users`) .set('Authorization', `Bearer ${user1.accessToken}`) .send({ sharedUserIds: [user1.userId] }); expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest('Cannot be shared with owner')); + expect(body).toEqual(errorDto.badRequest('Cannot be shared with owner')); }); it('should not be able to add existing user to shared album', async () => { - await request(server) + await request(app) .put(`/album/${album.id}/users`) .set('Authorization', `Bearer ${user1.accessToken}`) .send({ sharedUserIds: [user2.userId] }); - const { status, body } = await request(server) + const { status, body } = await request(app) .put(`/album/${album.id}/users`) .set('Authorization', `Bearer ${user1.accessToken}`) .send({ sharedUserIds: [user2.userId] }); expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest('User already added')); + expect(body).toEqual(errorDto.badRequest('User already added')); }); }); }); diff --git a/e2e/src/api/specs/asset.e2e-spec.ts b/e2e/src/api/specs/asset.e2e-spec.ts new file mode 100644 index 0000000000..2873bb0c3e --- /dev/null +++ b/e2e/src/api/specs/asset.e2e-spec.ts @@ -0,0 +1,749 @@ +import { + AssetFileUploadResponseDto, + AssetResponseDto, + AssetTypeEnum, + LoginResponseDto, + SharedLinkType, +} from '@immich/sdk'; +import { exiftool } from 'exiftool-vendored'; +import { DateTime } from 'luxon'; +import { createHash } from 'node:crypto'; +import { readFile, writeFile } from 'node:fs/promises'; +import { basename, join } from 'node:path'; +import { Socket } from 'socket.io-client'; +import { createUserDto, uuidDto } from 'src/fixtures'; +import { errorDto } from 'src/responses'; +import { apiUtils, app, dbUtils, tempDir, testAssetDir, wsUtils } from 'src/utils'; +import request from 'supertest'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; + +const TEN_TIMES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + +const locationAssetFilepath = `${testAssetDir}/metadata/gps-position/thompson-springs.jpg`; + +const sha1 = (bytes: Buffer) => createHash('sha1').update(bytes).digest('base64'); + +const readTags = async (bytes: Buffer, filename: string) => { + const filepath = join(tempDir, filename); + await writeFile(filepath, bytes); + return exiftool.read(filepath); +}; + +const today = DateTime.fromObject({ + year: 2023, + month: 11, + day: 3, +}) as DateTime; +const yesterday = today.minus({ days: 1 }); + +describe('/asset', () => { + let admin: LoginResponseDto; + let user1: LoginResponseDto; + let user2: LoginResponseDto; + let userStats: LoginResponseDto; + let user1Assets: AssetFileUploadResponseDto[]; + let user2Assets: AssetFileUploadResponseDto[]; + let assetLocation: AssetFileUploadResponseDto; + let ws: Socket; + + beforeAll(async () => { + apiUtils.setup(); + await dbUtils.reset(); + admin = await apiUtils.adminSetup({ onboarding: false }); + + [ws, user1, user2, userStats] = await Promise.all([ + wsUtils.connect(admin.accessToken), + apiUtils.userSetup(admin.accessToken, createUserDto.user1), + apiUtils.userSetup(admin.accessToken, createUserDto.user2), + apiUtils.userSetup(admin.accessToken, createUserDto.user3), + ]); + + // asset location + assetLocation = await apiUtils.createAsset(admin.accessToken, { + assetData: { + filename: 'thompson-springs.jpg', + bytes: await readFile(locationAssetFilepath), + }, + }); + + await wsUtils.waitForEvent({ event: 'upload', assetId: assetLocation.id }); + + user1Assets = await Promise.all([ + apiUtils.createAsset(user1.accessToken), + apiUtils.createAsset(user1.accessToken), + apiUtils.createAsset(user1.accessToken, { + isFavorite: true, + isReadOnly: true, + fileCreatedAt: yesterday.toISO(), + fileModifiedAt: yesterday.toISO(), + assetData: { filename: 'example.mp4' }, + }), + apiUtils.createAsset(user1.accessToken), + apiUtils.createAsset(user1.accessToken), + ]); + + user2Assets = await Promise.all([apiUtils.createAsset(user2.accessToken)]); + + for (const asset of [...user1Assets, ...user2Assets]) { + expect(asset.duplicate).toBe(false); + } + + await Promise.all([ + // stats + apiUtils.createAsset(userStats.accessToken), + apiUtils.createAsset(userStats.accessToken, { isFavorite: true }), + apiUtils.createAsset(userStats.accessToken, { isArchived: true }), + apiUtils.createAsset(userStats.accessToken, { + isArchived: true, + isFavorite: true, + assetData: { filename: 'example.mp4' }, + }), + ]); + + const person1 = await apiUtils.createPerson(user1.accessToken, { + name: 'Test Person', + }); + await dbUtils.createFace({ + assetId: user1Assets[0].id, + personId: person1.id, + }); + }, 30_000); + + afterAll(() => { + wsUtils.disconnect(ws); + }); + + describe('GET /asset/:id', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).get(`/asset/${uuidDto.notFound}`); + expect(body).toEqual(errorDto.unauthorized); + expect(status).toBe(401); + }); + + it('should require a valid id', async () => { + const { status, body } = await request(app) + .get(`/asset/${uuidDto.invalid}`) + .set('Authorization', `Bearer ${user1.accessToken}`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['id must be a UUID'])); + }); + + it('should require access', async () => { + const { status, body } = await request(app) + .get(`/asset/${user2Assets[0].id}`) + .set('Authorization', `Bearer ${user1.accessToken}`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.noPermission); + }); + + it('should get the asset info', async () => { + const { status, body } = await request(app) + .get(`/asset/${user1Assets[0].id}`) + .set('Authorization', `Bearer ${user1.accessToken}`); + expect(status).toBe(200); + expect(body).toMatchObject({ id: user1Assets[0].id }); + }); + + it('should work with a shared link', async () => { + const sharedLink = await apiUtils.createSharedLink(user1.accessToken, { + type: SharedLinkType.Individual, + assetIds: [user1Assets[0].id], + }); + + const { status, body } = await request(app).get(`/asset/${user1Assets[0].id}?key=${sharedLink.key}`); + expect(status).toBe(200); + expect(body).toMatchObject({ id: user1Assets[0].id }); + }); + + it('should not send people data for shared links for un-authenticated users', async () => { + const { status, body } = await request(app) + .get(`/asset/${user1Assets[0].id}`) + .set('Authorization', `Bearer ${user1.accessToken}`); + + expect(status).toEqual(200); + expect(body).toMatchObject({ + id: user1Assets[0].id, + isFavorite: false, + people: [ + { + birthDate: null, + id: expect.any(String), + isHidden: false, + name: 'Test Person', + thumbnailPath: '/my/awesome/thumbnail.jpg', + }, + ], + }); + + const sharedLink = await apiUtils.createSharedLink(user1.accessToken, { + type: SharedLinkType.Individual, + assetIds: [user1Assets[0].id], + }); + + const data = await request(app).get(`/asset/${user1Assets[0].id}?key=${sharedLink.key}`); + expect(data.status).toBe(200); + expect(data.body).toMatchObject({ people: [] }); + }); + }); + + describe('GET /asset/statistics', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).get('/asset/statistics'); + + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should return stats of all assets', async () => { + const { status, body } = await request(app) + .get('/asset/statistics') + .set('Authorization', `Bearer ${userStats.accessToken}`); + + expect(body).toEqual({ images: 3, videos: 1, total: 4 }); + expect(status).toBe(200); + }); + + it('should return stats of all favored assets', async () => { + const { status, body } = await request(app) + .get('/asset/statistics') + .set('Authorization', `Bearer ${userStats.accessToken}`) + .query({ isFavorite: true }); + + expect(status).toBe(200); + expect(body).toEqual({ images: 1, videos: 1, total: 2 }); + }); + + it('should return stats of all archived assets', async () => { + const { status, body } = await request(app) + .get('/asset/statistics') + .set('Authorization', `Bearer ${userStats.accessToken}`) + .query({ isArchived: true }); + + expect(status).toBe(200); + expect(body).toEqual({ images: 1, videos: 1, total: 2 }); + }); + + it('should return stats of all favored and archived assets', async () => { + const { status, body } = await request(app) + .get('/asset/statistics') + .set('Authorization', `Bearer ${userStats.accessToken}`) + .query({ isFavorite: true, isArchived: true }); + + expect(status).toBe(200); + expect(body).toEqual({ images: 0, videos: 1, total: 1 }); + }); + + it('should return stats of all assets neither favored nor archived', async () => { + const { status, body } = await request(app) + .get('/asset/statistics') + .set('Authorization', `Bearer ${userStats.accessToken}`) + .query({ isFavorite: false, isArchived: false }); + + expect(status).toBe(200); + expect(body).toEqual({ images: 1, videos: 0, total: 1 }); + }); + }); + + describe('GET /asset/random', () => { + beforeAll(async () => { + await Promise.all([ + apiUtils.createAsset(user1.accessToken), + apiUtils.createAsset(user1.accessToken), + apiUtils.createAsset(user1.accessToken), + apiUtils.createAsset(user1.accessToken), + apiUtils.createAsset(user1.accessToken), + apiUtils.createAsset(user1.accessToken), + ]); + }); + + it('should require authentication', async () => { + const { status, body } = await request(app).get('/asset/random'); + + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it.each(TEN_TIMES)('should return 1 random assets', async () => { + const { status, body } = await request(app) + .get('/asset/random') + .set('Authorization', `Bearer ${user1.accessToken}`); + + expect(status).toBe(200); + + const assets: AssetResponseDto[] = body; + expect(assets.length).toBe(1); + expect(assets[0].ownerId).toBe(user1.userId); + }); + + it.each(TEN_TIMES)('should return 2 random assets', async () => { + const { status, body } = await request(app) + .get('/asset/random?count=2') + .set('Authorization', `Bearer ${user1.accessToken}`); + + expect(status).toBe(200); + + const assets: AssetResponseDto[] = body; + expect(assets.length).toBe(2); + + for (const asset of assets) { + expect(asset.ownerId).toBe(user1.userId); + } + }); + + it.each(TEN_TIMES)( + 'should return 1 asset if there are 10 assets in the database but user 2 only has 1', + async () => { + const { status, body } = await request(app) + .get('/asset/random') + .set('Authorization', `Bearer ${user2.accessToken}`); + + expect(status).toBe(200); + expect(body).toEqual([expect.objectContaining({ id: user2Assets[0].id })]); + }, + ); + + it('should return error', async () => { + const { status } = await request(app) + .get('/asset/random?count=ABC') + .set('Authorization', `Bearer ${user1.accessToken}`); + + expect(status).toBe(400); + }); + }); + + describe('PUT /asset/:id', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).put(`/asset/:${uuidDto.notFound}`); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should require a valid id', async () => { + const { status, body } = await request(app) + .put(`/asset/${uuidDto.invalid}`) + .set('Authorization', `Bearer ${user1.accessToken}`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['id must be a UUID'])); + }); + + it('should require access', async () => { + const { status, body } = await request(app) + .put(`/asset/${user2Assets[0].id}`) + .set('Authorization', `Bearer ${user1.accessToken}`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.noPermission); + }); + + it('should favorite an asset', async () => { + const before = await apiUtils.getAssetInfo(user1.accessToken, user1Assets[0].id); + expect(before.isFavorite).toBe(false); + + const { status, body } = await request(app) + .put(`/asset/${user1Assets[0].id}`) + .set('Authorization', `Bearer ${user1.accessToken}`) + .send({ isFavorite: true }); + expect(body).toMatchObject({ id: user1Assets[0].id, isFavorite: true }); + expect(status).toEqual(200); + }); + + it('should archive an asset', async () => { + const before = await apiUtils.getAssetInfo(user1.accessToken, user1Assets[0].id); + expect(before.isArchived).toBe(false); + + const { status, body } = await request(app) + .put(`/asset/${user1Assets[0].id}`) + .set('Authorization', `Bearer ${user1.accessToken}`) + .send({ isArchived: true }); + expect(body).toMatchObject({ id: user1Assets[0].id, isArchived: true }); + expect(status).toEqual(200); + }); + + it('should update date time original', async () => { + const { status, body } = await request(app) + .put(`/asset/${user1Assets[0].id}`) + .set('Authorization', `Bearer ${user1.accessToken}`) + .send({ dateTimeOriginal: '2023-11-19T18:11:00.000-07:00' }); + + expect(body).toMatchObject({ + id: user1Assets[0].id, + exifInfo: expect.objectContaining({ + dateTimeOriginal: '2023-11-20T01:11:00.000Z', + }), + }); + expect(status).toEqual(200); + }); + + it('should reject invalid gps coordinates', async () => { + for (const test of [ + { latitude: 12 }, + { longitude: 12 }, + { latitude: 12, longitude: 'abc' }, + { latitude: 'abc', longitude: 12 }, + { latitude: null, longitude: 12 }, + { latitude: 12, longitude: null }, + { latitude: 91, longitude: 12 }, + { latitude: -91, longitude: 12 }, + { latitude: 12, longitude: -181 }, + { latitude: 12, longitude: 181 }, + ]) { + const { status, body } = await request(app) + .put(`/asset/${user1Assets[0].id}`) + .send(test) + .set('Authorization', `Bearer ${user1.accessToken}`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest()); + } + }); + + it('should update gps data', async () => { + const { status, body } = await request(app) + .put(`/asset/${user1Assets[0].id}`) + .set('Authorization', `Bearer ${user1.accessToken}`) + .send({ latitude: 12, longitude: 12 }); + + expect(body).toMatchObject({ + id: user1Assets[0].id, + exifInfo: expect.objectContaining({ latitude: 12, longitude: 12 }), + }); + expect(status).toEqual(200); + }); + + it('should set the description', async () => { + const { status, body } = await request(app) + .put(`/asset/${user1Assets[0].id}`) + .set('Authorization', `Bearer ${user1.accessToken}`) + .send({ description: 'Test asset description' }); + expect(body).toMatchObject({ + id: user1Assets[0].id, + exifInfo: expect.objectContaining({ + description: 'Test asset description', + }), + }); + expect(status).toEqual(200); + }); + + it('should return tagged people', async () => { + const { status, body } = await request(app) + .put(`/asset/${user1Assets[0].id}`) + .set('Authorization', `Bearer ${user1.accessToken}`) + .send({ isFavorite: true }); + expect(status).toEqual(200); + expect(body).toMatchObject({ + id: user1Assets[0].id, + isFavorite: true, + people: [ + { + birthDate: null, + id: expect.any(String), + isHidden: false, + name: 'Test Person', + thumbnailPath: '/my/awesome/thumbnail.jpg', + }, + ], + }); + }); + }); + + describe('DELETE /asset', () => { + it('should require authentication', async () => { + const { status, body } = await request(app) + .delete(`/asset`) + .send({ ids: [uuidDto.notFound] }); + + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should require a valid uuid', async () => { + const { status, body } = await request(app) + .delete(`/asset`) + .send({ ids: [uuidDto.invalid] }) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['each value in ids must be a UUID'])); + }); + + it('should throw an error when the id is not found', async () => { + const { status, body } = await request(app) + .delete(`/asset`) + .send({ ids: [uuidDto.notFound] }) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest('Not found or no asset.delete access')); + }); + + it('should move an asset to the trash', async () => { + const { id: assetId } = await apiUtils.createAsset(admin.accessToken); + + const before = await apiUtils.getAssetInfo(admin.accessToken, assetId); + expect(before.isTrashed).toBe(false); + + const { status } = await request(app) + .delete('/asset') + .send({ ids: [assetId] }) + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(204); + + const after = await apiUtils.getAssetInfo(admin.accessToken, assetId); + expect(after.isTrashed).toBe(true); + }); + }); + + describe('POST /asset/upload', () => { + const tests = [ + { + input: 'formats/jpg/el_torcal_rocks.jpg', + expected: { + type: AssetTypeEnum.Image, + originalFileName: 'el_torcal_rocks', + resized: true, + exifInfo: { + dateTimeOriginal: '2012-08-05T11:39:59.000Z', + exifImageWidth: 512, + exifImageHeight: 341, + latitude: null, + longitude: null, + focalLength: 75, + iso: 200, + fNumber: 11, + exposureTime: '1/160', + fileSizeInByte: 53_493, + make: 'SONY', + model: 'DSLR-A550', + orientation: null, + description: 'SONY DSC', + }, + }, + }, + { + input: 'formats/heic/IMG_2682.heic', + expected: { + type: AssetTypeEnum.Image, + originalFileName: 'IMG_2682', + resized: true, + fileCreatedAt: '2019-03-21T16:04:22.348Z', + exifInfo: { + dateTimeOriginal: '2019-03-21T16:04:22.348Z', + exifImageWidth: 4032, + exifImageHeight: 3024, + latitude: 41.2203, + longitude: -96.071_625, + make: 'Apple', + model: 'iPhone 7', + lensModel: 'iPhone 7 back camera 3.99mm f/1.8', + fileSizeInByte: 880_703, + exposureTime: '1/887', + iso: 20, + focalLength: 3.99, + fNumber: 1.8, + timeZone: 'America/Chicago', + }, + }, + }, + { + input: 'formats/png/density_plot.png', + expected: { + type: AssetTypeEnum.Image, + originalFileName: 'density_plot', + resized: true, + exifInfo: { + exifImageWidth: 800, + exifImageHeight: 800, + latitude: null, + longitude: null, + fileSizeInByte: 25_408, + }, + }, + }, + { + input: 'formats/raw/Nikon/D80/glarus.nef', + expected: { + type: AssetTypeEnum.Image, + originalFileName: 'glarus', + resized: true, + fileCreatedAt: '2010-07-20T17:27:12.000Z', + exifInfo: { + make: 'NIKON CORPORATION', + model: 'NIKON D80', + exposureTime: '1/200', + fNumber: 10, + focalLength: 18, + iso: 100, + fileSizeInByte: 9_057_784, + dateTimeOriginal: '2010-07-20T17:27:12.000Z', + latitude: null, + longitude: null, + orientation: '1', + }, + }, + }, + { + input: 'formats/raw/Nikon/D700/philadelphia.nef', + expected: { + type: AssetTypeEnum.Image, + originalFileName: 'philadelphia', + resized: true, + fileCreatedAt: '2016-09-22T22:10:29.060Z', + exifInfo: { + make: 'NIKON CORPORATION', + model: 'NIKON D700', + exposureTime: '1/400', + fNumber: 11, + focalLength: 85, + iso: 200, + fileSizeInByte: 15_856_335, + dateTimeOriginal: '2016-09-22T22:10:29.060Z', + latitude: null, + longitude: null, + orientation: '1', + timeZone: 'UTC-5', + }, + }, + }, + ]; + + for (const { input, expected } of tests) { + it(`should generate a thumbnail for ${input}`, async () => { + const filepath = join(testAssetDir, input); + const { id, duplicate } = await apiUtils.createAsset(admin.accessToken, { + assetData: { bytes: await readFile(filepath), filename: basename(filepath) }, + }); + + expect(duplicate).toBe(false); + + await wsUtils.waitForEvent({ event: 'upload', assetId: id }); + + const asset = await apiUtils.getAssetInfo(admin.accessToken, id); + + expect(asset.exifInfo).toBeDefined(); + expect(asset.exifInfo).toMatchObject(expected.exifInfo); + expect(asset).toMatchObject(expected); + }); + } + + it('should handle a duplicate', async () => { + const filepath = 'formats/jpeg/el_torcal_rocks.jpeg'; + const { duplicate } = await apiUtils.createAsset(admin.accessToken, { + assetData: { + bytes: await readFile(join(testAssetDir, filepath)), + filename: basename(filepath), + }, + }); + + expect(duplicate).toBe(true); + }); + + // These hashes were created by copying the image files to a Samsung phone, + // exporting the video from Samsung's stock Gallery app, and hashing them locally. + // This ensures that immich+exiftool are extracting the videos the same way Samsung does. + // DO NOT assume immich+exiftool are doing things correctly and just copy whatever hash it gives + // into the test here. + const motionTests = [ + { + filepath: 'formats/motionphoto/Samsung One UI 5.jpg', + checksum: 'fr14niqCq6N20HB8rJYEvpsUVtI=', + }, + { + filepath: 'formats/motionphoto/Samsung One UI 6.jpg', + checksum: 'lT9Uviw/FFJYCjfIxAGPTjzAmmw=', + }, + { + filepath: 'formats/motionphoto/Samsung One UI 6.heic', + checksum: '/ejgzywvgvzvVhUYVfvkLzFBAF0=', + }, + ]; + + for (const { filepath, checksum } of motionTests) { + it(`should extract motionphoto video from ${filepath}`, async () => { + const response = await apiUtils.createAsset(admin.accessToken, { + assetData: { + bytes: await readFile(join(testAssetDir, filepath)), + filename: basename(filepath), + }, + }); + + await wsUtils.waitForEvent({ event: 'upload', assetId: response.id }); + + expect(response.duplicate).toBe(false); + + const asset = await apiUtils.getAssetInfo(admin.accessToken, response.id); + expect(asset.livePhotoVideoId).toBeDefined(); + + const video = await apiUtils.getAssetInfo(admin.accessToken, asset.livePhotoVideoId as string); + expect(video.checksum).toStrictEqual(checksum); + }); + } + }); + + describe('GET /asset/thumbnail/:id', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).get(`/asset/thumbnail/${assetLocation.id}`); + + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should not include gps data for webp thumbnails', async () => { + const { status, body, type } = await request(app) + .get(`/asset/thumbnail/${assetLocation.id}?format=WEBP`) + .set('Authorization', `Bearer ${admin.accessToken}`); + + await wsUtils.waitForEvent({ + event: 'upload', + assetId: assetLocation.id, + }); + + expect(status).toBe(200); + expect(body).toBeDefined(); + expect(type).toBe('image/webp'); + + const exifData = await readTags(body, 'thumbnail.webp'); + expect(exifData).not.toHaveProperty('GPSLongitude'); + expect(exifData).not.toHaveProperty('GPSLatitude'); + }); + + it('should not include gps data for jpeg thumbnails', async () => { + const { status, body, type } = await request(app) + .get(`/asset/thumbnail/${assetLocation.id}?format=JPEG`) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(200); + expect(body).toBeDefined(); + expect(type).toBe('image/jpeg'); + + const exifData = await readTags(body, 'thumbnail.jpg'); + expect(exifData).not.toHaveProperty('GPSLongitude'); + expect(exifData).not.toHaveProperty('GPSLatitude'); + }); + }); + + describe('GET /asset/file/:id', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).get(`/asset/thumbnail/${assetLocation.id}`); + + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should download the original', async () => { + const { status, body, type } = await request(app) + .get(`/asset/file/${assetLocation.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(200); + expect(body).toBeDefined(); + expect(type).toBe('image/jpeg'); + + const asset = await apiUtils.getAssetInfo(admin.accessToken, assetLocation.id); + + const original = await readFile(locationAssetFilepath); + const originalChecksum = sha1(original); + const downloadChecksum = sha1(body); + + expect(originalChecksum).toBe(downloadChecksum); + expect(downloadChecksum).toBe(asset.checksum); + }); + }); +}); diff --git a/e2e/src/api/specs/audit.e2e-spec.ts b/e2e/src/api/specs/audit.e2e-spec.ts new file mode 100644 index 0000000000..13c753039d --- /dev/null +++ b/e2e/src/api/specs/audit.e2e-spec.ts @@ -0,0 +1,43 @@ +import { deleteAssets, getAuditFiles, updateAsset, type LoginResponseDto } from '@immich/sdk'; +import { apiUtils, asBearerAuth, dbUtils, fileUtils } from 'src/utils'; +import { beforeAll, describe, expect, it } from 'vitest'; + +describe('/audit', () => { + let admin: LoginResponseDto; + + beforeAll(async () => { + apiUtils.setup(); + await dbUtils.reset(); + await fileUtils.reset(); + + admin = await apiUtils.adminSetup(); + }); + + describe('GET :/file-report', () => { + it('excludes assets without issues from report', async () => { + const [trashedAsset, archivedAsset] = await Promise.all([ + apiUtils.createAsset(admin.accessToken), + apiUtils.createAsset(admin.accessToken), + apiUtils.createAsset(admin.accessToken), + ]); + + await Promise.all([ + deleteAssets({ assetBulkDeleteDto: { ids: [trashedAsset.id] } }, { headers: asBearerAuth(admin.accessToken) }), + updateAsset( + { + id: archivedAsset.id, + updateAssetDto: { isArchived: true }, + }, + { headers: asBearerAuth(admin.accessToken) }, + ), + ]); + + const body = await getAuditFiles({ + headers: asBearerAuth(admin.accessToken), + }); + + expect(body.orphans).toHaveLength(0); + expect(body.extras).toHaveLength(0); + }); + }); +}); diff --git a/e2e/src/api/specs/auth.e2e-spec.ts b/e2e/src/api/specs/auth.e2e-spec.ts new file mode 100644 index 0000000000..a58e215718 --- /dev/null +++ b/e2e/src/api/specs/auth.e2e-spec.ts @@ -0,0 +1,255 @@ +import { LoginResponseDto, getAuthDevices, login, signUpAdmin } from '@immich/sdk'; +import { loginDto, signupDto, uuidDto } from 'src/fixtures'; +import { deviceDto, errorDto, loginResponseDto, signupResponseDto } from 'src/responses'; +import { apiUtils, app, asBearerAuth, dbUtils } from 'src/utils'; +import request from 'supertest'; +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; + +const { name, email, password } = signupDto.admin; + +describe(`/auth/admin-sign-up`, () => { + beforeAll(() => { + apiUtils.setup(); + }); + + beforeEach(async () => { + await dbUtils.reset(); + }); + + describe('POST /auth/admin-sign-up', () => { + const invalid = [ + { + should: 'require an email address', + data: { name, password }, + }, + { + should: 'require a password', + data: { name, email }, + }, + { + should: 'require a name', + data: { email, password }, + }, + { + should: 'require a valid email', + data: { name, email: 'immich', password }, + }, + ]; + + for (const { should, data } of invalid) { + it(`should ${should}`, async () => { + const { status, body } = await request(app).post('/auth/admin-sign-up').send(data); + expect(status).toEqual(400); + expect(body).toEqual(errorDto.badRequest()); + }); + } + + it(`should sign up the admin`, async () => { + const { status, body } = await request(app).post('/auth/admin-sign-up').send(signupDto.admin); + expect(status).toBe(201); + expect(body).toEqual(signupResponseDto.admin); + }); + + it('should sign up the admin with a local domain', async () => { + const { status, body } = await request(app) + .post('/auth/admin-sign-up') + .send({ ...signupDto.admin, email: 'admin@local' }); + expect(status).toEqual(201); + expect(body).toEqual({ + ...signupResponseDto.admin, + email: 'admin@local', + }); + }); + + it('should transform email to lower case', async () => { + const { status, body } = await request(app) + .post('/auth/admin-sign-up') + .send({ ...signupDto.admin, email: 'aDmIn@IMMICH.cloud' }); + expect(status).toEqual(201); + expect(body).toEqual(signupResponseDto.admin); + }); + + it('should not allow a second admin to sign up', async () => { + await signUpAdmin({ signUpDto: signupDto.admin }); + + const { status, body } = await request(app).post('/auth/admin-sign-up').send(signupDto.admin); + + expect(status).toBe(400); + expect(body).toEqual(errorDto.alreadyHasAdmin); + }); + }); +}); + +describe('/auth/*', () => { + let admin: LoginResponseDto; + + beforeEach(async () => { + await dbUtils.reset(); + await signUpAdmin({ signUpDto: signupDto.admin }); + admin = await login({ loginCredentialDto: loginDto.admin }); + }); + + describe(`POST /auth/login`, () => { + it('should reject an incorrect password', async () => { + const { status, body } = await request(app).post('/auth/login').send({ email, password: 'incorrect' }); + expect(status).toBe(401); + expect(body).toEqual(errorDto.incorrectLogin); + }); + + for (const key of Object.keys(loginDto.admin)) { + it(`should not allow null ${key}`, async () => { + const { status, body } = await request(app) + .post('/auth/login') + .send({ ...loginDto.admin, [key]: null }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest()); + }); + } + + it('should accept a correct password', async () => { + const { status, body, headers } = await request(app).post('/auth/login').send({ email, password }); + expect(status).toBe(201); + expect(body).toEqual(loginResponseDto.admin); + + const token = body.accessToken; + expect(token).toBeDefined(); + + const cookies = headers['set-cookie']; + expect(cookies).toHaveLength(3); + expect(cookies[0]).toEqual(`immich_access_token=${token}; HttpOnly; Path=/; Max-Age=34560000; SameSite=Lax;`); + expect(cookies[1]).toEqual('immich_auth_type=password; HttpOnly; Path=/; Max-Age=34560000; SameSite=Lax;'); + expect(cookies[2]).toEqual('immich_is_authenticated=true; Path=/; Max-Age=34560000; SameSite=Lax;'); + }); + }); + + describe('GET /auth/devices', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).get('/auth/devices'); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should get a list of authorized devices', async () => { + const { status, body } = await request(app) + .get('/auth/devices') + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(200); + expect(body).toEqual([deviceDto.current]); + }); + }); + + describe('DELETE /auth/devices', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).delete(`/auth/devices`); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should logout all devices (except the current one)', async () => { + for (let i = 0; i < 5; i++) { + await login({ loginCredentialDto: loginDto.admin }); + } + + await expect(getAuthDevices({ headers: asBearerAuth(admin.accessToken) })).resolves.toHaveLength(6); + + const { status } = await request(app).delete(`/auth/devices`).set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(204); + + await expect(getAuthDevices({ headers: asBearerAuth(admin.accessToken) })).resolves.toHaveLength(1); + }); + + it('should throw an error for a non-existent device id', async () => { + const { status, body } = await request(app) + .delete(`/auth/devices/${uuidDto.notFound}`) + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest('Not found or no authDevice.delete access')); + }); + + it('should logout a device', async () => { + const [device] = await getAuthDevices({ + headers: asBearerAuth(admin.accessToken), + }); + const { status } = await request(app) + .delete(`/auth/devices/${device.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(204); + + const response = await request(app) + .post('/auth/validateToken') + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(response.body).toEqual(errorDto.invalidToken); + expect(response.status).toBe(401); + }); + }); + + describe('POST /auth/validateToken', () => { + it('should reject an invalid token', async () => { + const { status, body } = await request(app).post(`/auth/validateToken`).set('Authorization', 'Bearer 123'); + expect(status).toBe(401); + expect(body).toEqual(errorDto.invalidToken); + }); + + it('should accept a valid token', async () => { + const { status, body } = await request(app) + .post(`/auth/validateToken`) + .send({}) + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(200); + expect(body).toEqual({ authStatus: true }); + }); + }); + + describe('POST /auth/change-password', () => { + it('should require authentication', async () => { + const { status, body } = await request(app) + .post(`/auth/change-password`) + .send({ password, newPassword: 'Password1234' }); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should require the current password', async () => { + const { status, body } = await request(app) + .post(`/auth/change-password`) + .send({ password: 'wrong-password', newPassword: 'Password1234' }) + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.wrongPassword); + }); + + it('should change the password', async () => { + const { status } = await request(app) + .post(`/auth/change-password`) + .send({ password, newPassword: 'Password1234' }) + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(200); + + await login({ + loginCredentialDto: { + email: 'admin@immich.cloud', + password: 'Password1234', + }, + }); + }); + }); + + describe('POST /auth/logout', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).post(`/auth/logout`); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should logout the user', async () => { + const { status, body } = await request(app) + .post(`/auth/logout`) + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(200); + expect(body).toEqual({ + successful: true, + redirectUri: '/auth/login?autoLaunch=0', + }); + }); + }); +}); diff --git a/e2e/src/api/specs/download.e2e-spec.ts b/e2e/src/api/specs/download.e2e-spec.ts new file mode 100644 index 0000000000..cf4aae3e0a --- /dev/null +++ b/e2e/src/api/specs/download.e2e-spec.ts @@ -0,0 +1,60 @@ +import { AssetFileUploadResponseDto, LoginResponseDto } from '@immich/sdk'; +import { errorDto } from 'src/responses'; +import { apiUtils, app, dbUtils } from 'src/utils'; +import request from 'supertest'; +import { beforeAll, describe, expect, it } from 'vitest'; + +describe('/download', () => { + let admin: LoginResponseDto; + let asset1: AssetFileUploadResponseDto; + + beforeAll(async () => { + apiUtils.setup(); + await dbUtils.reset(); + admin = await apiUtils.adminSetup(); + asset1 = await apiUtils.createAsset(admin.accessToken); + }); + + describe('POST /download/info', () => { + it('should require authentication', async () => { + const { status, body } = await request(app) + .post(`/download/info`) + .send({ assetIds: [asset1.id] }); + + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should download info', async () => { + const { status, body } = await request(app) + .post('/download/info') + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ assetIds: [asset1.id] }); + + expect(status).toBe(201); + expect(body).toEqual( + expect.objectContaining({ + archives: [expect.objectContaining({ assetIds: [asset1.id] })], + }), + ); + }); + }); + + describe('POST /download/asset/:id', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).post(`/download/asset/${asset1.id}`); + + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should download file', async () => { + const response = await request(app) + .post(`/download/asset/${asset1.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(response.status).toBe(200); + expect(response.headers['content-type']).toEqual('image/png'); + }); + }); +}); diff --git a/e2e/src/api/specs/library.e2e-spec.ts b/e2e/src/api/specs/library.e2e-spec.ts new file mode 100644 index 0000000000..8213cc86ea --- /dev/null +++ b/e2e/src/api/specs/library.e2e-spec.ts @@ -0,0 +1,456 @@ +import { LibraryResponseDto, LibraryType, LoginResponseDto, getAllLibraries } from '@immich/sdk'; +import { userDto, uuidDto } from 'src/fixtures'; +import { errorDto } from 'src/responses'; +import { apiUtils, app, asBearerAuth, dbUtils, testAssetDirInternal } from 'src/utils'; +import request from 'supertest'; +import { beforeAll, describe, expect, it } from 'vitest'; + +describe('/library', () => { + let admin: LoginResponseDto; + let user: LoginResponseDto; + let library: LibraryResponseDto; + + beforeAll(async () => { + apiUtils.setup(); + await dbUtils.reset(); + admin = await apiUtils.adminSetup(); + user = await apiUtils.userSetup(admin.accessToken, userDto.user1); + library = await apiUtils.createLibrary(admin.accessToken, { type: LibraryType.External }); + }); + + describe('GET /library', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).get('/library'); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should start with a default upload library', async () => { + const { status, body } = await request(app).get('/library').set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(200); + expect(body).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + ownerId: admin.userId, + type: LibraryType.Upload, + name: 'Default Library', + refreshedAt: null, + assetCount: 0, + importPaths: [], + exclusionPatterns: [], + }), + ]), + ); + }); + }); + + describe('POST /library', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).post('/library').send({}); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should require admin authentication', async () => { + const { status, body } = await request(app) + .post('/library') + .set('Authorization', `Bearer ${user.accessToken}`) + .send({ type: LibraryType.External }); + + expect(status).toBe(403); + expect(body).toEqual(errorDto.forbidden); + }); + + it('should create an external library with defaults', async () => { + const { status, body } = await request(app) + .post('/library') + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ type: LibraryType.External }); + + expect(status).toBe(201); + expect(body).toEqual( + expect.objectContaining({ + ownerId: admin.userId, + type: LibraryType.External, + name: 'New External Library', + refreshedAt: null, + assetCount: 0, + importPaths: [], + exclusionPatterns: [], + }), + ); + }); + + it('should create an external library with options', async () => { + const { status, body } = await request(app) + .post('/library') + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ + type: LibraryType.External, + name: 'My Awesome Library', + importPaths: ['/path/to/import'], + exclusionPatterns: ['**/Raw/**'], + }); + + expect(status).toBe(201); + expect(body).toEqual( + expect.objectContaining({ + name: 'My Awesome Library', + importPaths: ['/path/to/import'], + }), + ); + }); + + it('should not create an external library with duplicate import paths', async () => { + const { status, body } = await request(app) + .post('/library') + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ + type: LibraryType.External, + name: 'My Awesome Library', + importPaths: ['/path', '/path'], + exclusionPatterns: ['**/Raw/**'], + }); + + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(["All importPaths's elements must be unique"])); + }); + + it('should not create an external library with duplicate exclusion patterns', async () => { + const { status, body } = await request(app) + .post('/library') + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ + type: LibraryType.External, + name: 'My Awesome Library', + importPaths: ['/path/to/import'], + exclusionPatterns: ['**/Raw/**', '**/Raw/**'], + }); + + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(["All exclusionPatterns's elements must be unique"])); + }); + + it('should create an upload library with defaults', async () => { + const { status, body } = await request(app) + .post('/library') + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ type: LibraryType.Upload }); + + expect(status).toBe(201); + expect(body).toEqual( + expect.objectContaining({ + ownerId: admin.userId, + type: LibraryType.Upload, + name: 'New Upload Library', + refreshedAt: null, + assetCount: 0, + importPaths: [], + exclusionPatterns: [], + }), + ); + }); + + it('should create an upload library with options', async () => { + const { status, body } = await request(app) + .post('/library') + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ type: LibraryType.Upload, name: 'My Awesome Library' }); + + expect(status).toBe(201); + expect(body).toEqual( + expect.objectContaining({ + name: 'My Awesome Library', + }), + ); + }); + + it('should not allow upload libraries to have import paths', async () => { + const { status, body } = await request(app) + .post('/library') + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ type: LibraryType.Upload, importPaths: ['/path/to/import'] }); + + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest('Upload libraries cannot have import paths')); + }); + + it('should not allow upload libraries to have exclusion patterns', async () => { + const { status, body } = await request(app) + .post('/library') + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ type: LibraryType.Upload, exclusionPatterns: ['**/Raw/**'] }); + + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest('Upload libraries cannot have exclusion patterns')); + }); + }); + + describe('PUT /library/:id', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).put(`/library/${uuidDto.notFound}`).send({}); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should change the library name', async () => { + const { status, body } = await request(app) + .put(`/library/${library.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ name: 'New Library Name' }); + + expect(status).toBe(200); + expect(body).toEqual( + expect.objectContaining({ + name: 'New Library Name', + }), + ); + }); + + it('should not set an empty name', async () => { + const { status, body } = await request(app) + .put(`/library/${library.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ name: '' }); + + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['name should not be empty'])); + }); + + it('should change the import paths', async () => { + const { status, body } = await request(app) + .put(`/library/${library.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ importPaths: [testAssetDirInternal] }); + + expect(status).toBe(200); + expect(body).toEqual( + expect.objectContaining({ + importPaths: [testAssetDirInternal], + }), + ); + }); + + it('should reject an empty import path', async () => { + const { status, body } = await request(app) + .put(`/library/${library.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ importPaths: [''] }); + + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['each value in importPaths should not be empty'])); + }); + + it('should reject duplicate import paths', async () => { + const { status, body } = await request(app) + .put(`/library/${library.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ importPaths: ['/path', '/path'] }); + + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(["All importPaths's elements must be unique"])); + }); + + it('should change the exclusion pattern', async () => { + const { status, body } = await request(app) + .put(`/library/${library.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ exclusionPatterns: ['**/Raw/**'] }); + + expect(status).toBe(200); + expect(body).toEqual( + expect.objectContaining({ + exclusionPatterns: ['**/Raw/**'], + }), + ); + }); + + it('should reject duplicate exclusion patterns', async () => { + const { status, body } = await request(app) + .put(`/library/${library.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ exclusionPatterns: ['**/*.jpg', '**/*.jpg'] }); + + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(["All exclusionPatterns's elements must be unique"])); + }); + + it('should reject an empty exclusion pattern', async () => { + const { status, body } = await request(app) + .put(`/library/${library.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ exclusionPatterns: [''] }); + + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['each value in exclusionPatterns should not be empty'])); + }); + }); + + describe('GET /library/:id', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).get(`/library/${uuidDto.notFound}`); + + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should require admin access', async () => { + const { status, body } = await request(app) + .get(`/library/${uuidDto.notFound}`) + .set('Authorization', `Bearer ${user.accessToken}`); + expect(status).toBe(403); + expect(body).toEqual(errorDto.forbidden); + }); + + it('should get library by id', async () => { + const library = await apiUtils.createLibrary(admin.accessToken, { type: LibraryType.External }); + + const { status, body } = await request(app) + .get(`/library/${library.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(200); + expect(body).toEqual( + expect.objectContaining({ + ownerId: admin.userId, + type: LibraryType.External, + name: 'New External Library', + refreshedAt: null, + assetCount: 0, + importPaths: [], + exclusionPatterns: [], + }), + ); + }); + }); + + describe('DELETE /library/:id', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).delete(`/library/${uuidDto.notFound}`); + + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should not delete the last upload library', async () => { + const libraries = await getAllLibraries( + { $type: LibraryType.Upload }, + { headers: asBearerAuth(admin.accessToken) }, + ); + + const adminLibraries = libraries.filter((library) => library.ownerId === admin.userId); + expect(adminLibraries.length).toBeGreaterThanOrEqual(1); + const lastLibrary = adminLibraries.pop() as LibraryResponseDto; + + // delete all but the last upload library + for (const library of adminLibraries) { + const { status } = await request(app) + .delete(`/library/${library.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(204); + } + + const { status, body } = await request(app) + .delete(`/library/${lastLibrary.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(body).toEqual(errorDto.noDeleteUploadLibrary); + expect(status).toBe(400); + }); + + it('should delete an external library', async () => { + const library = await apiUtils.createLibrary(admin.accessToken, { type: LibraryType.External }); + + const { status, body } = await request(app) + .delete(`/library/${library.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(204); + expect(body).toEqual({}); + + const libraries = await getAllLibraries({}, { headers: asBearerAuth(admin.accessToken) }); + expect(libraries).not.toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: library.id, + }), + ]), + ); + }); + }); + + describe('GET /library/:id/statistics', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).get(`/library/${uuidDto.notFound}/statistics`); + + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + }); + + describe('POST /library/:id/scan', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).post(`/library/${uuidDto.notFound}/scan`).send({}); + + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + }); + + describe('POST /library/:id/removeOffline', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).post(`/library/${uuidDto.notFound}/removeOffline`).send({}); + + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + }); + + describe('POST /library/:id/validate', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).post(`/library/${uuidDto.notFound}/validate`).send({}); + + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should pass with no import paths', async () => { + const response = await apiUtils.validateLibrary(admin.accessToken, library.id, { importPaths: [] }); + expect(response.importPaths).toEqual([]); + }); + + it('should fail if path does not exist', async () => { + const pathToTest = `${testAssetDirInternal}/does/not/exist`; + + const response = await apiUtils.validateLibrary(admin.accessToken, library.id, { + importPaths: [pathToTest], + }); + + expect(response.importPaths?.length).toEqual(1); + const pathResponse = response?.importPaths?.at(0); + + expect(pathResponse).toEqual({ + importPath: pathToTest, + isValid: false, + message: `Path does not exist (ENOENT)`, + }); + }); + + it('should fail if path is a file', async () => { + const pathToTest = `${testAssetDirInternal}/albums/nature/el_torcal_rocks.jpg`; + + const response = await apiUtils.validateLibrary(admin.accessToken, library.id, { + importPaths: [pathToTest], + }); + + expect(response.importPaths?.length).toEqual(1); + const pathResponse = response?.importPaths?.at(0); + + expect(pathResponse).toEqual({ + importPath: pathToTest, + isValid: false, + message: `Not a directory`, + }); + }); + }); +}); diff --git a/e2e/src/api/specs/oauth.e2e-spec.ts b/e2e/src/api/specs/oauth.e2e-spec.ts new file mode 100644 index 0000000000..1324d3fa7f --- /dev/null +++ b/e2e/src/api/specs/oauth.e2e-spec.ts @@ -0,0 +1,23 @@ +import { errorDto } from 'src/responses'; +import { apiUtils, app, dbUtils } from 'src/utils'; +import request from 'supertest'; +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; + +describe(`/oauth`, () => { + beforeAll(() => { + apiUtils.setup(); + }); + + beforeEach(async () => { + await dbUtils.reset(); + await apiUtils.adminSetup(); + }); + + describe('POST /oauth/authorize', () => { + it(`should throw an error if a redirect uri is not provided`, async () => { + const { status, body } = await request(app).post('/oauth/authorize').send({}); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['redirectUri must be a string', 'redirectUri should not be empty'])); + }); + }); +}); diff --git a/server/e2e/api/specs/partner.e2e-spec.ts b/e2e/src/api/specs/partner.e2e-spec.ts similarity index 57% rename from server/e2e/api/specs/partner.e2e-spec.ts rename to e2e/src/api/specs/partner.e2e-spec.ts index b254aa61f2..2c88391bd4 100644 --- a/server/e2e/api/specs/partner.e2e-spec.ts +++ b/e2e/src/api/specs/partner.e2e-spec.ts @@ -1,68 +1,57 @@ -import { LoginResponseDto, PartnerDirection } from '@app/domain'; -import { PartnerController } from '@app/immich'; -import { errorStub, userDto } from '@test/fixtures'; +import { LoginResponseDto, createPartner } from '@immich/sdk'; +import { createUserDto } from 'src/fixtures'; +import { errorDto } from 'src/responses'; +import { apiUtils, app, asBearerAuth, dbUtils } from 'src/utils'; import request from 'supertest'; -import { api } from '../../client'; -import { testApp } from '../utils'; +import { beforeAll, describe, expect, it } from 'vitest'; -describe(`${PartnerController.name} (e2e)`, () => { - let server: any; +describe('/partner', () => { + let admin: LoginResponseDto; let user1: LoginResponseDto; let user2: LoginResponseDto; let user3: LoginResponseDto; beforeAll(async () => { - server = (await testApp.create()).getHttpServer(); + apiUtils.setup(); + await dbUtils.reset(); - await testApp.reset(); - await api.authApi.adminSignUp(server); - const admin = await api.authApi.adminLogin(server); - - await Promise.all([ - api.userApi.create(server, admin.accessToken, userDto.user1), - api.userApi.create(server, admin.accessToken, userDto.user2), - api.userApi.create(server, admin.accessToken, userDto.user3), - ]); + admin = await apiUtils.adminSetup(); [user1, user2, user3] = await Promise.all([ - api.authApi.login(server, userDto.user1), - api.authApi.login(server, userDto.user2), - api.authApi.login(server, userDto.user3), + apiUtils.userSetup(admin.accessToken, createUserDto.user1), + apiUtils.userSetup(admin.accessToken, createUserDto.user2), + apiUtils.userSetup(admin.accessToken, createUserDto.user3), ]); await Promise.all([ - api.partnerApi.create(server, user1.accessToken, user2.userId), - api.partnerApi.create(server, user2.accessToken, user1.userId), + createPartner({ id: user2.userId }, { headers: asBearerAuth(user1.accessToken) }), + createPartner({ id: user1.userId }, { headers: asBearerAuth(user2.accessToken) }), ]); }); - afterAll(async () => { - await testApp.teardown(); - }); - describe('GET /partner', () => { it('should require authentication', async () => { - const { status, body } = await request(server).get('/partner'); + const { status, body } = await request(app).get('/partner'); expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); + expect(body).toEqual(errorDto.unauthorized); }); it('should get all partners shared by user', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/partner') .set('Authorization', `Bearer ${user1.accessToken}`) - .query({ direction: PartnerDirection.SharedBy }); + .query({ direction: 'shared-by' }); expect(status).toBe(200); expect(body).toEqual([expect.objectContaining({ id: user2.userId })]); }); it('should get all partners that share with user', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/partner') .set('Authorization', `Bearer ${user1.accessToken}`) - .query({ direction: PartnerDirection.SharedWith }); + .query({ direction: 'shared-with' }); expect(status).toBe(200); expect(body).toEqual([expect.objectContaining({ id: user2.userId })]); @@ -71,14 +60,14 @@ describe(`${PartnerController.name} (e2e)`, () => { describe('POST /partner/:id', () => { it('should require authentication', async () => { - const { status, body } = await request(server).post(`/partner/${user3.userId}`); + const { status, body } = await request(app).post(`/partner/${user3.userId}`); expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); + expect(body).toEqual(errorDto.unauthorized); }); it('should share with new partner', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .post(`/partner/${user3.userId}`) .set('Authorization', `Bearer ${user1.accessToken}`); @@ -87,7 +76,7 @@ describe(`${PartnerController.name} (e2e)`, () => { }); it('should not share with new partner if already sharing with this partner', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .post(`/partner/${user2.userId}`) .set('Authorization', `Bearer ${user1.accessToken}`); @@ -98,14 +87,14 @@ describe(`${PartnerController.name} (e2e)`, () => { describe('PUT /partner/:id', () => { it('should require authentication', async () => { - const { status, body } = await request(server).put(`/partner/${user2.userId}`); + const { status, body } = await request(app).put(`/partner/${user2.userId}`); expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); + expect(body).toEqual(errorDto.unauthorized); }); it('should update partner', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .put(`/partner/${user2.userId}`) .set('Authorization', `Bearer ${user1.accessToken}`) .send({ inTimeline: false }); @@ -117,14 +106,14 @@ describe(`${PartnerController.name} (e2e)`, () => { describe('DELETE /partner/:id', () => { it('should require authentication', async () => { - const { status, body } = await request(server).delete(`/partner/${user3.userId}`); + const { status, body } = await request(app).delete(`/partner/${user3.userId}`); expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); + expect(body).toEqual(errorDto.unauthorized); }); it('should delete partner', async () => { - const { status } = await request(server) + const { status } = await request(app) .delete(`/partner/${user3.userId}`) .set('Authorization', `Bearer ${user1.accessToken}`); @@ -132,7 +121,7 @@ describe(`${PartnerController.name} (e2e)`, () => { }); it('should throw a bad request if partner not found', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .delete(`/partner/${user3.userId}`) .set('Authorization', `Bearer ${user1.accessToken}`); diff --git a/e2e/src/api/specs/person.e2e-spec.ts b/e2e/src/api/specs/person.e2e-spec.ts new file mode 100644 index 0000000000..77a10b343e --- /dev/null +++ b/e2e/src/api/specs/person.e2e-spec.ts @@ -0,0 +1,172 @@ +import { LoginResponseDto, PersonResponseDto } from '@immich/sdk'; +import { uuidDto } from 'src/fixtures'; +import { errorDto } from 'src/responses'; +import { apiUtils, app, dbUtils } from 'src/utils'; +import request from 'supertest'; +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; + +describe('/activity', () => { + let admin: LoginResponseDto; + let visiblePerson: PersonResponseDto; + let hiddenPerson: PersonResponseDto; + + beforeAll(async () => { + apiUtils.setup(); + await dbUtils.reset(); + admin = await apiUtils.adminSetup(); + }); + + beforeEach(async () => { + await dbUtils.reset(['person']); + + [visiblePerson, hiddenPerson] = await Promise.all([ + apiUtils.createPerson(admin.accessToken, { + name: 'visible_person', + }), + apiUtils.createPerson(admin.accessToken, { + name: 'hidden_person', + isHidden: true, + }), + ]); + + const asset = await apiUtils.createAsset(admin.accessToken); + + await Promise.all([ + dbUtils.createFace({ assetId: asset.id, personId: visiblePerson.id }), + dbUtils.createFace({ assetId: asset.id, personId: hiddenPerson.id }), + ]); + }); + + describe('GET /person', () => { + beforeEach(async () => {}); + + it('should require authentication', async () => { + const { status, body } = await request(app).get('/person'); + + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should return all people (including hidden)', async () => { + const { status, body } = await request(app) + .get('/person') + .set('Authorization', `Bearer ${admin.accessToken}`) + .query({ withHidden: true }); + + expect(status).toBe(200); + expect(body).toEqual({ + total: 2, + hidden: 1, + people: [ + expect.objectContaining({ name: 'visible_person' }), + expect.objectContaining({ name: 'hidden_person' }), + ], + }); + }); + + it('should return only visible people', async () => { + const { status, body } = await request(app).get('/person').set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(200); + expect(body).toEqual({ + total: 2, + hidden: 1, + people: [expect.objectContaining({ name: 'visible_person' })], + }); + }); + }); + + describe('GET /person/:id', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).get(`/person/${uuidDto.notFound}`); + + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should throw error if person with id does not exist', async () => { + const { status, body } = await request(app) + .get(`/person/${uuidDto.notFound}`) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest()); + }); + + it('should return person information', async () => { + const { status, body } = await request(app) + .get(`/person/${visiblePerson.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(200); + expect(body).toEqual(expect.objectContaining({ id: visiblePerson.id })); + }); + }); + + describe('PUT /person/:id', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).put(`/person/${uuidDto.notFound}`); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + for (const { key, type } of [ + { key: 'name', type: 'string' }, + { key: 'featureFaceAssetId', type: 'string' }, + { key: 'isHidden', type: 'boolean value' }, + ]) { + it(`should not allow null ${key}`, async () => { + const { status, body } = await request(app) + .put(`/person/${visiblePerson.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ [key]: null }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest([`${key} must be a ${type}`])); + }); + } + + it('should not accept invalid birth dates', async () => { + for (const { birthDate, response } of [ + { birthDate: false, response: 'Not found or no person.write access' }, + { birthDate: 'false', response: ['birthDate must be a Date instance'] }, + { + birthDate: '123567', + response: 'Not found or no person.write access', + }, + { birthDate: 123_567, response: 'Not found or no person.write access' }, + ]) { + const { status, body } = await request(app) + .put(`/person/${uuidDto.notFound}`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ birthDate }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(response)); + } + }); + + it('should update a date of birth', async () => { + const { status, body } = await request(app) + .put(`/person/${visiblePerson.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ birthDate: '1990-01-01T05:00:00.000Z' }); + expect(status).toBe(200); + expect(body).toMatchObject({ birthDate: '1990-01-01' }); + }); + + it('should clear a date of birth', async () => { + // TODO ironically this uses the update endpoint to create the person + const person = await apiUtils.createPerson(admin.accessToken, { + birthDate: new Date('1990-01-01').toISOString(), + }); + + expect(person.birthDate).toBeDefined(); + + const { status, body } = await request(app) + .put(`/person/${person.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ birthDate: null }); + expect(status).toBe(200); + expect(body).toMatchObject({ birthDate: null }); + }); + }); +}); diff --git a/server/e2e/api/specs/server-info.e2e-spec.ts b/e2e/src/api/specs/server-info.e2e-spec.ts similarity index 68% rename from server/e2e/api/specs/server-info.e2e-spec.ts rename to e2e/src/api/specs/server-info.e2e-spec.ts index f5664a11a3..7c8c45709e 100644 --- a/server/e2e/api/specs/server-info.e2e-spec.ts +++ b/e2e/src/api/specs/server-info.e2e-spec.ts @@ -1,38 +1,30 @@ -import { LoginResponseDto } from '@app/domain'; -import { ServerInfoController } from '@app/immich'; -import { errorStub, userDto } from '@test/fixtures'; +import { LoginResponseDto, getServerConfig } from '@immich/sdk'; +import { createUserDto } from 'src/fixtures'; +import { errorDto } from 'src/responses'; +import { apiUtils, app, dbUtils } from 'src/utils'; import request from 'supertest'; -import { api } from '../../client'; -import { testApp } from '../utils'; +import { beforeAll, describe, expect, it } from 'vitest'; -describe(`${ServerInfoController.name} (e2e)`, () => { - let server: any; +describe('/server-info', () => { let admin: LoginResponseDto; let nonAdmin: LoginResponseDto; beforeAll(async () => { - server = (await testApp.create()).getHttpServer(); - - await testApp.reset(); - await api.authApi.adminSignUp(server); - admin = await api.authApi.adminLogin(server); - await api.userApi.create(server, admin.accessToken, userDto.user1); - nonAdmin = await api.authApi.login(server, userDto.user1); - }); - - afterAll(async () => { - await testApp.teardown(); + apiUtils.setup(); + await dbUtils.reset(); + admin = await apiUtils.adminSetup({ onboarding: false }); + nonAdmin = await apiUtils.userSetup(admin.accessToken, createUserDto.user1); }); describe('GET /server-info', () => { it('should require authentication', async () => { - const { status, body } = await request(server).get('/server-info'); + const { status, body } = await request(app).get('/server-info'); expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); + expect(body).toEqual(errorDto.unauthorized); }); it('should return the disk information', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/server-info') .set('Authorization', `Bearer ${admin.accessToken}`); expect(status).toBe(200); @@ -50,7 +42,7 @@ describe(`${ServerInfoController.name} (e2e)`, () => { describe('GET /server-info/ping', () => { it('should respond with pong', async () => { - const { status, body } = await request(server).get('/server-info/ping'); + const { status, body } = await request(app).get('/server-info/ping'); expect(status).toBe(200); expect(body).toEqual({ res: 'pong' }); }); @@ -58,7 +50,7 @@ describe(`${ServerInfoController.name} (e2e)`, () => { describe('GET /server-info/version', () => { it('should respond with the server version', async () => { - const { status, body } = await request(server).get('/server-info/version'); + const { status, body } = await request(app).get('/server-info/version'); expect(status).toBe(200); expect(body).toEqual({ major: expect.any(Number), @@ -70,12 +62,12 @@ describe(`${ServerInfoController.name} (e2e)`, () => { describe('GET /server-info/features', () => { it('should respond with the server features', async () => { - const { status, body } = await request(server).get('/server-info/features'); + const { status, body } = await request(app).get('/server-info/features'); expect(status).toBe(200); expect(body).toEqual({ - smartSearch: true, + smartSearch: false, configFile: false, - facialRecognition: true, + facialRecognition: false, map: true, reverseGeocoding: true, oauth: false, @@ -90,7 +82,7 @@ describe(`${ServerInfoController.name} (e2e)`, () => { describe('GET /server-info/config', () => { it('should respond with the server configuration', async () => { - const { status, body } = await request(server).get('/server-info/config'); + const { status, body } = await request(app).get('/server-info/config'); expect(status).toBe(200); expect(body).toEqual({ loginPageMessage: '', @@ -105,21 +97,21 @@ describe(`${ServerInfoController.name} (e2e)`, () => { describe('GET /server-info/statistics', () => { it('should require authentication', async () => { - const { status, body } = await request(server).get('/server-info/statistics'); + const { status, body } = await request(app).get('/server-info/statistics'); expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); + expect(body).toEqual(errorDto.unauthorized); }); it('should only work for admins', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/server-info/statistics') .set('Authorization', `Bearer ${nonAdmin.accessToken}`); expect(status).toBe(403); - expect(body).toEqual(errorStub.forbidden); + expect(body).toEqual(errorDto.forbidden); }); it('should return the server stats', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/server-info/statistics') .set('Authorization', `Bearer ${admin.accessToken}`); expect(status).toBe(200); @@ -151,7 +143,7 @@ describe(`${ServerInfoController.name} (e2e)`, () => { describe('GET /server-info/media-types', () => { it('should return accepted media types', async () => { - const { status, body } = await request(server).get('/server-info/media-types'); + const { status, body } = await request(app).get('/server-info/media-types'); expect(status).toBe(200); expect(body).toEqual({ sidecar: ['.xmp'], @@ -163,7 +155,7 @@ describe(`${ServerInfoController.name} (e2e)`, () => { describe('GET /server-info/theme', () => { it('should respond with the server theme', async () => { - const { status, body } = await request(server).get('/server-info/theme'); + const { status, body } = await request(app).get('/server-info/theme'); expect(status).toBe(200); expect(body).toEqual({ customCss: '', @@ -173,15 +165,15 @@ describe(`${ServerInfoController.name} (e2e)`, () => { describe('POST /server-info/admin-onboarding', () => { it('should set admin onboarding', async () => { - const config = await api.serverInfoApi.getConfig(server); + const config = await getServerConfig({}); expect(config.isOnboarded).toBe(false); - const { status } = await request(server) + const { status } = await request(app) .post('/server-info/admin-onboarding') .set('Authorization', `Bearer ${admin.accessToken}`); expect(status).toBe(204); - const newConfig = await api.serverInfoApi.getConfig(server); + const newConfig = await getServerConfig({}); expect(newConfig.isOnboarded).toBe(true); }); }); diff --git a/server/e2e/api/specs/shared-link.e2e-spec.ts b/e2e/src/api/specs/shared-link.e2e-spec.ts similarity index 57% rename from server/e2e/api/specs/shared-link.e2e-spec.ts rename to e2e/src/api/specs/shared-link.e2e-spec.ts index 034b2f2637..f2e5b01867 100644 --- a/server/e2e/api/specs/shared-link.e2e-spec.ts +++ b/e2e/src/api/specs/shared-link.e2e-spec.ts @@ -1,24 +1,22 @@ import { AlbumResponseDto, - AssetResponseDto, - IAssetRepository, + AssetFileUploadResponseDto, LoginResponseDto, SharedLinkResponseDto, -} from '@app/domain'; -import { SharedLinkController } from '@app/immich'; -import { SharedLinkType } from '@app/infra/entities'; -import { INestApplication } from '@nestjs/common'; -import { errorStub, userDto, uuidStub } from '@test/fixtures'; -import { DateTime } from 'luxon'; + SharedLinkType, + createAlbum, + deleteUser, +} from '@immich/sdk'; +import { createUserDto, uuidDto } from 'src/fixtures'; +import { errorDto } from 'src/responses'; +import { apiUtils, app, asBearerAuth, dbUtils } from 'src/utils'; import request from 'supertest'; -import { api } from '../../client'; -import { testApp } from '../utils'; +import { beforeAll, describe, expect, it } from 'vitest'; -describe(`${SharedLinkController.name} (e2e)`, () => { - let server: any; +describe('/shared-link', () => { let admin: LoginResponseDto; - let asset1: AssetResponseDto; - let asset2: AssetResponseDto; + let asset1: AssetFileUploadResponseDto; + let asset2: AssetFileUploadResponseDto; let user1: LoginResponseDto; let user2: LoginResponseDto; let album: AlbumResponseDto; @@ -30,97 +28,81 @@ describe(`${SharedLinkController.name} (e2e)`, () => { let linkWithAssets: SharedLinkResponseDto; let linkWithMetadata: SharedLinkResponseDto; let linkWithoutMetadata: SharedLinkResponseDto; - let app: INestApplication; beforeAll(async () => { - app = await testApp.create(); - server = app.getHttpServer(); - const assetRepository = app.get(IAssetRepository); + apiUtils.setup(); + await dbUtils.reset(); - await testApp.reset(); - await api.authApi.adminSignUp(server); - admin = await api.authApi.adminLogin(server); - - await Promise.all([ - api.userApi.create(server, admin.accessToken, userDto.user1), - api.userApi.create(server, admin.accessToken, userDto.user2), - ]); + admin = await apiUtils.adminSetup(); [user1, user2] = await Promise.all([ - api.authApi.login(server, userDto.user1), - api.authApi.login(server, userDto.user2), + apiUtils.userSetup(admin.accessToken, createUserDto.user1), + apiUtils.userSetup(admin.accessToken, createUserDto.user2), ]); [asset1, asset2] = await Promise.all([ - api.assetApi.create(server, user1.accessToken), - api.assetApi.create(server, user1.accessToken), + apiUtils.createAsset(user1.accessToken), + apiUtils.createAsset(user1.accessToken), ]); - await assetRepository.upsertExif({ - assetId: asset1.id, - longitude: -108.400968333333, - latitude: 39.115, - orientation: '1', - dateTimeOriginal: DateTime.fromISO('2022-01-10T19:15:44.310Z').toJSDate(), - timeZone: 'UTC-4', - state: 'Mesa County, Colorado', - country: 'United States of America', - }); - [album, deletedAlbum, metadataAlbum] = await Promise.all([ - api.albumApi.create(server, user1.accessToken, { albumName: 'album' }), - api.albumApi.create(server, user2.accessToken, { albumName: 'deleted album' }), - api.albumApi.create(server, user1.accessToken, { albumName: 'metadata album', assetIds: [asset1.id] }), + createAlbum({ createAlbumDto: { albumName: 'album' } }, { headers: asBearerAuth(user1.accessToken) }), + createAlbum({ createAlbumDto: { albumName: 'deleted album' } }, { headers: asBearerAuth(user2.accessToken) }), + createAlbum( + { + createAlbumDto: { + albumName: 'metadata album', + assetIds: [asset1.id], + }, + }, + { headers: asBearerAuth(user1.accessToken) }, + ), ]); [linkWithDeletedAlbum, linkWithAlbum, linkWithAssets, linkWithPassword, linkWithMetadata, linkWithoutMetadata] = await Promise.all([ - api.sharedLinkApi.create(server, user2.accessToken, { - type: SharedLinkType.ALBUM, + apiUtils.createSharedLink(user2.accessToken, { + type: SharedLinkType.Album, albumId: deletedAlbum.id, }), - api.sharedLinkApi.create(server, user1.accessToken, { - type: SharedLinkType.ALBUM, + apiUtils.createSharedLink(user1.accessToken, { + type: SharedLinkType.Album, albumId: album.id, }), - api.sharedLinkApi.create(server, user1.accessToken, { - type: SharedLinkType.INDIVIDUAL, + apiUtils.createSharedLink(user1.accessToken, { + type: SharedLinkType.Individual, assetIds: [asset1.id], }), - api.sharedLinkApi.create(server, user1.accessToken, { - type: SharedLinkType.ALBUM, + apiUtils.createSharedLink(user1.accessToken, { + type: SharedLinkType.Album, albumId: album.id, password: 'foo', }), - api.sharedLinkApi.create(server, user1.accessToken, { - type: SharedLinkType.ALBUM, + apiUtils.createSharedLink(user1.accessToken, { + type: SharedLinkType.Album, albumId: metadataAlbum.id, showMetadata: true, }), - api.sharedLinkApi.create(server, user1.accessToken, { - type: SharedLinkType.ALBUM, + apiUtils.createSharedLink(user1.accessToken, { + type: SharedLinkType.Album, albumId: metadataAlbum.id, showMetadata: false, }), ]); - await api.userApi.delete(server, admin.accessToken, user2.userId); - }); - - afterAll(async () => { - await testApp.teardown(); + await deleteUser({ id: user2.userId }, { headers: asBearerAuth(admin.accessToken) }); }); describe('GET /shared-link', () => { it('should require authentication', async () => { - const { status, body } = await request(server).get('/shared-link'); + const { status, body } = await request(app).get('/shared-link'); expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); + expect(body).toEqual(errorDto.unauthorized); }); it('should get all shared links created by user', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/shared-link') .set('Authorization', `Bearer ${user1.accessToken}`); @@ -138,7 +120,7 @@ describe(`${SharedLinkController.name} (e2e)`, () => { }); it('should not get shared links created by other users', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/shared-link') .set('Authorization', `Bearer ${admin.accessToken}`); @@ -149,60 +131,64 @@ describe(`${SharedLinkController.name} (e2e)`, () => { describe('GET /shared-link/me', () => { it('should not require admin authentication', async () => { - const { status } = await request(server) - .get('/shared-link/me') - .set('Authorization', `Bearer ${admin.accessToken}`); + const { status } = await request(app).get('/shared-link/me').set('Authorization', `Bearer ${admin.accessToken}`); expect(status).toBe(403); }); it('should get data for correct shared link', async () => { - const { status, body } = await request(server).get('/shared-link/me').query({ key: linkWithAlbum.key }); + const { status, body } = await request(app).get('/shared-link/me').query({ key: linkWithAlbum.key }); expect(status).toBe(200); expect(body).toEqual( expect.objectContaining({ album, userId: user1.userId, - type: SharedLinkType.ALBUM, + type: SharedLinkType.Album, }), ); }); it('should return unauthorized for incorrect shared link', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/shared-link/me') .query({ key: linkWithAlbum.key + 'foo' }); expect(status).toBe(401); - expect(body).toEqual(errorStub.invalidShareKey); + expect(body).toEqual(errorDto.invalidShareKey); }); it('should return unauthorized if target has been soft deleted', async () => { - const { status, body } = await request(server).get('/shared-link/me').query({ key: linkWithDeletedAlbum.key }); + const { status, body } = await request(app).get('/shared-link/me').query({ key: linkWithDeletedAlbum.key }); expect(status).toBe(401); - expect(body).toEqual(errorStub.invalidShareKey); + expect(body).toEqual(errorDto.invalidShareKey); }); it('should return unauthorized for password protected link', async () => { - const { status, body } = await request(server).get('/shared-link/me').query({ key: linkWithPassword.key }); + const { status, body } = await request(app).get('/shared-link/me').query({ key: linkWithPassword.key }); expect(status).toBe(401); - expect(body).toEqual(errorStub.invalidSharePassword); + expect(body).toEqual(errorDto.invalidSharePassword); }); it('should get data for correct password protected link', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/shared-link/me') .query({ key: linkWithPassword.key, password: 'foo' }); expect(status).toBe(200); - expect(body).toEqual(expect.objectContaining({ album, userId: user1.userId, type: SharedLinkType.ALBUM })); + expect(body).toEqual( + expect.objectContaining({ + album, + userId: user1.userId, + type: SharedLinkType.Album, + }), + ); }); it('should return metadata for album shared link', async () => { - const { status, body } = await request(server).get('/shared-link/me').query({ key: linkWithMetadata.key }); + const { status, body } = await request(app).get('/shared-link/me').query({ key: linkWithMetadata.key }); expect(status).toBe(200); expect(body.assets).toHaveLength(1); @@ -211,22 +197,14 @@ describe(`${SharedLinkController.name} (e2e)`, () => { originalFileName: 'example', localDateTime: expect.any(String), fileCreatedAt: expect.any(String), - exifInfo: expect.objectContaining({ - longitude: -108.400968333333, - latitude: 39.115, - orientation: '1', - dateTimeOriginal: expect.any(String), - timeZone: 'UTC-4', - state: 'Mesa County, Colorado', - country: 'United States of America', - }), + exifInfo: expect.any(Object), }), ); expect(body.album).toBeDefined(); }); it('should not return metadata for album shared link without metadata', async () => { - const { status, body } = await request(server).get('/shared-link/me').query({ key: linkWithoutMetadata.key }); + const { status, body } = await request(app).get('/shared-link/me').query({ key: linkWithoutMetadata.key }); expect(status).toBe(200); expect(body.assets).toHaveLength(1); @@ -242,23 +220,29 @@ describe(`${SharedLinkController.name} (e2e)`, () => { describe('GET /shared-link/:id', () => { it('should require authentication', async () => { - const { status, body } = await request(server).get(`/shared-link/${linkWithAlbum.id}`); + const { status, body } = await request(app).get(`/shared-link/${linkWithAlbum.id}`); expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); + expect(body).toEqual(errorDto.unauthorized); }); it('should get shared link by id', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .get(`/shared-link/${linkWithAlbum.id}`) .set('Authorization', `Bearer ${user1.accessToken}`); expect(status).toBe(200); - expect(body).toEqual(expect.objectContaining({ album, userId: user1.userId, type: SharedLinkType.ALBUM })); + expect(body).toEqual( + expect.objectContaining({ + album, + userId: user1.userId, + type: SharedLinkType.Album, + }), + ); }); it('should not get shared link by id if user has not created the link or it does not exist', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .get(`/shared-link/${linkWithAlbum.id}`) .set('Authorization', `Bearer ${admin.accessToken}`); @@ -269,100 +253,109 @@ describe(`${SharedLinkController.name} (e2e)`, () => { describe('POST /shared-link', () => { it('should require authentication', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .post('/shared-link') - .send({ type: SharedLinkType.ALBUM, albumId: uuidStub.notFound }); + .send({ type: SharedLinkType.Album, albumId: uuidDto.notFound }); expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); + expect(body).toEqual(errorDto.unauthorized); }); it('should require a type and the correspondent asset/album id', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .post('/shared-link') .set('Authorization', `Bearer ${user1.accessToken}`); expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest()); + expect(body).toEqual(errorDto.badRequest()); }); it('should require an asset/album id', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .post('/shared-link') .set('Authorization', `Bearer ${user1.accessToken}`) - .send({ type: SharedLinkType.ALBUM }); + .send({ type: SharedLinkType.Album }); expect(status).toBe(400); expect(body).toEqual(expect.objectContaining({ message: 'Invalid albumId' })); }); it('should require a valid asset id', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .post('/shared-link') .set('Authorization', `Bearer ${user1.accessToken}`) - .send({ type: SharedLinkType.INDIVIDUAL, assetId: uuidStub.notFound }); + .send({ type: SharedLinkType.Individual, assetId: uuidDto.notFound }); expect(status).toBe(400); expect(body).toEqual(expect.objectContaining({ message: 'Invalid assetIds' })); }); it('should create a shared link', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .post('/shared-link') .set('Authorization', `Bearer ${user1.accessToken}`) - .send({ type: SharedLinkType.ALBUM, albumId: album.id }); + .send({ type: SharedLinkType.Album, albumId: album.id }); expect(status).toBe(201); - expect(body).toEqual(expect.objectContaining({ type: SharedLinkType.ALBUM, userId: user1.userId })); + expect(body).toEqual( + expect.objectContaining({ + type: SharedLinkType.Album, + userId: user1.userId, + }), + ); }); }); describe('PATCH /shared-link/:id', () => { it('should require authentication', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .patch(`/shared-link/${linkWithAlbum.id}`) .send({ description: 'foo' }); expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); + expect(body).toEqual(errorDto.unauthorized); }); it('should fail if invalid link', async () => { - const { status, body } = await request(server) - .patch(`/shared-link/${uuidStub.notFound}`) + const { status, body } = await request(app) + .patch(`/shared-link/${uuidDto.notFound}`) .set('Authorization', `Bearer ${user1.accessToken}`) .send({ description: 'foo' }); expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest()); + expect(body).toEqual(errorDto.badRequest()); }); it('should update shared link', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .patch(`/shared-link/${linkWithAlbum.id}`) .set('Authorization', `Bearer ${user1.accessToken}`) .send({ description: 'foo' }); expect(status).toBe(200); expect(body).toEqual( - expect.objectContaining({ type: SharedLinkType.ALBUM, userId: user1.userId, description: 'foo' }), + expect.objectContaining({ + type: SharedLinkType.Album, + userId: user1.userId, + description: 'foo', + }), ); }); }); describe('PUT /shared-link/:id/assets', () => { it('should not add assets to shared link (album)', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .put(`/shared-link/${linkWithAlbum.id}/assets`) .set('Authorization', `Bearer ${user1.accessToken}`) .send({ assetIds: [asset2.id] }); expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest('Invalid shared link type')); + expect(body).toEqual(errorDto.badRequest('Invalid shared link type')); }); it('should add an assets to a shared link (individual)', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .put(`/shared-link/${linkWithAssets.id}/assets`) .set('Authorization', `Bearer ${user1.accessToken}`) .send({ assetIds: [asset2.id] }); @@ -374,17 +367,17 @@ describe(`${SharedLinkController.name} (e2e)`, () => { describe('DELETE /shared-link/:id/assets', () => { it('should not remove assets from a shared link (album)', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .delete(`/shared-link/${linkWithAlbum.id}/assets`) .set('Authorization', `Bearer ${user1.accessToken}`) .send({ assetIds: [asset2.id] }); expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest('Invalid shared link type')); + expect(body).toEqual(errorDto.badRequest('Invalid shared link type')); }); it('should remove assets from a shared link (individual)', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .delete(`/shared-link/${linkWithAssets.id}/assets`) .set('Authorization', `Bearer ${user1.accessToken}`) .send({ assetIds: [asset2.id] }); @@ -396,23 +389,23 @@ describe(`${SharedLinkController.name} (e2e)`, () => { describe('DELETE /shared-link/:id', () => { it('should require authentication', async () => { - const { status, body } = await request(server).delete(`/shared-link/${linkWithAlbum.id}`); + const { status, body } = await request(app).delete(`/shared-link/${linkWithAlbum.id}`); expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); + expect(body).toEqual(errorDto.unauthorized); }); it('should fail if invalid link', async () => { - const { status, body } = await request(server) - .delete(`/shared-link/${uuidStub.notFound}`) + const { status, body } = await request(app) + .delete(`/shared-link/${uuidDto.notFound}`) .set('Authorization', `Bearer ${user1.accessToken}`); expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest()); + expect(body).toEqual(errorDto.badRequest()); }); it('should delete a shared link', async () => { - const { status } = await request(server) + const { status } = await request(app) .delete(`/shared-link/${linkWithAlbum.id}`) .set('Authorization', `Bearer ${user1.accessToken}`); diff --git a/server/e2e/api/specs/system-config.e2e-spec.ts b/e2e/src/api/specs/system-config.e2e-spec.ts similarity index 58% rename from server/e2e/api/specs/system-config.e2e-spec.ts rename to e2e/src/api/specs/system-config.e2e-spec.ts index a6b7387091..6d8880d3fc 100644 --- a/server/e2e/api/specs/system-config.e2e-spec.ts +++ b/e2e/src/api/specs/system-config.e2e-spec.ts @@ -1,49 +1,41 @@ -import { LoginResponseDto } from '@app/domain'; -import { SystemConfigController } from '@app/immich'; -import { errorStub, userDto } from '@test/fixtures'; +import { LoginResponseDto } from '@immich/sdk'; +import { createUserDto } from 'src/fixtures'; +import { errorDto } from 'src/responses'; +import { apiUtils, app, dbUtils } from 'src/utils'; import request from 'supertest'; -import { api } from '../../client'; -import { testApp } from '../utils'; +import { beforeAll, describe, expect, it } from 'vitest'; -describe(`${SystemConfigController.name} (e2e)`, () => { - let server: any; +describe('/system-config', () => { let admin: LoginResponseDto; let nonAdmin: LoginResponseDto; beforeAll(async () => { - server = (await testApp.create()).getHttpServer(); - - await testApp.reset(); - await api.authApi.adminSignUp(server); - admin = await api.authApi.adminLogin(server); - await api.userApi.create(server, admin.accessToken, userDto.user1); - nonAdmin = await api.authApi.login(server, userDto.user1); - }); - - afterAll(async () => { - await testApp.teardown(); + apiUtils.setup(); + await dbUtils.reset(); + admin = await apiUtils.adminSetup(); + nonAdmin = await apiUtils.userSetup(admin.accessToken, createUserDto.user1); }); describe('GET /system-config/map/style.json', () => { it('should require authentication', async () => { - const { status, body } = await request(server).get('/system-config/map/style.json'); + const { status, body } = await request(app).get('/system-config/map/style.json'); expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); + expect(body).toEqual(errorDto.unauthorized); }); it('should throw an error if a theme is not light or dark', async () => { for (const theme of ['dark1', true, 123, '', null, undefined]) { - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/system-config/map/style.json') .query({ theme }) .set('Authorization', `Bearer ${admin.accessToken}`); expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest(['theme must be one of the following values: light, dark'])); + expect(body).toEqual(errorDto.badRequest(['theme must be one of the following values: light, dark'])); } }); it('should return the light style.json', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/system-config/map/style.json') .query({ theme: 'light' }) .set('Authorization', `Bearer ${admin.accessToken}`); @@ -52,7 +44,7 @@ describe(`${SystemConfigController.name} (e2e)`, () => { }); it('should return the dark style.json', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/system-config/map/style.json') .query({ theme: 'dark' }) .set('Authorization', `Bearer ${admin.accessToken}`); @@ -61,7 +53,7 @@ describe(`${SystemConfigController.name} (e2e)`, () => { }); it('should not require admin authentication', async () => { - const { status, body } = await request(server) + const { status, body } = await request(app) .get('/system-config/map/style.json') .query({ theme: 'dark' }) .set('Authorization', `Bearer ${nonAdmin.accessToken}`); diff --git a/e2e/src/api/specs/trash.e2e-spec.ts b/e2e/src/api/specs/trash.e2e-spec.ts new file mode 100644 index 0000000000..60ed75f118 --- /dev/null +++ b/e2e/src/api/specs/trash.e2e-spec.ts @@ -0,0 +1,97 @@ +import { LoginResponseDto, getAllAssets } from '@immich/sdk'; +import { Socket } from 'socket.io-client'; +import { errorDto } from 'src/responses'; +import { apiUtils, app, asBearerAuth, dbUtils, wsUtils } from 'src/utils'; +import request from 'supertest'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; + +describe('/trash', () => { + let admin: LoginResponseDto; + let ws: Socket; + + beforeAll(async () => { + apiUtils.setup(); + await dbUtils.reset(); + admin = await apiUtils.adminSetup({ onboarding: false }); + ws = await wsUtils.connect(admin.accessToken); + }); + + afterAll(() => { + wsUtils.disconnect(ws); + }); + + describe('POST /trash/empty', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).post('/trash/empty'); + + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should empty the trash', async () => { + const { id: assetId } = await apiUtils.createAsset(admin.accessToken); + await apiUtils.deleteAssets(admin.accessToken, [assetId]); + + const before = await getAllAssets({}, { headers: asBearerAuth(admin.accessToken) }); + + expect(before.length).toBeGreaterThanOrEqual(1); + + const { status } = await request(app).post('/trash/empty').set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(204); + + await wsUtils.waitForEvent({ event: 'delete', assetId }); + + const after = await getAllAssets({}, { headers: asBearerAuth(admin.accessToken) }); + expect(after.length).toBe(0); + }); + }); + + describe('POST /trash/restore', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).post('/trash/restore'); + + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should restore all trashed assets', async () => { + const { id: assetId } = await apiUtils.createAsset(admin.accessToken); + await apiUtils.deleteAssets(admin.accessToken, [assetId]); + + const before = await apiUtils.getAssetInfo(admin.accessToken, assetId); + expect(before.isTrashed).toBe(true); + + const { status } = await request(app).post('/trash/restore').set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(204); + + const after = await apiUtils.getAssetInfo(admin.accessToken, assetId); + expect(after.isTrashed).toBe(false); + }); + }); + + describe('POST /trash/restore/assets', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).post('/trash/restore/assets'); + + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should restore a trashed asset by id', async () => { + const { id: assetId } = await apiUtils.createAsset(admin.accessToken); + await apiUtils.deleteAssets(admin.accessToken, [assetId]); + + const before = await apiUtils.getAssetInfo(admin.accessToken, assetId); + expect(before.isTrashed).toBe(true); + + const { status } = await request(app) + .post('/trash/restore/assets') + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ ids: [assetId] }); + expect(status).toBe(204); + + const after = await apiUtils.getAssetInfo(admin.accessToken, assetId); + expect(after.isTrashed).toBe(false); + }); + }); +}); diff --git a/e2e/src/api/specs/user.e2e-spec.ts b/e2e/src/api/specs/user.e2e-spec.ts new file mode 100644 index 0000000000..e47e1d531c --- /dev/null +++ b/e2e/src/api/specs/user.e2e-spec.ts @@ -0,0 +1,288 @@ +import { LoginResponseDto, deleteUser, getUserById } from '@immich/sdk'; +import { createUserDto, userDto } from 'src/fixtures'; +import { errorDto } from 'src/responses'; +import { apiUtils, app, asBearerAuth, dbUtils } from 'src/utils'; +import request from 'supertest'; +import { beforeAll, describe, expect, it } from 'vitest'; + +describe('/server-info', () => { + let admin: LoginResponseDto; + let deletedUser: LoginResponseDto; + let userToDelete: LoginResponseDto; + let nonAdmin: LoginResponseDto; + + beforeAll(async () => { + apiUtils.setup(); + await dbUtils.reset(); + admin = await apiUtils.adminSetup({ onboarding: false }); + + [deletedUser, nonAdmin, userToDelete] = await Promise.all([ + apiUtils.userSetup(admin.accessToken, createUserDto.user1), + apiUtils.userSetup(admin.accessToken, createUserDto.user2), + apiUtils.userSetup(admin.accessToken, createUserDto.user3), + ]); + + await deleteUser({ id: deletedUser.userId }, { headers: asBearerAuth(admin.accessToken) }); + }); + + describe('GET /user', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).get('/user'); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should get users', async () => { + const { status, body } = await request(app).get('/user').set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toEqual(200); + expect(body).toHaveLength(4); + expect(body).toEqual( + expect.arrayContaining([ + expect.objectContaining({ email: 'admin@immich.cloud' }), + expect.objectContaining({ email: 'user1@immich.cloud' }), + expect.objectContaining({ email: 'user2@immich.cloud' }), + expect.objectContaining({ email: 'user3@immich.cloud' }), + ]), + ); + }); + + it('should hide deleted users', async () => { + const { status, body } = await request(app) + .get(`/user`) + .query({ isAll: true }) + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(200); + expect(body).toHaveLength(3); + expect(body).toEqual( + expect.arrayContaining([ + expect.objectContaining({ email: 'admin@immich.cloud' }), + expect.objectContaining({ email: 'user2@immich.cloud' }), + expect.objectContaining({ email: 'user3@immich.cloud' }), + ]), + ); + }); + + it('should include deleted users', async () => { + const { status, body } = await request(app) + .get(`/user`) + .query({ isAll: false }) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(200); + expect(body).toHaveLength(4); + expect(body).toEqual( + expect.arrayContaining([ + expect.objectContaining({ email: 'admin@immich.cloud' }), + expect.objectContaining({ email: 'user1@immich.cloud' }), + expect.objectContaining({ email: 'user2@immich.cloud' }), + expect.objectContaining({ email: 'user3@immich.cloud' }), + ]), + ); + }); + }); + + describe('GET /user/info/:id', () => { + it('should require authentication', async () => { + const { status } = await request(app).get(`/user/info/${admin.userId}`); + expect(status).toEqual(401); + }); + + it('should get the user info', async () => { + const { status, body } = await request(app) + .get(`/user/info/${admin.userId}`) + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(200); + expect(body).toMatchObject({ + id: admin.userId, + email: 'admin@immich.cloud', + }); + }); + }); + + describe('GET /user/me', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).get(`/user/me`); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should get my info', async () => { + const { status, body } = await request(app).get(`/user/me`).set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(200); + expect(body).toMatchObject({ + id: admin.userId, + email: 'admin@immich.cloud', + }); + }); + }); + + describe('POST /user', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).post(`/user`).send(createUserDto.user1); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + for (const key of Object.keys(createUserDto.user1)) { + it(`should not allow null ${key}`, async () => { + const { status, body } = await request(app) + .post(`/user`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ ...createUserDto.user1, [key]: null }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest()); + }); + } + + it('should ignore `isAdmin`', async () => { + const { status, body } = await request(app) + .post(`/user`) + .send({ + isAdmin: true, + email: 'user4@immich.cloud', + password: 'password123', + name: 'Immich', + }) + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(body).toMatchObject({ + email: 'user4@immich.cloud', + isAdmin: false, + shouldChangePassword: true, + }); + expect(status).toBe(201); + }); + + it('should create a user without memories enabled', async () => { + const { status, body } = await request(app) + .post(`/user`) + .send({ + email: 'no-memories@immich.cloud', + password: 'Password123', + name: 'No Memories', + memoriesEnabled: false, + }) + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(body).toMatchObject({ + email: 'no-memories@immich.cloud', + memoriesEnabled: false, + }); + expect(status).toBe(201); + }); + }); + + describe('DELETE /user/:id', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).delete(`/user/${userToDelete.userId}`); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should delete user', async () => { + const { status, body } = await request(app) + .delete(`/user/${userToDelete.userId}`) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(200); + expect(body).toMatchObject({ + id: userToDelete.userId, + updatedAt: expect.any(String), + deletedAt: expect.any(String), + }); + }); + }); + + describe('PUT /user', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).put(`/user`); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + for (const key of Object.keys(userDto.admin)) { + it(`should not allow null ${key}`, async () => { + const { status, body } = await request(app) + .put(`/user`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ ...userDto.admin, [key]: null }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest()); + }); + } + + it('should not allow a non-admin to become an admin', async () => { + const { status, body } = await request(app) + .put(`/user`) + .send({ isAdmin: true, id: nonAdmin.userId }) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(400); + expect(body).toEqual(errorDto.alreadyHasAdmin); + }); + + it('ignores updates to profileImagePath', async () => { + const { status, body } = await request(app) + .put(`/user`) + .send({ id: admin.userId, profileImagePath: 'invalid.jpg' }) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(200); + expect(body).toMatchObject({ id: admin.userId, profileImagePath: '' }); + }); + + it('should ignore updates to createdAt, updatedAt and deletedAt', async () => { + const before = await getUserById({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) }); + + const { status, body } = await request(app) + .put(`/user`) + .send({ + id: admin.userId, + createdAt: '2023-01-01T00:00:00.000Z', + updatedAt: '2023-01-01T00:00:00.000Z', + deletedAt: '2023-01-01T00:00:00.000Z', + }) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(200); + expect(body).toStrictEqual(before); + }); + + it('should update first and last name', async () => { + const before = await getUserById({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) }); + + const { status, body } = await request(app) + .put(`/user`) + .send({ + id: admin.userId, + name: 'Name', + }) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(200); + expect(body).toEqual({ + ...before, + updatedAt: expect.any(String), + name: 'Name', + }); + expect(before.updatedAt).not.toEqual(body.updatedAt); + }); + + it('should update memories enabled', async () => { + const before = await getUserById({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) }); + const { status, body } = await request(app) + .put(`/user`) + .send({ + id: admin.userId, + memoriesEnabled: false, + }) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(200); + expect(body).toMatchObject({ + ...before, + updatedAt: expect.anything(), + memoriesEnabled: false, + }); + expect(before.updatedAt).not.toEqual(body.updatedAt); + }); + }); +}); diff --git a/e2e/src/cli/specs/login.e2e-spec.ts b/e2e/src/cli/specs/login.e2e-spec.ts new file mode 100644 index 0000000000..e3140ecea9 --- /dev/null +++ b/e2e/src/cli/specs/login.e2e-spec.ts @@ -0,0 +1,48 @@ +import { stat } from 'node:fs/promises'; +import { apiUtils, app, dbUtils, immichCli } from 'src/utils'; +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; + +describe(`immich login-key`, () => { + beforeAll(() => { + apiUtils.setup(); + }); + + beforeEach(async () => { + await dbUtils.reset(); + }); + + it('should require a url', async () => { + const { stderr, exitCode } = await immichCli(['login-key']); + expect(stderr).toBe("error: missing required argument 'url'"); + expect(exitCode).toBe(1); + }); + + it('should require a key', async () => { + const { stderr, exitCode } = await immichCli(['login-key', app]); + expect(stderr).toBe("error: missing required argument 'key'"); + expect(exitCode).toBe(1); + }); + + it('should require a valid key', async () => { + const { stderr, exitCode } = await immichCli(['login-key', app, 'immich-is-so-cool']); + expect(stderr).toContain('Failed to connect to server http://127.0.0.1:2283/api: Error: 401'); + expect(exitCode).toBe(1); + }); + + it('should login', async () => { + const admin = await apiUtils.adminSetup(); + const key = await apiUtils.createApiKey(admin.accessToken); + const { stdout, stderr, exitCode } = await immichCli(['login-key', app, `${key.secret}`]); + expect(stdout.split('\n')).toEqual([ + 'Logging in...', + 'Logged in as admin@immich.cloud', + 'Wrote auth info to /tmp/immich/auth.yml', + ]); + expect(stderr).toBe(''); + expect(exitCode).toBe(0); + + const stats = await stat('/tmp/immich/auth.yml'); + const mode = (stats.mode & 0o777).toString(8); + expect(mode).toEqual('600'); + }); +}); diff --git a/e2e/src/cli/specs/server-info.e2e-spec.ts b/e2e/src/cli/specs/server-info.e2e-spec.ts new file mode 100644 index 0000000000..038a2c2ca0 --- /dev/null +++ b/e2e/src/cli/specs/server-info.e2e-spec.ts @@ -0,0 +1,25 @@ +import { apiUtils, cliUtils, dbUtils, immichCli } from 'src/utils'; +import { beforeAll, describe, expect, it } from 'vitest'; + +describe(`immich server-info`, () => { + beforeAll(async () => { + apiUtils.setup(); + await dbUtils.reset(); + await cliUtils.login(); + }); + + it('should return the server info', async () => { + const { stderr, stdout, exitCode } = await immichCli(['server-info']); + expect(stdout.split('\n')).toEqual([ + expect.stringContaining('Server Version:'), + expect.stringContaining('Image Types:'), + expect.stringContaining('Video Types:'), + 'Statistics:', + ' Images: 0', + ' Videos: 0', + ' Total: 0', + ]); + expect(stderr).toBe(''); + expect(exitCode).toBe(0); + }); +}); diff --git a/e2e/src/cli/specs/upload.e2e-spec.ts b/e2e/src/cli/specs/upload.e2e-spec.ts new file mode 100644 index 0000000000..bda625241e --- /dev/null +++ b/e2e/src/cli/specs/upload.e2e-spec.ts @@ -0,0 +1,146 @@ +import { getAllAlbums, getAllAssets } from '@immich/sdk'; +import { mkdir, readdir, rm, symlink } from 'node:fs/promises'; +import { apiUtils, asKeyAuth, cliUtils, dbUtils, immichCli, testAssetDir } from 'src/utils'; +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; + +describe(`immich upload`, () => { + let key: string; + + beforeAll(async () => { + apiUtils.setup(); + await dbUtils.reset(); + key = await cliUtils.login(); + }); + + beforeEach(async () => { + await dbUtils.reset(['assets', 'albums']); + }); + + describe('immich upload --recursive', () => { + it('should upload a folder recursively', async () => { + const { stderr, stdout, exitCode } = await immichCli(['upload', `${testAssetDir}/albums/nature/`, '--recursive']); + expect(stderr).toBe(''); + expect(stdout.split('\n')).toEqual( + expect.arrayContaining([expect.stringContaining('Successfully uploaded 9 assets')]), + ); + expect(exitCode).toBe(0); + + const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); + expect(assets.length).toBe(9); + }); + }); + + describe('immich upload --recursive --album', () => { + it('should create albums from folder names', async () => { + const { stderr, stdout, exitCode } = await immichCli([ + 'upload', + `${testAssetDir}/albums/nature/`, + '--recursive', + '--album', + ]); + expect(stdout.split('\n')).toEqual( + expect.arrayContaining([ + expect.stringContaining('Successfully uploaded 9 assets'), + expect.stringContaining('Successfully created 1 new album'), + expect.stringContaining('Successfully updated 9 assets'), + ]), + ); + expect(stderr).toBe(''); + expect(exitCode).toBe(0); + + const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); + expect(assets.length).toBe(9); + + const albums = await getAllAlbums({}, { headers: asKeyAuth(key) }); + expect(albums.length).toBe(1); + expect(albums[0].albumName).toBe('nature'); + }); + + it('should add existing assets to albums', async () => { + const response1 = await immichCli(['upload', `${testAssetDir}/albums/nature/`, '--recursive']); + expect(response1.stdout.split('\n')).toEqual( + expect.arrayContaining([expect.stringContaining('Successfully uploaded 9 assets')]), + ); + expect(response1.stderr).toBe(''); + expect(response1.exitCode).toBe(0); + + const assets1 = await getAllAssets({}, { headers: asKeyAuth(key) }); + expect(assets1.length).toBe(9); + + const albums1 = await getAllAlbums({}, { headers: asKeyAuth(key) }); + expect(albums1.length).toBe(0); + + const response2 = await immichCli(['upload', `${testAssetDir}/albums/nature/`, '--recursive', '--album']); + expect(response2.stdout.split('\n')).toEqual( + expect.arrayContaining([ + expect.stringContaining('All assets were already uploaded, nothing to do.'), + expect.stringContaining('Successfully updated 9 assets'), + ]), + ); + expect(response2.stderr).toBe(''); + expect(response2.exitCode).toBe(0); + + const assets2 = await getAllAssets({}, { headers: asKeyAuth(key) }); + expect(assets2.length).toBe(9); + + const albums2 = await getAllAlbums({}, { headers: asKeyAuth(key) }); + expect(albums2.length).toBe(1); + expect(albums2[0].albumName).toBe('nature'); + }); + }); + + describe('immich upload --recursive --album-name=e2e', () => { + it('should create a named album', async () => { + const { stderr, stdout, exitCode } = await immichCli([ + 'upload', + `${testAssetDir}/albums/nature/`, + '--recursive', + '--album-name=e2e', + ]); + expect(stdout.split('\n')).toEqual( + expect.arrayContaining([ + expect.stringContaining('Successfully uploaded 9 assets'), + expect.stringContaining('Successfully created 1 new album'), + expect.stringContaining('Successfully updated 9 assets'), + ]), + ); + expect(stderr).toBe(''); + expect(exitCode).toBe(0); + + const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); + expect(assets.length).toBe(9); + + const albums = await getAllAlbums({}, { headers: asKeyAuth(key) }); + expect(albums.length).toBe(1); + expect(albums[0].albumName).toBe('e2e'); + }); + }); + + describe('immich upload --delete', () => { + it('should delete local files if specified', async () => { + await mkdir(`/tmp/albums/nature`, { recursive: true }); + const filesToLink = await readdir(`${testAssetDir}/albums/nature`); + for (const file of filesToLink) { + await symlink(`${testAssetDir}/albums/nature/${file}`, `/tmp/albums/nature/${file}`); + } + + const { stderr, stdout, exitCode } = await immichCli(['upload', `/tmp/albums/nature`, '--delete']); + + const files = await readdir(`/tmp/albums/nature`); + await rm(`/tmp/albums/nature`, { recursive: true }); + expect(files).toEqual([]); + + expect(stdout.split('\n')).toEqual( + expect.arrayContaining([ + expect.stringContaining('Successfully uploaded 9 assets'), + expect.stringContaining('Deleting assets that have been uploaded'), + ]), + ); + expect(stderr).toBe(''); + expect(exitCode).toBe(0); + + const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); + expect(assets.length).toBe(9); + }); + }); +}); diff --git a/e2e/src/cli/specs/version.e2e-spec.ts b/e2e/src/cli/specs/version.e2e-spec.ts new file mode 100644 index 0000000000..e94ccf214f --- /dev/null +++ b/e2e/src/cli/specs/version.e2e-spec.ts @@ -0,0 +1,29 @@ +import { readFileSync } from 'node:fs'; +import { apiUtils, immichCli } from 'src/utils'; +import { beforeAll, describe, expect, it } from 'vitest'; + +const pkg = JSON.parse(readFileSync('../cli/package.json', 'utf8')); + +describe(`immich --version`, () => { + beforeAll(() => { + apiUtils.setup(); + }); + + describe('immich --version', () => { + it('should print the cli version', async () => { + const { stdout, stderr, exitCode } = await immichCli(['--version']); + expect(stdout).toEqual(pkg.version); + expect(stderr).toEqual(''); + expect(exitCode).toBe(0); + }); + }); + + describe('immich -V', () => { + it('should print the cli version', async () => { + const { stdout, stderr, exitCode } = await immichCli(['-V']); + expect(stdout).toEqual(pkg.version); + expect(stderr).toEqual(''); + expect(exitCode).toBe(0); + }); + }); +}); diff --git a/e2e/src/fixtures.ts b/e2e/src/fixtures.ts new file mode 100644 index 0000000000..6a1a1b3968 --- /dev/null +++ b/e2e/src/fixtures.ts @@ -0,0 +1,78 @@ +import { UserAvatarColor } from '@immich/sdk'; + +export const uuidDto = { + invalid: 'invalid-uuid', + // valid uuid v4 + notFound: '00000000-0000-4000-a000-000000000000', +}; + +const adminLoginDto = { + email: 'admin@immich.cloud', + password: 'password', +}; +const adminSignupDto = { ...adminLoginDto, name: 'Immich Admin' }; + +export const loginDto = { + admin: adminLoginDto, +}; + +export const signupDto = { + admin: adminSignupDto, +}; + +export const createUserDto = { + user1: { + email: 'user1@immich.cloud', + name: 'User 1', + password: 'password1', + }, + user2: { + email: 'user2@immich.cloud', + name: 'User 2', + password: 'password12', + }, + user3: { + email: 'user3@immich.cloud', + name: 'User 3', + password: 'password123', + }, +}; + +export const userDto = { + admin: { + name: signupDto.admin.name, + email: signupDto.admin.email, + password: signupDto.admin.password, + storageLabel: 'admin', + oauthId: '', + shouldChangePassword: false, + profileImagePath: '', + createdAt: new Date('2021-01-01'), + deletedAt: null, + updatedAt: new Date('2021-01-01'), + tags: [], + assets: [], + memoriesEnabled: true, + avatarColor: UserAvatarColor.Primary, + quotaSizeInBytes: null, + quotaUsageInBytes: 0, + }, + user1: { + name: createUserDto.user1.name, + email: createUserDto.user1.email, + password: createUserDto.user1.password, + storageLabel: null, + oauthId: '', + shouldChangePassword: false, + profileImagePath: '', + createdAt: new Date('2021-01-01'), + deletedAt: null, + updatedAt: new Date('2021-01-01'), + tags: [], + assets: [], + memoriesEnabled: true, + avatarColor: UserAvatarColor.Primary, + quotaSizeInBytes: null, + quotaUsageInBytes: 0, + }, +}; diff --git a/e2e/src/generators.ts b/e2e/src/generators.ts new file mode 100644 index 0000000000..c87427ceab --- /dev/null +++ b/e2e/src/generators.ts @@ -0,0 +1,31 @@ +import { PNG } from 'pngjs'; + +const createPNG = (r: number, g: number, b: number) => { + const image = new PNG({ width: 1, height: 1 }); + image.data[0] = r; + image.data[1] = g; + image.data[2] = b; + image.data[3] = 255; + return PNG.sync.write(image); +}; + +function* newPngFactory() { + for (let r = 0; r < 255; r++) { + for (let g = 0; g < 255; g++) { + for (let b = 0; b < 255; b++) { + yield createPNG(r, g, b); + } + } + } +} + +const pngFactory = newPngFactory(); + +export const makeRandomImage = () => { + const { value } = pngFactory.next(); + if (!value) { + throw new Error('Ran out of random asset data'); + } + + return value; +}; diff --git a/e2e/src/responses.ts b/e2e/src/responses.ts new file mode 100644 index 0000000000..76e289ade2 --- /dev/null +++ b/e2e/src/responses.ts @@ -0,0 +1,102 @@ +import { expect } from 'vitest'; + +export const errorDto = { + unauthorized: { + error: 'Unauthorized', + statusCode: 401, + message: 'Authentication required', + }, + forbidden: { + error: 'Forbidden', + statusCode: 403, + message: expect.any(String), + }, + wrongPassword: { + error: 'Bad Request', + statusCode: 400, + message: 'Wrong password', + }, + invalidToken: { + error: 'Unauthorized', + statusCode: 401, + message: 'Invalid user token', + }, + invalidShareKey: { + error: 'Unauthorized', + statusCode: 401, + message: 'Invalid share key', + }, + invalidSharePassword: { + error: 'Unauthorized', + statusCode: 401, + message: 'Invalid password', + }, + badRequest: (message: any = null) => ({ + error: 'Bad Request', + statusCode: 400, + message: message ?? expect.anything(), + }), + noPermission: { + error: 'Bad Request', + statusCode: 400, + message: expect.stringContaining('Not found or no'), + }, + incorrectLogin: { + error: 'Unauthorized', + statusCode: 401, + message: 'Incorrect email or password', + }, + alreadyHasAdmin: { + error: 'Bad Request', + statusCode: 400, + message: 'The server already has an admin', + }, + noDeleteUploadLibrary: { + error: 'Bad Request', + statusCode: 400, + message: 'Cannot delete the last upload library', + }, +}; + +export const signupResponseDto = { + admin: { + avatarColor: expect.any(String), + id: expect.any(String), + name: 'Immich Admin', + email: 'admin@immich.cloud', + storageLabel: 'admin', + profileImagePath: '', + // why? lol + shouldChangePassword: true, + isAdmin: true, + createdAt: expect.any(String), + updatedAt: expect.any(String), + deletedAt: null, + oauthId: '', + memoriesEnabled: true, + quotaUsageInBytes: 0, + quotaSizeInBytes: null, + }, +}; + +export const loginResponseDto = { + admin: { + accessToken: expect.any(String), + name: 'Immich Admin', + isAdmin: true, + profileImagePath: '', + shouldChangePassword: true, + userEmail: 'admin@immich.cloud', + userId: expect.any(String), + }, +}; +export const deviceDto = { + current: { + id: expect.any(String), + createdAt: expect.any(String), + updatedAt: expect.any(String), + current: true, + deviceOS: '', + deviceType: '', + }, +}; diff --git a/e2e/src/setup.ts b/e2e/src/setup.ts new file mode 100644 index 0000000000..e0ff443566 --- /dev/null +++ b/e2e/src/setup.ts @@ -0,0 +1,24 @@ +import { exec, spawn } from 'node:child_process'; + +export default async () => { + let _resolve: () => unknown; + const ready = new Promise((resolve) => (_resolve = resolve)); + + const child = spawn('docker', ['compose', 'up'], { stdio: 'pipe' }); + + child.stdout.on('data', (data) => { + const input = data.toString(); + console.log(input); + if (input.includes('Immich Microservices is listening')) { + _resolve(); + } + }); + + child.stderr.on('data', (data) => console.log(data.toString())); + + await ready; + + return async () => { + await new Promise((resolve) => exec('docker compose down', () => resolve())); + }; +}; diff --git a/e2e/src/utils.ts b/e2e/src/utils.ts new file mode 100644 index 0000000000..34f25e396d --- /dev/null +++ b/e2e/src/utils.ts @@ -0,0 +1,371 @@ +import { + AssetFileUploadResponseDto, + AssetResponseDto, + CreateAlbumDto, + CreateAssetDto, + CreateLibraryDto, + CreateUserDto, + PersonUpdateDto, + SharedLinkCreateDto, + ValidateLibraryDto, + createAlbum, + createApiKey, + createLibrary, + createPerson, + createSharedLink, + createUser, + defaults, + deleteAssets, + getAssetInfo, + login, + setAdminOnboarding, + signUpAdmin, + updatePerson, + validate, +} from '@immich/sdk'; +import { BrowserContext } from '@playwright/test'; +import { exec, spawn } from 'node:child_process'; +import { access } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import path from 'node:path'; +import { promisify } from 'node:util'; +import pg from 'pg'; +import { io, type Socket } from 'socket.io-client'; +import { loginDto, signupDto } from 'src/fixtures'; +import { makeRandomImage } from 'src/generators'; +import request from 'supertest'; + +const execPromise = promisify(exec); + +export const app = 'http://127.0.0.1:2283/api'; + +const directoryExists = (directory: string) => + access(directory) + .then(() => true) + .catch(() => false); + +// TODO move test assets into e2e/assets +export const testAssetDir = path.resolve(`./../server/test/assets/`); +export const testAssetDirInternal = '/data/assets'; +export const tempDir = tmpdir(); + +const serverContainerName = 'immich-e2e-server'; +const mediaDir = '/usr/src/app/upload'; +const dirs = [ + `"${mediaDir}/thumbs"`, + `"${mediaDir}/upload"`, + `"${mediaDir}/library"`, + `"${mediaDir}/encoded-video"`, +].join(' '); + +if (!(await directoryExists(`${testAssetDir}/albums`))) { + throw new Error( + `Test assets not found. Please checkout https://github.com/immich-app/test-assets into ${testAssetDir} before testing`, + ); +} + +export const asBearerAuth = (accessToken: string) => ({ + Authorization: `Bearer ${accessToken}`, +}); + +export const asKeyAuth = (key: string) => ({ 'x-api-key': key }); + +let client: pg.Client | null = null; + +export const fileUtils = { + reset: async () => { + await execPromise(`docker exec -i "${serverContainerName}" /bin/bash -c "rm -rf ${dirs} && mkdir ${dirs}"`); + }, +}; + +export const dbUtils = { + createFace: async ({ assetId, personId }: { assetId: string; personId: string }) => { + if (!client) { + return; + } + + const vector = Array.from({ length: 512 }, Math.random); + const embedding = `[${vector.join(',')}]`; + + await client.query('INSERT INTO asset_faces ("assetId", "personId", "embedding") VALUES ($1, $2, $3)', [ + assetId, + personId, + embedding, + ]); + }, + setPersonThumbnail: async (personId: string) => { + if (!client) { + return; + } + + await client.query(`UPDATE "person" set "thumbnailPath" = '/my/awesome/thumbnail.jpg' where "id" = $1`, [personId]); + }, + reset: async (tables?: string[]) => { + try { + if (!client) { + client = new pg.Client('postgres://postgres:postgres@127.0.0.1:5433/immich'); + await client.connect(); + } + + tables = tables || [ + 'libraries', + 'shared_links', + 'person', + 'albums', + 'assets', + 'asset_faces', + 'activity', + 'api_keys', + 'user_token', + 'users', + 'system_metadata', + ]; + + for (const table of tables) { + await client.query(`DELETE FROM ${table} CASCADE;`); + } + } catch (error) { + console.error('Failed to reset database', error); + throw error; + } + }, + teardown: async () => { + try { + if (client) { + await client.end(); + client = null; + } + } catch (error) { + console.error('Failed to teardown database', error); + throw error; + } + }, +}; +export interface CliResponse { + stdout: string; + stderr: string; + exitCode: number | null; +} + +export const immichCli = async (args: string[]) => { + let _resolve: (value: CliResponse) => void; + const deferred = new Promise((resolve) => (_resolve = resolve)); + const _args = ['node_modules/.bin/immich', '-d', '/tmp/immich/', ...args]; + const child = spawn('node', _args, { + stdio: 'pipe', + }); + + let stdout = ''; + let stderr = ''; + + child.stdout.on('data', (data) => (stdout += data.toString())); + child.stderr.on('data', (data) => (stderr += data.toString())); + child.on('exit', (exitCode) => { + _resolve({ + stdout: stdout.trim(), + stderr: stderr.trim(), + exitCode, + }); + }); + + return deferred; +}; + +export interface AdminSetupOptions { + onboarding?: boolean; +} + +export enum SocketEvent { + UPLOAD = 'upload', + DELETE = 'delete', +} + +export type EventType = 'upload' | 'delete'; +export interface WaitOptions { + event: EventType; + assetId: string; + timeout?: number; +} + +const events: Record> = { + upload: new Set(), + delete: new Set(), +}; + +const callbacks: Record void> = {}; + +const onEvent = ({ event, assetId }: { event: EventType; assetId: string }) => { + events[event].add(assetId); + const callback = callbacks[assetId]; + if (callback) { + callback(); + delete callbacks[assetId]; + } +}; + +export const wsUtils = { + connect: async (accessToken: string) => { + const websocket = io('http://127.0.0.1:2283', { + path: '/api/socket.io', + transports: ['websocket'], + extraHeaders: { Authorization: `Bearer ${accessToken}` }, + autoConnect: true, + forceNew: true, + }); + + return new Promise((resolve) => { + websocket + .on('connect', () => resolve(websocket)) + .on('on_upload_success', (data: AssetResponseDto) => onEvent({ event: 'upload', assetId: data.id })) + .on('on_asset_delete', (assetId: string) => onEvent({ event: 'delete', assetId })) + .connect(); + }); + }, + disconnect: (ws: Socket) => { + if (ws?.connected) { + ws.disconnect(); + } + + for (const set of Object.values(events)) { + set.clear(); + } + }, + waitForEvent: async ({ event, assetId, timeout: ms }: WaitOptions): Promise => { + const set = events[event]; + if (set.has(assetId)) { + return; + } + + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => reject(new Error(`Timed out waiting for ${event} event`)), ms || 5000); + + callbacks[assetId] = () => { + clearTimeout(timeout); + resolve(); + }; + }); + }, +}; + +type AssetData = { bytes?: Buffer; filename: string }; + +export const apiUtils = { + setup: () => { + defaults.baseUrl = app; + }, + + adminSetup: async (options?: AdminSetupOptions) => { + options = options || { onboarding: true }; + + await signUpAdmin({ signUpDto: signupDto.admin }); + const response = await login({ loginCredentialDto: loginDto.admin }); + if (options.onboarding) { + await setAdminOnboarding({ headers: asBearerAuth(response.accessToken) }); + } + return response; + }, + userSetup: async (accessToken: string, dto: CreateUserDto) => { + await createUser({ createUserDto: dto }, { headers: asBearerAuth(accessToken) }); + return login({ + loginCredentialDto: { email: dto.email, password: dto.password }, + }); + }, + createApiKey: (accessToken: string) => { + return createApiKey({ apiKeyCreateDto: { name: 'e2e' } }, { headers: asBearerAuth(accessToken) }); + }, + createAlbum: (accessToken: string, dto: CreateAlbumDto) => + createAlbum({ createAlbumDto: dto }, { headers: asBearerAuth(accessToken) }), + createAsset: async ( + accessToken: string, + dto?: Partial> & { assetData?: AssetData }, + ) => { + const _dto = { + deviceAssetId: 'test-1', + deviceId: 'test', + fileCreatedAt: new Date().toISOString(), + fileModifiedAt: new Date().toISOString(), + ...dto, + }; + + const assetData = dto?.assetData?.bytes || makeRandomImage(); + const filename = dto?.assetData?.filename || 'example.png'; + + const builder = request(app) + .post(`/asset/upload`) + .attach('assetData', assetData, filename) + .set('Authorization', `Bearer ${accessToken}`); + + for (const [key, value] of Object.entries(_dto)) { + void builder.field(key, String(value)); + } + + const { body } = await builder; + + return body as AssetFileUploadResponseDto; + }, + getAssetInfo: (accessToken: string, id: string) => getAssetInfo({ id }, { headers: asBearerAuth(accessToken) }), + deleteAssets: (accessToken: string, ids: string[]) => + deleteAssets({ assetBulkDeleteDto: { ids } }, { headers: asBearerAuth(accessToken) }), + createPerson: async (accessToken: string, dto?: PersonUpdateDto) => { + // TODO fix createPerson to accept a body + const person = await createPerson({ headers: asBearerAuth(accessToken) }); + await dbUtils.setPersonThumbnail(person.id); + + if (!dto) { + return person; + } + + return updatePerson({ id: person.id, personUpdateDto: dto }, { headers: asBearerAuth(accessToken) }); + }, + createSharedLink: (accessToken: string, dto: SharedLinkCreateDto) => + createSharedLink({ sharedLinkCreateDto: dto }, { headers: asBearerAuth(accessToken) }), + createLibrary: (accessToken: string, dto: CreateLibraryDto) => + createLibrary({ createLibraryDto: dto }, { headers: asBearerAuth(accessToken) }), + validateLibrary: (accessToken: string, id: string, dto: ValidateLibraryDto) => + validate({ id, validateLibraryDto: dto }, { headers: asBearerAuth(accessToken) }), +}; + +export const cliUtils = { + login: async () => { + const admin = await apiUtils.adminSetup(); + const key = await apiUtils.createApiKey(admin.accessToken); + await immichCli(['login-key', app, `${key.secret}`]); + return key.secret; + }, +}; + +export const webUtils = { + setAuthCookies: async (context: BrowserContext, accessToken: string) => + await context.addCookies([ + { + name: 'immich_access_token', + value: accessToken, + domain: '127.0.0.1', + path: '/', + expires: 1_742_402_728, + httpOnly: true, + secure: false, + sameSite: 'Lax', + }, + { + name: 'immich_auth_type', + value: 'password', + domain: '127.0.0.1', + path: '/', + expires: 1_742_402_728, + httpOnly: true, + secure: false, + sameSite: 'Lax', + }, + { + name: 'immich_is_authenticated', + value: 'true', + domain: '127.0.0.1', + path: '/', + expires: 1_742_402_728, + httpOnly: false, + secure: false, + sameSite: 'Lax', + }, + ]), +}; diff --git a/e2e/specs/auth.e2e-spec.ts b/e2e/src/web/specs/auth.e2e-spec.ts similarity index 86% rename from e2e/specs/auth.e2e-spec.ts rename to e2e/src/web/specs/auth.e2e-spec.ts index 4c55d67ac1..23210205a3 100644 --- a/e2e/specs/auth.e2e-spec.ts +++ b/e2e/src/web/specs/auth.e2e-spec.ts @@ -1,13 +1,17 @@ -import { test, expect } from '@playwright/test'; -import { app } from '../test-utils'; +import { expect, test } from '@playwright/test'; +import { apiUtils, dbUtils, webUtils } from 'src/utils'; test.describe('Registration', () => { - test.beforeAll(async () => { - await app.reset(); + test.beforeAll(() => { + apiUtils.setup(); + }); + + test.beforeEach(async () => { + await dbUtils.reset(); }); test.afterAll(async () => { - await app.teardown(); + await dbUtils.teardown(); }); test('admin registration', async ({ page }) => { @@ -41,7 +45,8 @@ test.describe('Registration', () => { }); test('user registration', async ({ context, page }) => { - await app.adminSetup(context); + const admin = await apiUtils.adminSetup(); + await webUtils.setAuthCookies(context, admin.accessToken); // create user await page.goto('/admin/user-management'); @@ -63,7 +68,7 @@ test.describe('Registration', () => { await page.getByRole('button', { name: 'Login' }).click(); // change password - expect(page.getByRole('heading')).toHaveText('Change Password'); + await expect(page.getByRole('heading')).toHaveText('Change Password'); await expect(page).toHaveURL('/auth/change-password'); await page.getByLabel('New Password').fill('new-password'); await page.getByLabel('Confirm Password').fill('new-password'); diff --git a/e2e/src/web/specs/shared-link.e2e-spec.ts b/e2e/src/web/specs/shared-link.e2e-spec.ts new file mode 100644 index 0000000000..6b2dbad95c --- /dev/null +++ b/e2e/src/web/specs/shared-link.e2e-spec.ts @@ -0,0 +1,69 @@ +import { + AlbumResponseDto, + AssetFileUploadResponseDto, + LoginResponseDto, + SharedLinkResponseDto, + SharedLinkType, + createAlbum, +} from '@immich/sdk'; +import { test } from '@playwright/test'; +import { apiUtils, asBearerAuth, dbUtils } from 'src/utils'; + +test.describe('Shared Links', () => { + let admin: LoginResponseDto; + let asset: AssetFileUploadResponseDto; + let album: AlbumResponseDto; + let sharedLink: SharedLinkResponseDto; + let sharedLinkPassword: SharedLinkResponseDto; + + test.beforeAll(async () => { + apiUtils.setup(); + await dbUtils.reset(); + admin = await apiUtils.adminSetup(); + asset = await apiUtils.createAsset(admin.accessToken); + album = await createAlbum( + { + createAlbumDto: { + albumName: 'Test Album', + assetIds: [asset.id], + }, + }, + { headers: asBearerAuth(admin.accessToken) }, + ); + sharedLink = await apiUtils.createSharedLink(admin.accessToken, { + type: SharedLinkType.Album, + albumId: album.id, + }); + sharedLinkPassword = await apiUtils.createSharedLink(admin.accessToken, { + type: SharedLinkType.Album, + albumId: album.id, + password: 'test-password', + }); + }); + + test.afterAll(async () => { + await dbUtils.teardown(); + }); + + test('download from a shared link', async ({ page }) => { + await page.goto(`/share/${sharedLink.key}`); + await page.getByRole('heading', { name: 'Test Album' }).waitFor(); + await page.locator('.group > div').first().hover(); + await page.waitForSelector('#asset-group-by-date svg'); + await page.getByRole('checkbox').click(); + await page.getByRole('button', { name: 'Download' }).click(); + await page.getByText('DOWNLOADING', { exact: true }).waitFor(); + }); + + test('enter password for a shared link', async ({ page }) => { + await page.goto(`/share/${sharedLinkPassword.key}`); + await page.getByPlaceholder('Password').fill('test-password'); + await page.getByRole('button', { name: 'Submit' }).click(); + await page.getByRole('heading', { name: 'Test Album' }).waitFor(); + }); + + test('show error for invalid shared link', async ({ page }) => { + await page.goto('/share/invalid'); + await page.getByRole('heading', { name: 'Invalid share key' }).waitFor(); + }); +}); diff --git a/e2e/test-utils.ts b/e2e/test-utils.ts deleted file mode 100644 index f0d13be816..0000000000 --- a/e2e/test-utils.ts +++ /dev/null @@ -1,79 +0,0 @@ -import pg from 'pg'; -import { defaults, login, setAdminOnboarding, signUpAdmin } from '@immich/sdk'; -import { BrowserContext } from '@playwright/test'; - -const client = new pg.Client( - 'postgres://postgres:postgres@localhost:5432/immich' -); -let connected = false; - -const loginCredentialDto = { - email: 'admin@immich.cloud', - password: 'password', -}; -const signUpDto = { ...loginCredentialDto, name: 'Immich Admin' }; - -const setBaseUrl = () => (defaults.baseUrl = 'http://127.0.0.1:2283/api'); -const asAuthHeader = (accessToken: string) => ({ - Authorization: `Bearer ${accessToken}`, -}); - -export const app = { - adminSetup: async (context: BrowserContext) => { - setBaseUrl(); - await signUpAdmin({ signUpDto }); - - const response = await login({ loginCredentialDto }); - - await context.addCookies([ - { - name: 'immich_access_token', - value: response.accessToken, - domain: '127.0.0.1', - path: '/', - expires: 1742402728, - httpOnly: true, - secure: false, - sameSite: 'Lax', - }, - { - name: 'immich_auth_type', - value: 'password', - domain: '127.0.0.1', - path: '/', - expires: 1742402728, - httpOnly: true, - secure: false, - sameSite: 'Lax', - }, - { - name: 'immich_is_authenticated', - value: 'true', - domain: '127.0.0.1', - path: '/', - expires: 1742402728, - httpOnly: false, - secure: false, - sameSite: 'Lax', - }, - ]); - - await setAdminOnboarding({ headers: asAuthHeader(response.accessToken) }); - - return response; - }, - reset: async () => { - if (!connected) { - await client.connect(); - } - - for (const table of ['users', 'system_metadata']) { - await client.query(`DELETE FROM ${table} CASCADE;`); - } - }, - teardown: async () => { - if (connected) { - await client.end(); - } - }, -}; diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json index c91b03d9db..341d2ba189 100644 --- a/e2e/tsconfig.json +++ b/e2e/tsconfig.json @@ -16,8 +16,8 @@ "skipLibCheck": true, "esModuleInterop": true, "rootDirs": ["src"], - "baseUrl": "./", - "types": ["vitest/globals"] + "baseUrl": "./" }, + "include": ["src/**/*.ts"], "exclude": ["dist", "node_modules"] } diff --git a/e2e/vitest.config.ts b/e2e/vitest.config.ts new file mode 100644 index 0000000000..b8cc098ddd --- /dev/null +++ b/e2e/vitest.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from 'vitest/config'; + +// skip `docker compose up` if `make e2e` was already run +const globalSetup: string[] = []; +try { + await fetch('http://127.0.0.1:2283/api/server-info/ping'); +} catch { + globalSetup.push('src/setup.ts'); +} + +export default defineConfig({ + test: { + include: ['src/{api,cli}/specs/*.e2e-spec.ts'], + globalSetup, + poolOptions: { + threads: { + singleThread: true, + }, + }, + }, +}); diff --git a/install.sh b/install.sh index 6b266ef925..fc894ce21e 100755 --- a/install.sh +++ b/install.sh @@ -1,15 +1,13 @@ +#!/usr/bin/env bash + echo "Starting Immich installation..." ip_address=$(hostname -I | awk '{print $1}') -RED='\033[0;31m' -GREEN='\032[0;31m' -NC='\033[0m' # No Color - create_immich_directory() { echo "Creating Immich directory..." mkdir -p ./immich-app/immich-data - cd ./immich-app + cd ./immich-app || exit } download_docker_compose_file() { @@ -34,18 +32,18 @@ replace_env_value() { populate_upload_location() { echo "Populating default UPLOAD_LOCATION value..." upload_location=$(pwd)/immich-data - replace_env_value "UPLOAD_LOCATION" $upload_location + replace_env_value "UPLOAD_LOCATION" "$upload_location" } start_docker_compose() { echo "Starting Immich's docker containers" - if docker compose > /dev/null 2>&1; then + if docker compose >/dev/null 2>&1; then docker_bin="docker compose" - elif docker-compose > /dev/null 2>&1; then + elif docker-compose >/dev/null 2>&1; then docker_bin="docker-compose" else - echo 'Cannot find `docker compose` or `docker-compose`.' + echo "Cannot find \`docker compose\` or \`docker-compose\`." exit 1 fi @@ -59,7 +57,7 @@ start_docker_compose() { } show_friendly_message() { - echo "Succesfully deployed Immich!" + echo "Successfully deployed Immich!" echo "You can access the website at http://$ip_address:2283 and the server URL for the mobile app is http://$ip_address:2283/api" echo "The library location is $upload_location" echo "---------------------------------------------------" diff --git a/machine-learning/ann/__init__.py b/machine-learning/ann/__init__.py index 0793d1011b..e69de29bb2 100644 --- a/machine-learning/ann/__init__.py +++ b/machine-learning/ann/__init__.py @@ -1 +0,0 @@ -from .ann import Ann, is_available diff --git a/machine-learning/ann/ann.py b/machine-learning/ann/ann.py index 94f665bfc7..148d5ba101 100644 --- a/machine-learning/ann/ann.py +++ b/machine-learning/ann/ann.py @@ -32,8 +32,7 @@ T = TypeVar("T", covariant=True) class Newable(Protocol[T]): - def new(self) -> None: - ... + def new(self) -> None: ... class _Singleton(type, Newable[T]): diff --git a/machine-learning/ann/build.sh b/machine-learning/ann/build.sh index d90fa1ae1b..219c0ef1b1 100644 --- a/machine-learning/ann/build.sh +++ b/machine-learning/ann/build.sh @@ -1 +1,3 @@ -g++ -shared -O3 -o libann.so -fuse-ld=gold -std=c++17 -I$ARMNN_PATH/include -larmnn -larmnnDeserializer -larmnnTfLiteParser -larmnnOnnxParser -L$ARMNN_PATH ann.cpp +#!/usr/bin/env sh + +g++ -shared -O3 -o libann.so -fuse-ld=gold -std=c++17 -I"$ARMNN_PATH"/include -larmnn -larmnnDeserializer -larmnnTfLiteParser -larmnnOnnxParser -L"$ARMNN_PATH" ann.cpp diff --git a/machine-learning/ann/export/build-converter.sh b/machine-learning/ann/export/build-converter.sh index 0deb2e7ed5..94e9ebec2b 100755 --- a/machine-learning/ann/export/build-converter.sh +++ b/machine-learning/ann/export/build-converter.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env sh -cd armnn-23.11/ +cd armnn-23.11/ || exit g++ -o ../armnnconverter -O1 -DARMNN_ONNX_PARSER -DARMNN_SERIALIZER -DARMNN_TF_LITE_PARSER -fuse-ld=gold -std=c++17 -Iinclude -Isrc/armnnUtils -Ithird-party -larmnn -larmnnDeserializer -larmnnTfLiteParser -larmnnOnnxParser -larmnnSerializer -L../armnn src/armnnConverter/ArmnnConverter.cpp diff --git a/machine-learning/app/models/base.py b/machine-learning/app/models/base.py index 6097c7c987..ad48624b4e 100644 --- a/machine-learning/app/models/base.py +++ b/machine-learning/app/models/base.py @@ -1,18 +1,16 @@ from __future__ import annotations +import os from abc import ABC, abstractmethod from pathlib import Path from shutil import rmtree from typing import Any -import onnx import onnxruntime as ort from huggingface_hub import snapshot_download -from onnx.shape_inference import infer_shapes -from onnx.tools.update_model_dims import update_inputs_outputs_dims import ann.ann -from app.models.constants import STATIC_INPUT_PROVIDERS, SUPPORTED_PROVIDERS +from app.models.constants import SUPPORTED_PROVIDERS from ..config import get_cache_dir, get_hf_model_name, log, settings from ..schemas import ModelRuntime, ModelType @@ -113,63 +111,25 @@ class InferenceModel(ABC): ) model_path = onnx_path - if any(provider in STATIC_INPUT_PROVIDERS for provider in self.providers): - static_path = model_path.parent / "static_1" / "model.onnx" - static_path.parent.mkdir(parents=True, exist_ok=True) - if not static_path.is_file(): - self._convert_to_static(model_path, static_path) - model_path = static_path - match model_path.suffix: case ".armnn": session = AnnSession(model_path) case ".onnx": - session = ort.InferenceSession( - model_path.as_posix(), - sess_options=self.sess_options, - providers=self.providers, - provider_options=self.provider_options, - ) + cwd = os.getcwd() + try: + os.chdir(model_path.parent) + session = ort.InferenceSession( + model_path.as_posix(), + sess_options=self.sess_options, + providers=self.providers, + provider_options=self.provider_options, + ) + finally: + os.chdir(cwd) case _: raise ValueError(f"Unsupported model file type: {model_path.suffix}") return session - def _convert_to_static(self, source_path: Path, target_path: Path) -> None: - inferred = infer_shapes(onnx.load(source_path)) - inputs = self._get_static_dims(inferred.graph.input) - outputs = self._get_static_dims(inferred.graph.output) - - # check_model gets called in update_inputs_outputs_dims and doesn't work for large models - check_model = onnx.checker.check_model - try: - - def check_model_stub(*args: Any, **kwargs: Any) -> None: - pass - - onnx.checker.check_model = check_model_stub - updated_model = update_inputs_outputs_dims(inferred, inputs, outputs) - finally: - onnx.checker.check_model = check_model - - onnx.save( - updated_model, - target_path, - save_as_external_data=True, - all_tensors_to_one_file=False, - size_threshold=1048576, - ) - - def _get_static_dims(self, graph_io: Any, dim_size: int = 1) -> dict[str, list[int]]: - return { - field.name: [ - d.dim_value if d.HasField("dim_value") else dim_size - for shape in field.type.ListFields() - if (dim := shape[1].shape.dim) - for d in dim - ] - for field in graph_io - } - @property def model_type(self) -> ModelType: return self._model_type @@ -205,6 +165,14 @@ class InferenceModel(ABC): def providers_default(self) -> list[str]: available_providers = set(ort.get_available_providers()) log.debug(f"Available ORT providers: {available_providers}") + if (openvino := "OpenVINOExecutionProvider") in available_providers: + device_ids: list[str] = ort.capi._pybind_state.get_available_openvino_device_ids() + log.debug(f"Available OpenVINO devices: {device_ids}") + + gpu_devices = [device_id for device_id in device_ids if device_id.startswith("GPU")] + if not gpu_devices: + log.warning("No GPU device found in OpenVINO. Falling back to CPU.") + available_providers.remove(openvino) return [provider for provider in SUPPORTED_PROVIDERS if provider in available_providers] @property @@ -224,15 +192,7 @@ class InferenceModel(ABC): case "CPUExecutionProvider" | "CUDAExecutionProvider": option = {"arena_extend_strategy": "kSameAsRequested"} case "OpenVINOExecutionProvider": - try: - device_ids: list[str] = ort.capi._pybind_state.get_available_openvino_device_ids() - log.debug(f"Available OpenVINO devices: {device_ids}") - gpu_devices = [device_id for device_id in device_ids if device_id.startswith("GPU")] - option = {"device_id": gpu_devices[0]} if gpu_devices else {} - except AttributeError as e: - log.warning("Failed to get OpenVINO device IDs. Using default options.") - log.error(e) - option = {} + option = {"device_type": "GPU_FP32"} case _: option = {} options.append(option) diff --git a/machine-learning/app/models/constants.py b/machine-learning/app/models/constants.py index 18965d2b1d..b112e9279d 100644 --- a/machine-learning/app/models/constants.py +++ b/machine-learning/app/models/constants.py @@ -54,9 +54,6 @@ _INSIGHTFACE_MODELS = { SUPPORTED_PROVIDERS = ["CUDAExecutionProvider", "OpenVINOExecutionProvider", "CPUExecutionProvider"] -STATIC_INPUT_PROVIDERS = ["OpenVINOExecutionProvider"] - - def is_openclip(model_name: str) -> bool: return clean_name(model_name) in _OPENCLIP_MODELS diff --git a/machine-learning/app/test_main.py b/machine-learning/app/test_main.py index cf941c1bbf..0f802997fd 100644 --- a/machine-learning/app/test_main.py +++ b/machine-learning/app/test_main.py @@ -1,4 +1,5 @@ import json +import os from io import BytesIO from pathlib import Path from random import randint @@ -44,11 +45,23 @@ class TestBase: assert encoder.providers == self.CUDA_EP @pytest.mark.providers(OV_EP) - def test_sets_openvino_provider_if_available(self, providers: list[str]) -> None: + def test_sets_openvino_provider_if_available(self, providers: list[str], mocker: MockerFixture) -> None: + mocked = mocker.patch("app.models.base.ort.capi._pybind_state") + mocked.get_available_openvino_device_ids.return_value = ["GPU.0", "CPU"] + encoder = OpenCLIPEncoder("ViT-B-32__openai") assert encoder.providers == self.OV_EP + @pytest.mark.providers(OV_EP) + def test_avoids_openvino_if_gpu_not_available(self, providers: list[str], mocker: MockerFixture) -> None: + mocked = mocker.patch("app.models.base.ort.capi._pybind_state") + mocked.get_available_openvino_device_ids.return_value = ["CPU"] + + encoder = OpenCLIPEncoder("ViT-B-32__openai") + + assert encoder.providers == self.CPU_EP + @pytest.mark.providers(CUDA_EP_OUT_OF_ORDER) def test_sets_providers_in_correct_order(self, providers: list[str]) -> None: encoder = OpenCLIPEncoder("ViT-B-32__openai") @@ -67,22 +80,14 @@ class TestBase: assert encoder.providers == providers - def test_sets_default_provider_options(self) -> None: - encoder = OpenCLIPEncoder("ViT-B-32__openai", providers=["OpenVINOExecutionProvider", "CPUExecutionProvider"]) - - assert encoder.provider_options == [ - {}, - {"arena_extend_strategy": "kSameAsRequested"}, - ] - - def test_sets_openvino_device_id_if_possible(self, mocker: MockerFixture) -> None: + def test_sets_default_provider_options(self, mocker: MockerFixture) -> None: mocked = mocker.patch("app.models.base.ort.capi._pybind_state") mocked.get_available_openvino_device_ids.return_value = ["GPU.0", "CPU"] encoder = OpenCLIPEncoder("ViT-B-32__openai", providers=["OpenVINOExecutionProvider", "CPUExecutionProvider"]) assert encoder.provider_options == [ - {"device_id": "GPU.0"}, + {"device_type": "GPU_FP32"}, {"arena_extend_strategy": "kSameAsRequested"}, ] @@ -237,12 +242,12 @@ class TestBase: mock_model_path.is_file.return_value = True mock_model_path.suffix = ".armnn" mock_model_path.with_suffix.return_value = mock_model_path - mock_session = mocker.patch("app.models.base.AnnSession") + mock_ann = mocker.patch("app.models.base.AnnSession") encoder = OpenCLIPEncoder("ViT-B-32__openai") encoder._make_session(mock_model_path) - mock_session.assert_called_once() + mock_ann.assert_called_once() def test_make_session_return_ort_if_available_and_ann_is_not(self, mocker: MockerFixture) -> None: mock_armnn_path = mocker.Mock() @@ -256,6 +261,7 @@ class TestBase: mock_ann = mocker.patch("app.models.base.AnnSession") mock_ort = mocker.patch("app.models.base.ort.InferenceSession") + mocker.patch("app.models.base.os.chdir") encoder = OpenCLIPEncoder("ViT-B-32__openai") encoder._make_session(mock_armnn_path) @@ -278,6 +284,26 @@ class TestBase: mock_ann.assert_not_called() mock_ort.assert_not_called() + def test_make_session_changes_cwd(self, mocker: MockerFixture) -> None: + mock_model_path = mocker.Mock() + mock_model_path.is_file.return_value = True + mock_model_path.suffix = ".onnx" + mock_model_path.parent = "model_parent" + mock_model_path.with_suffix.return_value = mock_model_path + mock_ort = mocker.patch("app.models.base.ort.InferenceSession") + mock_chdir = mocker.patch("app.models.base.os.chdir") + + encoder = OpenCLIPEncoder("ViT-B-32__openai") + encoder._make_session(mock_model_path) + + mock_chdir.assert_has_calls( + [ + mock.call(mock_model_path.parent), + mock.call(os.getcwd()), + ] + ) + mock_ort.assert_called_once() + def test_download(self, mocker: MockerFixture) -> None: mock_snapshot_download = mocker.patch("app.models.base.snapshot_download") diff --git a/machine-learning/export/Dockerfile b/machine-learning/export/Dockerfile index 40402886ff..c22668380e 100644 --- a/machine-learning/export/Dockerfile +++ b/machine-learning/export/Dockerfile @@ -1,4 +1,4 @@ -FROM mambaorg/micromamba:bookworm-slim@sha256:926cac38640709f90f3fef2a3f730733b5c350be612f0d14706be8833b79ad8c as builder +FROM mambaorg/micromamba:bookworm-slim@sha256:6038b89363c9181215f3d9e8ce2720c880e224537f4028a854482e43a9b4998a as builder ENV NODE_ENV=production \ TRANSFORMERS_CACHE=/cache \ diff --git a/machine-learning/poetry.lock b/machine-learning/poetry.lock index 160b0b8e46..d7cff9c10b 100644 --- a/machine-learning/poetry.lock +++ b/machine-learning/poetry.lock @@ -64,33 +64,33 @@ trio = ["trio (>=0.23)"] [[package]] name = "black" -version = "24.1.1" +version = "24.2.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" files = [ - {file = "black-24.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2588021038bd5ada078de606f2a804cadd0a3cc6a79cb3e9bb3a8bf581325a4c"}, - {file = "black-24.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a95915c98d6e32ca43809d46d932e2abc5f1f7d582ffbe65a5b4d1588af7445"}, - {file = "black-24.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fa6a0e965779c8f2afb286f9ef798df770ba2b6cee063c650b96adec22c056a"}, - {file = "black-24.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:5242ecd9e990aeb995b6d03dc3b2d112d4a78f2083e5a8e86d566340ae80fec4"}, - {file = "black-24.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fc1ec9aa6f4d98d022101e015261c056ddebe3da6a8ccfc2c792cbe0349d48b7"}, - {file = "black-24.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0269dfdea12442022e88043d2910429bed717b2d04523867a85dacce535916b8"}, - {file = "black-24.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3d64db762eae4a5ce04b6e3dd745dcca0fb9560eb931a5be97472e38652a161"}, - {file = "black-24.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:5d7b06ea8816cbd4becfe5f70accae953c53c0e53aa98730ceccb0395520ee5d"}, - {file = "black-24.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e2c8dfa14677f90d976f68e0c923947ae68fa3961d61ee30976c388adc0b02c8"}, - {file = "black-24.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a21725862d0e855ae05da1dd25e3825ed712eaaccef6b03017fe0853a01aa45e"}, - {file = "black-24.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07204d078e25327aad9ed2c64790d681238686bce254c910de640c7cc4fc3aa6"}, - {file = "black-24.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:a83fe522d9698d8f9a101b860b1ee154c1d25f8a82ceb807d319f085b2627c5b"}, - {file = "black-24.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:08b34e85170d368c37ca7bf81cf67ac863c9d1963b2c1780c39102187ec8dd62"}, - {file = "black-24.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7258c27115c1e3b5de9ac6c4f9957e3ee2c02c0b39222a24dc7aa03ba0e986f5"}, - {file = "black-24.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40657e1b78212d582a0edecafef133cf1dd02e6677f539b669db4746150d38f6"}, - {file = "black-24.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e298d588744efda02379521a19639ebcd314fba7a49be22136204d7ed1782717"}, - {file = "black-24.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:34afe9da5056aa123b8bfda1664bfe6fb4e9c6f311d8e4a6eb089da9a9173bf9"}, - {file = "black-24.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:854c06fb86fd854140f37fb24dbf10621f5dab9e3b0c29a690ba595e3d543024"}, - {file = "black-24.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3897ae5a21ca132efa219c029cce5e6bfc9c3d34ed7e892113d199c0b1b444a2"}, - {file = "black-24.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:ecba2a15dfb2d97105be74bbfe5128bc5e9fa8477d8c46766505c1dda5883aac"}, - {file = "black-24.1.1-py3-none-any.whl", hash = "sha256:5cdc2e2195212208fbcae579b931407c1fa9997584f0a415421748aeafff1168"}, - {file = "black-24.1.1.tar.gz", hash = "sha256:48b5760dcbfe5cf97fd4fba23946681f3a81514c6ab8a45b50da67ac8fbc6c7b"}, + {file = "black-24.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29"}, + {file = "black-24.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430"}, + {file = "black-24.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f"}, + {file = "black-24.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a"}, + {file = "black-24.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd"}, + {file = "black-24.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2"}, + {file = "black-24.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92"}, + {file = "black-24.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23"}, + {file = "black-24.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b"}, + {file = "black-24.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9"}, + {file = "black-24.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693"}, + {file = "black-24.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982"}, + {file = "black-24.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4"}, + {file = "black-24.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218"}, + {file = "black-24.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0"}, + {file = "black-24.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d"}, + {file = "black-24.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8"}, + {file = "black-24.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8"}, + {file = "black-24.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540"}, + {file = "black-24.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31"}, + {file = "black-24.2.0-py3-none-any.whl", hash = "sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6"}, + {file = "black-24.2.0.tar.gz", hash = "sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894"}, ] [package.dependencies] @@ -1250,13 +1250,13 @@ test = ["Cython (>=0.29.24,<0.30.0)"] [[package]] name = "httpx" -version = "0.26.0" +version = "0.27.0" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpx-0.26.0-py3-none-any.whl", hash = "sha256:8915f5a3627c4d47b73e8202457cb28f1266982d1159bd5779d86a80c0eab1cd"}, - {file = "httpx-0.26.0.tar.gz", hash = "sha256:451b55c30d5185ea6b23c2c793abf9bb237d2a7dfb901ced6ff69ad37ec1dfaf"}, + {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, + {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, ] [package.dependencies] @@ -2101,61 +2101,61 @@ numpy = [ [[package]] name = "orjson" -version = "3.9.13" +version = "3.9.15" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ - {file = "orjson-3.9.13-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:fa6b67f8bef277c2a4aadd548d58796854e7d760964126c3209b19bccc6a74f1"}, - {file = "orjson-3.9.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b812417199eeb169c25f67815cfb66fd8de7ff098bf57d065e8c1943a7ba5c8f"}, - {file = "orjson-3.9.13-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7ccd5bd222e5041069ad9d9868ab59e6dbc53ecde8d8c82b919954fbba43b46b"}, - {file = "orjson-3.9.13-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eaaf80957c38e9d3f796f355a80fad945e72cd745e6b64c210e635b7043b673e"}, - {file = "orjson-3.9.13-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:60da7316131185d0110a1848e9ad15311e6c8938ee0b5be8cbd7261e1d80ee8f"}, - {file = "orjson-3.9.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b98cd948372f0eb219bc309dee4633db1278687161e3280d9e693b6076951d2"}, - {file = "orjson-3.9.13-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3869d65561f10071d3e7f35ae58fd377056f67d7aaed5222f318390c3ad30339"}, - {file = "orjson-3.9.13-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:43fd6036b16bb6742d03dae62f7bdf8214d06dea47e4353cde7e2bd1358d186f"}, - {file = "orjson-3.9.13-cp310-none-win32.whl", hash = "sha256:0d3ba9d88e20765335260d7b25547d7c571eee2b698200f97afa7d8c7cd668fc"}, - {file = "orjson-3.9.13-cp310-none-win_amd64.whl", hash = "sha256:6e47153db080f5e87e8ba638f1a8b18995eede6b0abb93964d58cf11bcea362f"}, - {file = "orjson-3.9.13-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4584e8eb727bc431baaf1bf97e35a1d8a0109c924ec847395673dfd5f4ef6d6f"}, - {file = "orjson-3.9.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f37f0cdd026ef777a4336e599d8194c8357fc14760c2a5ddcfdf1965d45504b"}, - {file = "orjson-3.9.13-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d714595d81efab11b42bccd119977d94b25d12d3a806851ff6bfd286a4bce960"}, - {file = "orjson-3.9.13-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9171e8e1a1f221953e38e84ae0abffe8759002fd8968106ee379febbb5358b33"}, - {file = "orjson-3.9.13-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ab9dbdec3f13f3ea6f937564ce21651844cfbf2725099f2f490426acf683c23"}, - {file = "orjson-3.9.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:811ac076855e33e931549340288e0761873baf29276ad00f221709933c644330"}, - {file = "orjson-3.9.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:860d0f5b42d0c0afd73fa4177709f6e1b966ba691fcd72175affa902052a81d6"}, - {file = "orjson-3.9.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:838b898e8c1f26eb6b8d81b180981273f6f5110c76c22c384979aca854194f1b"}, - {file = "orjson-3.9.13-cp311-none-win32.whl", hash = "sha256:d3222db9df629ef3c3673124f2e05fb72bc4a320c117e953fec0d69dde82e36d"}, - {file = "orjson-3.9.13-cp311-none-win_amd64.whl", hash = "sha256:978117122ca4cc59b28af5322253017f6c5fc03dbdda78c7f4b94ae984c8dd43"}, - {file = "orjson-3.9.13-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:031df1026c7ea8303332d78711f180231e3ae8b564271fb748a03926587c5546"}, - {file = "orjson-3.9.13-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fd9a2101d04e85086ea6198786a3f016e45475f800712e6833e14bf9ce2832f"}, - {file = "orjson-3.9.13-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:446d9ad04204e79229ae19502daeea56479e55cbc32634655d886f5a39e91b44"}, - {file = "orjson-3.9.13-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b57c0954a9fdd2b05b9cec0f5a12a0bdce5bf021a5b3b09323041613972481ab"}, - {file = "orjson-3.9.13-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:266e55c83f81248f63cc93d11c5e3a53df49a5d2598fa9e9db5f99837a802d5d"}, - {file = "orjson-3.9.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31372ba3a9fe8ad118e7d22fba46bbc18e89039e3bfa89db7bc8c18ee722dca8"}, - {file = "orjson-3.9.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e3b0c4da61f39899561e08e571f54472a09fa71717d9797928af558175ae5243"}, - {file = "orjson-3.9.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cc03a35bfc71c8ebf96ce49b82c2a7be6af4b3cd3ac34166fdb42ac510bbfff"}, - {file = "orjson-3.9.13-cp312-none-win_amd64.whl", hash = "sha256:49b7e3fe861cb246361825d1a238f2584ed8ea21e714bf6bb17cebb86772e61c"}, - {file = "orjson-3.9.13-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:62e9a99879c4d5a04926ac2518a992134bfa00d546ea5a4cae4b9be454d35a22"}, - {file = "orjson-3.9.13-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d92a3e835a5100f1d5b566fff79217eab92223ca31900dba733902a182a35ab0"}, - {file = "orjson-3.9.13-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:23f21faf072ed3b60b5954686f98157e073f6a8068eaa58dbde83e87212eda84"}, - {file = "orjson-3.9.13-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:828c502bb261588f7de897e06cb23c4b122997cb039d2014cb78e7dabe92ef0c"}, - {file = "orjson-3.9.13-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16946d095212a3dec552572c5d9bca7afa40f3116ad49695a397be07d529f1fa"}, - {file = "orjson-3.9.13-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3deadd8dc0e9ff844b5b656fa30a48dbee1c3b332d8278302dd9637f6b09f627"}, - {file = "orjson-3.9.13-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9b1b5adc5adf596c59dca57156b71ad301d73956f5bab4039b0e34dbf50b9fa0"}, - {file = "orjson-3.9.13-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ddc089315d030c54f0f03fb38286e2667c05009a78d659f108a8efcfbdf2e585"}, - {file = "orjson-3.9.13-cp38-none-win32.whl", hash = "sha256:ae77275a28667d9c82d4522b681504642055efa0368d73108511647c6499b31c"}, - {file = "orjson-3.9.13-cp38-none-win_amd64.whl", hash = "sha256:730385fdb99a21fce9bb84bb7fcbda72c88626facd74956bda712834b480729d"}, - {file = "orjson-3.9.13-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7e8e4a571d958910272af8d53a9cbe6599f9f5fd496a1bc51211183bb2072cbd"}, - {file = "orjson-3.9.13-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfad553a36548262e7da0f3a7464270e13900b898800fb571a5d4b298c3f8356"}, - {file = "orjson-3.9.13-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0d691c44604941945b00e0a13b19a7d9c1a19511abadf0080f373e98fdeb6b31"}, - {file = "orjson-3.9.13-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8c83718346de08d68b3cb1105c5d91e5fc39885d8610fdda16613d4e3941459"}, - {file = "orjson-3.9.13-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63ef57a53bfc2091a7cd50a640d9ae866bd7d92a5225a1bab6baa60ef62583f2"}, - {file = "orjson-3.9.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9156b96afa38db71344522f5517077eaedf62fcd2c9148392ff93d801128809c"}, - {file = "orjson-3.9.13-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31fb66b41fb2c4c817d9610f0bc7d31345728d7b5295ac78b63603407432a2b2"}, - {file = "orjson-3.9.13-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8a730bf07feacb0863974e67b206b7c503a62199de1cece2eb0d4c233ec29c11"}, - {file = "orjson-3.9.13-cp39-none-win32.whl", hash = "sha256:5ef58869f3399acbbe013518d8b374ee9558659eef14bca0984f67cb1fbd3c37"}, - {file = "orjson-3.9.13-cp39-none-win_amd64.whl", hash = "sha256:9bcf56efdb83244cde070e82a69c0f03c47c235f0a5cb6c81d9da23af7fbaae4"}, - {file = "orjson-3.9.13.tar.gz", hash = "sha256:fc6bc65b0cf524ee042e0bc2912b9206ef242edfba7426cf95763e4af01f527a"}, + {file = "orjson-3.9.15-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d61f7ce4727a9fa7680cd6f3986b0e2c732639f46a5e0156e550e35258aa313a"}, + {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4feeb41882e8aa17634b589533baafdceb387e01e117b1ec65534ec724023d04"}, + {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fbbeb3c9b2edb5fd044b2a070f127a0ac456ffd079cb82746fc84af01ef021a4"}, + {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b66bcc5670e8a6b78f0313bcb74774c8291f6f8aeef10fe70e910b8040f3ab75"}, + {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2973474811db7b35c30248d1129c64fd2bdf40d57d84beed2a9a379a6f57d0ab"}, + {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fe41b6f72f52d3da4db524c8653e46243c8c92df826ab5ffaece2dba9cccd58"}, + {file = "orjson-3.9.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4228aace81781cc9d05a3ec3a6d2673a1ad0d8725b4e915f1089803e9efd2b99"}, + {file = "orjson-3.9.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f7b65bfaf69493c73423ce9db66cfe9138b2f9ef62897486417a8fcb0a92bfe"}, + {file = "orjson-3.9.15-cp310-none-win32.whl", hash = "sha256:2d99e3c4c13a7b0fb3792cc04c2829c9db07838fb6973e578b85c1745e7d0ce7"}, + {file = "orjson-3.9.15-cp310-none-win_amd64.whl", hash = "sha256:b725da33e6e58e4a5d27958568484aa766e825e93aa20c26c91168be58e08cbb"}, + {file = "orjson-3.9.15-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c8e8fe01e435005d4421f183038fc70ca85d2c1e490f51fb972db92af6e047c2"}, + {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87f1097acb569dde17f246faa268759a71a2cb8c96dd392cd25c668b104cad2f"}, + {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff0f9913d82e1d1fadbd976424c316fbc4d9c525c81d047bbdd16bd27dd98cfc"}, + {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8055ec598605b0077e29652ccfe9372247474375e0e3f5775c91d9434e12d6b1"}, + {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6768a327ea1ba44c9114dba5fdda4a214bdb70129065cd0807eb5f010bfcbb5"}, + {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12365576039b1a5a47df01aadb353b68223da413e2e7f98c02403061aad34bde"}, + {file = "orjson-3.9.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:71c6b009d431b3839d7c14c3af86788b3cfac41e969e3e1c22f8a6ea13139404"}, + {file = "orjson-3.9.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e18668f1bd39e69b7fed19fa7cd1cd110a121ec25439328b5c89934e6d30d357"}, + {file = "orjson-3.9.15-cp311-none-win32.whl", hash = "sha256:62482873e0289cf7313461009bf62ac8b2e54bc6f00c6fabcde785709231a5d7"}, + {file = "orjson-3.9.15-cp311-none-win_amd64.whl", hash = "sha256:b3d336ed75d17c7b1af233a6561cf421dee41d9204aa3cfcc6c9c65cd5bb69a8"}, + {file = "orjson-3.9.15-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:82425dd5c7bd3adfe4e94c78e27e2fa02971750c2b7ffba648b0f5d5cc016a73"}, + {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c51378d4a8255b2e7c1e5cc430644f0939539deddfa77f6fac7b56a9784160a"}, + {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6ae4e06be04dc00618247c4ae3f7c3e561d5bc19ab6941427f6d3722a0875ef7"}, + {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcef128f970bb63ecf9a65f7beafd9b55e3aaf0efc271a4154050fc15cdb386e"}, + {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b72758f3ffc36ca566ba98a8e7f4f373b6c17c646ff8ad9b21ad10c29186f00d"}, + {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c57bc7b946cf2efa67ac55766e41764b66d40cbd9489041e637c1304400494"}, + {file = "orjson-3.9.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:946c3a1ef25338e78107fba746f299f926db408d34553b4754e90a7de1d44068"}, + {file = "orjson-3.9.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2f256d03957075fcb5923410058982aea85455d035607486ccb847f095442bda"}, + {file = "orjson-3.9.15-cp312-none-win_amd64.whl", hash = "sha256:5bb399e1b49db120653a31463b4a7b27cf2fbfe60469546baf681d1b39f4edf2"}, + {file = "orjson-3.9.15-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b17f0f14a9c0ba55ff6279a922d1932e24b13fc218a3e968ecdbf791b3682b25"}, + {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f6cbd8e6e446fb7e4ed5bac4661a29e43f38aeecbf60c4b900b825a353276a1"}, + {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:76bc6356d07c1d9f4b782813094d0caf1703b729d876ab6a676f3aaa9a47e37c"}, + {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fdfa97090e2d6f73dced247a2f2d8004ac6449df6568f30e7fa1a045767c69a6"}, + {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7413070a3e927e4207d00bd65f42d1b780fb0d32d7b1d951f6dc6ade318e1b5a"}, + {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9cf1596680ac1f01839dba32d496136bdd5d8ffb858c280fa82bbfeb173bdd40"}, + {file = "orjson-3.9.15-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:809d653c155e2cc4fd39ad69c08fdff7f4016c355ae4b88905219d3579e31eb7"}, + {file = "orjson-3.9.15-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:920fa5a0c5175ab14b9c78f6f820b75804fb4984423ee4c4f1e6d748f8b22bc1"}, + {file = "orjson-3.9.15-cp38-none-win32.whl", hash = "sha256:2b5c0f532905e60cf22a511120e3719b85d9c25d0e1c2a8abb20c4dede3b05a5"}, + {file = "orjson-3.9.15-cp38-none-win_amd64.whl", hash = "sha256:67384f588f7f8daf040114337d34a5188346e3fae6c38b6a19a2fe8c663a2f9b"}, + {file = "orjson-3.9.15-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6fc2fe4647927070df3d93f561d7e588a38865ea0040027662e3e541d592811e"}, + {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34cbcd216e7af5270f2ffa63a963346845eb71e174ea530867b7443892d77180"}, + {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f541587f5c558abd93cb0de491ce99a9ef8d1ae29dd6ab4dbb5a13281ae04cbd"}, + {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92255879280ef9c3c0bcb327c5a1b8ed694c290d61a6a532458264f887f052cb"}, + {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:05a1f57fb601c426635fcae9ddbe90dfc1ed42245eb4c75e4960440cac667262"}, + {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ede0bde16cc6e9b96633df1631fbcd66491d1063667f260a4f2386a098393790"}, + {file = "orjson-3.9.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e88b97ef13910e5f87bcbc4dd7979a7de9ba8702b54d3204ac587e83639c0c2b"}, + {file = "orjson-3.9.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57d5d8cf9c27f7ef6bc56a5925c7fbc76b61288ab674eb352c26ac780caa5b10"}, + {file = "orjson-3.9.15-cp39-none-win32.whl", hash = "sha256:001f4eb0ecd8e9ebd295722d0cbedf0748680fb9998d3993abaed2f40587257a"}, + {file = "orjson-3.9.15-cp39-none-win_amd64.whl", hash = "sha256:ea0b183a5fe6b2b45f3b854b0d19c4e932d6f5934ae1f723b07cf9560edd4ec7"}, + {file = "orjson-3.9.15.tar.gz", hash = "sha256:95cae920959d772f30ab36d3b25f83bb0f3be671e986c72ce22f8fa700dae061"}, ] [[package]] @@ -2465,13 +2465,13 @@ files = [ [[package]] name = "pytest" -version = "8.0.0" +version = "8.0.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.0.0-py3-none-any.whl", hash = "sha256:50fb9cbe836c3f20f0dfa99c565201fb75dc54c8d76373cd1bde06b06657bdb6"}, - {file = "pytest-8.0.0.tar.gz", hash = "sha256:249b1b0864530ba251b7438274c4d251c58d868edaaec8762893ad4a0d71c36c"}, + {file = "pytest-8.0.2-py3-none-any.whl", hash = "sha256:edfaaef32ce5172d5466b5127b42e0d6d35ebbe4453f0e3505d96afd93f6b096"}, + {file = "pytest-8.0.2.tar.gz", hash = "sha256:d4051d623a2e0b7e51960ba963193b09ce6daeb9759a451844a21e4ddedfc1bd"}, ] [package.dependencies] @@ -2836,28 +2836,28 @@ files = [ [[package]] name = "ruff" -version = "0.2.1" +version = "0.2.2" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.2.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:dd81b911d28925e7e8b323e8d06951554655021df8dd4ac3045d7212ac4ba080"}, - {file = "ruff-0.2.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dc586724a95b7d980aa17f671e173df00f0a2eef23f8babbeee663229a938fec"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c92db7101ef5bfc18e96777ed7bc7c822d545fa5977e90a585accac43d22f18a"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:13471684694d41ae0f1e8e3a7497e14cd57ccb7dd72ae08d56a159d6c9c3e30e"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a11567e20ea39d1f51aebd778685582d4c56ccb082c1161ffc10f79bebe6df35"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:00a818e2db63659570403e44383ab03c529c2b9678ba4ba6c105af7854008105"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be60592f9d218b52f03384d1325efa9d3b41e4c4d55ea022cd548547cc42cd2b"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbd2288890b88e8aab4499e55148805b58ec711053588cc2f0196a44f6e3d855"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ef052283da7dec1987bba8d8733051c2325654641dfe5877a4022108098683"}, - {file = "ruff-0.2.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7022d66366d6fded4ba3889f73cd791c2d5621b2ccf34befc752cb0df70f5fad"}, - {file = "ruff-0.2.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0a725823cb2a3f08ee743a534cb6935727d9e47409e4ad72c10a3faf042ad5ba"}, - {file = "ruff-0.2.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0034d5b6323e6e8fe91b2a1e55b02d92d0b582d2953a2b37a67a2d7dedbb7acc"}, - {file = "ruff-0.2.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e5cb5526d69bb9143c2e4d2a115d08ffca3d8e0fddc84925a7b54931c96f5c02"}, - {file = "ruff-0.2.1-py3-none-win32.whl", hash = "sha256:6b95ac9ce49b4fb390634d46d6ece32ace3acdd52814671ccaf20b7f60adb232"}, - {file = "ruff-0.2.1-py3-none-win_amd64.whl", hash = "sha256:e3affdcbc2afb6f5bd0eb3130139ceedc5e3f28d206fe49f63073cb9e65988e0"}, - {file = "ruff-0.2.1-py3-none-win_arm64.whl", hash = "sha256:efababa8e12330aa94a53e90a81eb6e2d55f348bc2e71adbf17d9cad23c03ee6"}, - {file = "ruff-0.2.1.tar.gz", hash = "sha256:3b42b5d8677cd0c72b99fcaf068ffc62abb5a19e71b4a3b9cfa50658a0af02f1"}, + {file = "ruff-0.2.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0a9efb032855ffb3c21f6405751d5e147b0c6b631e3ca3f6b20f917572b97eb6"}, + {file = "ruff-0.2.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d450b7fbff85913f866a5384d8912710936e2b96da74541c82c1b458472ddb39"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecd46e3106850a5c26aee114e562c329f9a1fbe9e4821b008c4404f64ff9ce73"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e22676a5b875bd72acd3d11d5fa9075d3a5f53b877fe7b4793e4673499318ba"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1695700d1e25a99d28f7a1636d85bafcc5030bba9d0578c0781ba1790dbcf51c"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b0c232af3d0bd8f521806223723456ffebf8e323bd1e4e82b0befb20ba18388e"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f63d96494eeec2fc70d909393bcd76c69f35334cdbd9e20d089fb3f0640216ca"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a61ea0ff048e06de273b2e45bd72629f470f5da8f71daf09fe481278b175001"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1439c8f407e4f356470e54cdecdca1bd5439a0673792dbe34a2b0a551a2fe3"}, + {file = "ruff-0.2.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:940de32dc8853eba0f67f7198b3e79bc6ba95c2edbfdfac2144c8235114d6726"}, + {file = "ruff-0.2.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0c126da55c38dd917621552ab430213bdb3273bb10ddb67bc4b761989210eb6e"}, + {file = "ruff-0.2.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3b65494f7e4bed2e74110dac1f0d17dc8e1f42faaa784e7c58a98e335ec83d7e"}, + {file = "ruff-0.2.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1ec49be4fe6ddac0503833f3ed8930528e26d1e60ad35c2446da372d16651ce9"}, + {file = "ruff-0.2.2-py3-none-win32.whl", hash = "sha256:d920499b576f6c68295bc04e7b17b6544d9d05f196bb3aac4358792ef6f34325"}, + {file = "ruff-0.2.2-py3-none-win_amd64.whl", hash = "sha256:cc9a91ae137d687f43a44c900e5d95e9617cb37d4c989e462980ba27039d239d"}, + {file = "ruff-0.2.2-py3-none-win_arm64.whl", hash = "sha256:c9d15fc41e6054bfc7200478720570078f0b41c9ae4f010bcc16bd6f4d1aacdd"}, + {file = "ruff-0.2.2.tar.gz", hash = "sha256:e62ed7f36b3068a30ba39193a14274cd706bc486fad521276458022f7bccb31d"}, ] [[package]] @@ -3096,121 +3096,121 @@ all = ["defusedxml", "fsspec", "imagecodecs (>=2023.8.12)", "lxml", "matplotlib" [[package]] name = "tokenizers" -version = "0.15.1" +version = "0.15.2" description = "" optional = false python-versions = ">=3.7" files = [ - {file = "tokenizers-0.15.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:32c9491dd1bcb33172c26b454dbd607276af959b9e78fa766e2694cafab3103c"}, - {file = "tokenizers-0.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29a1b784b870a097e7768f8c20c2dd851e2c75dad3efdae69a79d3e7f1d614d5"}, - {file = "tokenizers-0.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0049fbe648af04148b08cb211994ce8365ee628ce49724b56aaefd09a3007a78"}, - {file = "tokenizers-0.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e84b3c235219e75e24de6b71e6073cd2c8d740b14d88e4c6d131b90134e3a338"}, - {file = "tokenizers-0.15.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8cc575769ea11d074308c6d71cb10b036cdaec941562c07fc7431d956c502f0e"}, - {file = "tokenizers-0.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22bf28f299c4158e6d0b5eaebddfd500c4973d947ffeaca8bcbe2e8c137dff0b"}, - {file = "tokenizers-0.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:506555f98361db9c74e1323a862d77dcd7d64c2058829a368bf4159d986e339f"}, - {file = "tokenizers-0.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7061b0a28ade15906f5b2ec8c48d3bdd6e24eca6b427979af34954fbe31d5cef"}, - {file = "tokenizers-0.15.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7ed5e35507b7a0e2aac3285c4f5e37d4ec5cfc0e5825b862b68a0aaf2757af52"}, - {file = "tokenizers-0.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1c9df9247df0de6509dd751b1c086e5f124b220133b5c883bb691cb6fb3d786f"}, - {file = "tokenizers-0.15.1-cp310-none-win32.whl", hash = "sha256:dd999af1b4848bef1b11d289f04edaf189c269d5e6afa7a95fa1058644c3f021"}, - {file = "tokenizers-0.15.1-cp310-none-win_amd64.whl", hash = "sha256:39d06a57f7c06940d602fad98702cf7024c4eee7f6b9fe76b9f2197d5a4cc7e2"}, - {file = "tokenizers-0.15.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8ad034eb48bf728af06915e9294871f72fcc5254911eddec81d6df8dba1ce055"}, - {file = "tokenizers-0.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea9ede7c42f8fa90f31bfc40376fd91a7d83a4aa6ad38e6076de961d48585b26"}, - {file = "tokenizers-0.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b85d6fe1a20d903877aa0ef32ef6b96e81e0e48b71c206d6046ce16094de6970"}, - {file = "tokenizers-0.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a7d44f656320137c7d643b9c7dcc1814763385de737fb98fd2643880910f597"}, - {file = "tokenizers-0.15.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd244bd0793cdacf27ee65ec3db88c21f5815460e8872bbeb32b040469d6774e"}, - {file = "tokenizers-0.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f3f4a36e371b3cb1123adac8aeeeeab207ad32f15ed686d9d71686a093bb140"}, - {file = "tokenizers-0.15.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2921a53966afb29444da98d56a6ccbef23feb3b0c0f294b4e502370a0a64f25"}, - {file = "tokenizers-0.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f49068cf51f49c231067f1a8c9fc075ff960573f6b2a956e8e1b0154fb638ea5"}, - {file = "tokenizers-0.15.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0ab1a22f20eaaab832ab3b00a0709ca44a0eb04721e580277579411b622c741c"}, - {file = "tokenizers-0.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:671268f24b607c4adc6fa2b5b580fd4211b9f84b16bd7f46d62f8e5be0aa7ba4"}, - {file = "tokenizers-0.15.1-cp311-none-win32.whl", hash = "sha256:a4f03e33d2bf7df39c8894032aba599bf90f6f6378e683a19d28871f09bb07fc"}, - {file = "tokenizers-0.15.1-cp311-none-win_amd64.whl", hash = "sha256:30f689537bcc7576d8bd4daeeaa2cb8f36446ba2f13f421b173e88f2d8289c4e"}, - {file = "tokenizers-0.15.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f3a379dd0898a82ea3125e8f9c481373f73bffce6430d4315f0b6cd5547e409"}, - {file = "tokenizers-0.15.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d870ae58bba347d38ac3fc8b1f662f51e9c95272d776dd89f30035c83ee0a4f"}, - {file = "tokenizers-0.15.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d6d28e0143ec2e253a8a39e94bf1d24776dbe73804fa748675dbffff4a5cd6d8"}, - {file = "tokenizers-0.15.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61ae9ac9f44e2da128ee35db69489883b522f7abe033733fa54eb2de30dac23d"}, - {file = "tokenizers-0.15.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d8e322a47e29128300b3f7749a03c0ec2bce0a3dc8539ebff738d3f59e233542"}, - {file = "tokenizers-0.15.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:760334f475443bc13907b1a8e1cb0aeaf88aae489062546f9704dce6c498bfe2"}, - {file = "tokenizers-0.15.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1b173753d4aca1e7d0d4cb52b5e3ffecfb0ca014e070e40391b6bb4c1d6af3f2"}, - {file = "tokenizers-0.15.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82c1f13d457c8f0ab17e32e787d03470067fe8a3b4d012e7cc57cb3264529f4a"}, - {file = "tokenizers-0.15.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:425b46ceff4505f20191df54b50ac818055d9d55023d58ae32a5d895b6f15bb0"}, - {file = "tokenizers-0.15.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:681ac6ba3b4fdaf868ead8971221a061f580961c386e9732ea54d46c7b72f286"}, - {file = "tokenizers-0.15.1-cp312-none-win32.whl", hash = "sha256:f2272656063ccfba2044df2115095223960d80525d208e7a32f6c01c351a6f4a"}, - {file = "tokenizers-0.15.1-cp312-none-win_amd64.whl", hash = "sha256:9abe103203b1c6a2435d248d5ff4cceebcf46771bfbc4957a98a74da6ed37674"}, - {file = "tokenizers-0.15.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2ce9ed5c8ef26b026a66110e3c7b73d93ec2d26a0b1d0ea55ddce61c0e5f446f"}, - {file = "tokenizers-0.15.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:89b24d366137986c3647baac29ef902d2d5445003d11c30df52f1bd304689aeb"}, - {file = "tokenizers-0.15.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0faebedd01b413ab777ca0ee85914ed8b031ea5762ab0ea60b707ce8b9be6842"}, - {file = "tokenizers-0.15.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdbd9dfcdad4f3b95d801f768e143165165055c18e44ca79a8a26de889cd8e85"}, - {file = "tokenizers-0.15.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:97194324c12565b07e9993ca9aa813b939541185682e859fb45bb8d7d99b3193"}, - {file = "tokenizers-0.15.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:485e43e2cc159580e0d83fc919ec3a45ae279097f634b1ffe371869ffda5802c"}, - {file = "tokenizers-0.15.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:191d084d60e3589d6420caeb3f9966168269315f8ec7fbc3883122dc9d99759d"}, - {file = "tokenizers-0.15.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01c28cc8d7220634a75b14c53f4fc9d1b485f99a5a29306a999c115921de2897"}, - {file = "tokenizers-0.15.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:325212027745d3f8d5d5006bb9e5409d674eb80a184f19873f4f83494e1fdd26"}, - {file = "tokenizers-0.15.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3c5573603c36ce12dbe318bcfb490a94cad2d250f34deb2f06cb6937957bbb71"}, - {file = "tokenizers-0.15.1-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:1441161adb6d71a15a630d5c1d8659d5ebe41b6b209586fbeea64738e58fcbb2"}, - {file = "tokenizers-0.15.1-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:382a8d0c31afcfb86571afbfefa37186df90865ce3f5b731842dab4460e53a38"}, - {file = "tokenizers-0.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e76959783e3f4ec73b3f3d24d4eec5aa9225f0bee565c48e77f806ed1e048f12"}, - {file = "tokenizers-0.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:401df223e5eb927c5961a0fc6b171818a2bba01fb36ef18c3e1b69b8cd80e591"}, - {file = "tokenizers-0.15.1-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52606c233c759561a16e81b2290a7738c3affac7a0b1f0a16fe58dc22e04c7d"}, - {file = "tokenizers-0.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b72c658bbe5a05ed8bc2ac5ad782385bfd743ffa4bc87d9b5026341e709c6f44"}, - {file = "tokenizers-0.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:25f5643a2f005c42f0737a326c6c6bdfedfdc9a994b10a1923d9c3e792e4d6a6"}, - {file = "tokenizers-0.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c5b6f633999d6b42466bbfe21be2e26ad1760b6f106967a591a41d8cbca980e"}, - {file = "tokenizers-0.15.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ceb5c9ad11a015150b545c1a11210966a45b8c3d68a942e57cf8938c578a77ca"}, - {file = "tokenizers-0.15.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bedd4ce0c4872db193444c395b11c7697260ce86a635ab6d48102d76be07d324"}, - {file = "tokenizers-0.15.1-cp37-none-win32.whl", hash = "sha256:cd6caef6c14f5ed6d35f0ddb78eab8ca6306d0cd9870330bccff72ad014a6f42"}, - {file = "tokenizers-0.15.1-cp37-none-win_amd64.whl", hash = "sha256:d2bd7af78f58d75a55e5df61efae164ab9200c04b76025f9cc6eeb7aff3219c2"}, - {file = "tokenizers-0.15.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:59b3ca6c02e0bd5704caee274978bd055de2dff2e2f39dadf536c21032dfd432"}, - {file = "tokenizers-0.15.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:48fe21b67c22583bed71933a025fd66b1f5cfae1baefa423c3d40379b5a6e74e"}, - {file = "tokenizers-0.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3d190254c66a20fb1efbdf035e6333c5e1f1c73b1f7bfad88f9c31908ac2c2c4"}, - {file = "tokenizers-0.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fef90c8f5abf17d48d6635f5fd92ad258acd1d0c2d920935c8bf261782cfe7c8"}, - {file = "tokenizers-0.15.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fac011ef7da3357aa7eb19efeecf3d201ede9618f37ddedddc5eb809ea0963ca"}, - {file = "tokenizers-0.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:574ec5b3e71d1feda6b0ecac0e0445875729b4899806efbe2b329909ec75cb50"}, - {file = "tokenizers-0.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aca16c3c0637c051a59ea99c4253f16fbb43034fac849076a7e7913b2b9afd2d"}, - {file = "tokenizers-0.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a6f238fc2bbfd3e12e8529980ec1624c7e5b69d4e959edb3d902f36974f725a"}, - {file = "tokenizers-0.15.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:587e11a26835b73c31867a728f32ca8a93c9ded4a6cd746516e68b9d51418431"}, - {file = "tokenizers-0.15.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6456e7ad397352775e2efdf68a9ec5d6524bbc4543e926eef428d36de627aed4"}, - {file = "tokenizers-0.15.1-cp38-none-win32.whl", hash = "sha256:614f0da7dd73293214bd143e6221cafd3f7790d06b799f33a987e29d057ca658"}, - {file = "tokenizers-0.15.1-cp38-none-win_amd64.whl", hash = "sha256:a4fa0a20d9f69cc2bf1cfce41aa40588598e77ec1d6f56bf0eb99769969d1ede"}, - {file = "tokenizers-0.15.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:8d3f18a45e0cf03ce193d5900460dc2430eec4e14c786e5d79bddba7ea19034f"}, - {file = "tokenizers-0.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:38dbd6c38f88ad7d5dc5d70c764415d38fe3bcd99dc81638b572d093abc54170"}, - {file = "tokenizers-0.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:777286b1f7e52de92aa4af49fe31046cfd32885d1bbaae918fab3bba52794c33"}, - {file = "tokenizers-0.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58d4d550a3862a47dd249892d03a025e32286eb73cbd6bc887fb8fb64bc97165"}, - {file = "tokenizers-0.15.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4eda68ce0344f35042ae89220b40a0007f721776b727806b5c95497b35714bb7"}, - {file = "tokenizers-0.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cd33d15f7a3a784c3b665cfe807b8de3c6779e060349bd5005bb4ae5bdcb437"}, - {file = "tokenizers-0.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0a1aa370f978ac0bfb50374c3a40daa93fd56d47c0c70f0c79607fdac2ccbb42"}, - {file = "tokenizers-0.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:241482b940340fff26a2708cb9ba383a5bb8a2996d67a0ff2c4367bf4b86cc3a"}, - {file = "tokenizers-0.15.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:68f30b05f46a4d9aba88489eadd021904afe90e10a7950e28370d6e71b9db021"}, - {file = "tokenizers-0.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5a3c5d8025529670462b881b7b2527aacb6257398c9ec8e170070432c3ae3a82"}, - {file = "tokenizers-0.15.1-cp39-none-win32.whl", hash = "sha256:74d1827830f60a9d78da8f6d49a1fbea5422ce0eea42e2617877d23380a7efbc"}, - {file = "tokenizers-0.15.1-cp39-none-win_amd64.whl", hash = "sha256:9ff499923e4d6876d6b6a63ea84a56805eb35e91dd89b933a7aee0c56a3838c6"}, - {file = "tokenizers-0.15.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b3aa007a0f4408f62a8471bdaa3faccad644cbf2622639f2906b4f9b5339e8b8"}, - {file = "tokenizers-0.15.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f3d4176fa93d8b2070db8f3c70dc21106ae6624fcaaa334be6bdd3a0251e729e"}, - {file = "tokenizers-0.15.1-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1d0e463655ef8b2064df07bd4a445ed7f76f6da3b286b4590812587d42f80e89"}, - {file = "tokenizers-0.15.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:089138fd0351b62215c462a501bd68b8df0e213edcf99ab9efd5dba7b4cb733e"}, - {file = "tokenizers-0.15.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e563ac628f5175ed08e950430e2580e544b3e4b606a0995bb6b52b3a3165728"}, - {file = "tokenizers-0.15.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:244dcc28c5fde221cb4373961b20da30097669005b122384d7f9f22752487a46"}, - {file = "tokenizers-0.15.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d82951d46052dddae1369e68ff799a0e6e29befa9a0b46e387ae710fd4daefb0"}, - {file = "tokenizers-0.15.1-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7b14296bc9059849246ceb256ffbe97f8806a9b5d707e0095c22db312f4fc014"}, - {file = "tokenizers-0.15.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0309357bb9b6c8d86cdf456053479d7112074b470651a997a058cd7ad1c4ea57"}, - {file = "tokenizers-0.15.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:083f06e9d8d01b70b67bcbcb7751b38b6005512cce95808be6bf34803534a7e7"}, - {file = "tokenizers-0.15.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85288aea86ada579789447f0dcec108ebef8da4b450037eb4813d83e4da9371e"}, - {file = "tokenizers-0.15.1-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:385e6fcb01e8de90c1d157ae2a5338b23368d0b1c4cc25088cdca90147e35d17"}, - {file = "tokenizers-0.15.1-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:60067edfcbf7d6cd448ac47af41ec6e84377efbef7be0c06f15a7c1dd069e044"}, - {file = "tokenizers-0.15.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5f7e37f89acfe237d4eaf93c3b69b0f01f407a7a5d0b5a8f06ba91943ea3cf10"}, - {file = "tokenizers-0.15.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:6a63a15b523d42ebc1f4028e5a568013388c2aefa4053a263e511cb10aaa02f1"}, - {file = "tokenizers-0.15.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2417d9e4958a6c2fbecc34c27269e74561c55d8823bf914b422e261a11fdd5fd"}, - {file = "tokenizers-0.15.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8550974bace6210e41ab04231e06408cf99ea4279e0862c02b8d47e7c2b2828"}, - {file = "tokenizers-0.15.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:194ba82129b171bcd29235a969e5859a93e491e9b0f8b2581f500f200c85cfdd"}, - {file = "tokenizers-0.15.1-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:1bfd95eef8b01e6c0805dbccc8eaf41d8c5a84f0cce72c0ab149fe76aae0bce6"}, - {file = "tokenizers-0.15.1-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b87a15dd72f8216b03c151e3dace00c75c3fe7b0ee9643c25943f31e582f1a34"}, - {file = "tokenizers-0.15.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6ac22f358a0c2a6c685be49136ce7ea7054108986ad444f567712cf274b34cd8"}, - {file = "tokenizers-0.15.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e9d1f046a9b9d9a95faa103f07db5921d2c1c50f0329ebba4359350ee02b18b"}, - {file = "tokenizers-0.15.1-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a0fd30a4b74485f6a7af89fffb5fb84d6d5f649b3e74f8d37f624cc9e9e97cf"}, - {file = "tokenizers-0.15.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80e45dc206b9447fa48795a1247c69a1732d890b53e2cc51ba42bc2fefa22407"}, - {file = "tokenizers-0.15.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eaff56ef3e218017fa1d72007184401f04cb3a289990d2b6a0a76ce71c95f96"}, - {file = "tokenizers-0.15.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:b41dc107e4a4e9c95934e79b025228bbdda37d9b153d8b084160e88d5e48ad6f"}, - {file = "tokenizers-0.15.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1922b8582d0c33488764bcf32e80ef6054f515369e70092729c928aae2284bc2"}, - {file = "tokenizers-0.15.1.tar.gz", hash = "sha256:c0a331d6d5a3d6e97b7f99f562cee8d56797180797bc55f12070e495e717c980"}, + {file = "tokenizers-0.15.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:52f6130c9cbf70544287575a985bf44ae1bda2da7e8c24e97716080593638012"}, + {file = "tokenizers-0.15.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:054c1cc9c6d68f7ffa4e810b3d5131e0ba511b6e4be34157aa08ee54c2f8d9ee"}, + {file = "tokenizers-0.15.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9b9b070fdad06e347563b88c278995735292ded1132f8657084989a4c84a6d5"}, + {file = "tokenizers-0.15.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea621a7eef4b70e1f7a4e84dd989ae3f0eeb50fc8690254eacc08acb623e82f1"}, + {file = "tokenizers-0.15.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf7fd9a5141634fa3aa8d6b7be362e6ae1b4cda60da81388fa533e0b552c98fd"}, + {file = "tokenizers-0.15.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44f2a832cd0825295f7179eaf173381dc45230f9227ec4b44378322d900447c9"}, + {file = "tokenizers-0.15.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8b9ec69247a23747669ec4b0ca10f8e3dfb3545d550258129bd62291aabe8605"}, + {file = "tokenizers-0.15.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b6a4c78da863ff26dbd5ad9a8ecc33d8a8d97b535172601cf00aee9d7ce9ce"}, + {file = "tokenizers-0.15.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5ab2a4d21dcf76af60e05af8063138849eb1d6553a0d059f6534357bce8ba364"}, + {file = "tokenizers-0.15.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a47acfac7e511f6bbfcf2d3fb8c26979c780a91e06fb5b9a43831b2c0153d024"}, + {file = "tokenizers-0.15.2-cp310-none-win32.whl", hash = "sha256:064ff87bb6acdbd693666de9a4b692add41308a2c0ec0770d6385737117215f2"}, + {file = "tokenizers-0.15.2-cp310-none-win_amd64.whl", hash = "sha256:3b919afe4df7eb6ac7cafd2bd14fb507d3f408db7a68c43117f579c984a73843"}, + {file = "tokenizers-0.15.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:89cd1cb93e4b12ff39bb2d626ad77e35209de9309a71e4d3d4672667b4b256e7"}, + {file = "tokenizers-0.15.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cfed5c64e5be23d7ee0f0e98081a25c2a46b0b77ce99a4f0605b1ec43dd481fa"}, + {file = "tokenizers-0.15.2-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a907d76dcfda37023ba203ab4ceeb21bc5683436ebefbd895a0841fd52f6f6f2"}, + {file = "tokenizers-0.15.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20ea60479de6fc7b8ae756b4b097572372d7e4032e2521c1bbf3d90c90a99ff0"}, + {file = "tokenizers-0.15.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:48e2b9335be2bc0171df9281385c2ed06a15f5cf121c44094338306ab7b33f2c"}, + {file = "tokenizers-0.15.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:112a1dd436d2cc06e6ffdc0b06d55ac019a35a63afd26475205cb4b1bf0bfbff"}, + {file = "tokenizers-0.15.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4620cca5c2817177ee8706f860364cc3a8845bc1e291aaf661fb899e5d1c45b0"}, + {file = "tokenizers-0.15.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ccd73a82751c523b3fc31ff8194702e4af4db21dc20e55b30ecc2079c5d43cb7"}, + {file = "tokenizers-0.15.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:107089f135b4ae7817affe6264f8c7a5c5b4fd9a90f9439ed495f54fcea56fb4"}, + {file = "tokenizers-0.15.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0ff110ecc57b7aa4a594396525a3451ad70988e517237fe91c540997c4e50e29"}, + {file = "tokenizers-0.15.2-cp311-none-win32.whl", hash = "sha256:6d76f00f5c32da36c61f41c58346a4fa7f0a61be02f4301fd30ad59834977cc3"}, + {file = "tokenizers-0.15.2-cp311-none-win_amd64.whl", hash = "sha256:cc90102ed17271cf0a1262babe5939e0134b3890345d11a19c3145184b706055"}, + {file = "tokenizers-0.15.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f86593c18d2e6248e72fb91c77d413a815153b8ea4e31f7cd443bdf28e467670"}, + {file = "tokenizers-0.15.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0774bccc6608eca23eb9d620196687c8b2360624619623cf4ba9dc9bd53e8b51"}, + {file = "tokenizers-0.15.2-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d0222c5b7c9b26c0b4822a82f6a7011de0a9d3060e1da176f66274b70f846b98"}, + {file = "tokenizers-0.15.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3835738be1de66624fff2f4f6f6684775da4e9c00bde053be7564cbf3545cc66"}, + {file = "tokenizers-0.15.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0143e7d9dcd811855c1ce1ab9bf5d96d29bf5e528fd6c7824d0465741e8c10fd"}, + {file = "tokenizers-0.15.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db35825f6d54215f6b6009a7ff3eedee0848c99a6271c870d2826fbbedf31a38"}, + {file = "tokenizers-0.15.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f5e64b0389a2be47091d8cc53c87859783b837ea1a06edd9d8e04004df55a5c"}, + {file = "tokenizers-0.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e0480c452217edd35eca56fafe2029fb4d368b7c0475f8dfa3c5c9c400a7456"}, + {file = "tokenizers-0.15.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a33ab881c8fe70474980577e033d0bc9a27b7ab8272896e500708b212995d834"}, + {file = "tokenizers-0.15.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a308a607ca9de2c64c1b9ba79ec9a403969715a1b8ba5f998a676826f1a7039d"}, + {file = "tokenizers-0.15.2-cp312-none-win32.whl", hash = "sha256:b8fcfa81bcb9447df582c5bc96a031e6df4da2a774b8080d4f02c0c16b42be0b"}, + {file = "tokenizers-0.15.2-cp312-none-win_amd64.whl", hash = "sha256:38d7ab43c6825abfc0b661d95f39c7f8af2449364f01d331f3b51c94dcff7221"}, + {file = "tokenizers-0.15.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:38bfb0204ff3246ca4d5e726e8cc8403bfc931090151e6eede54d0e0cf162ef0"}, + {file = "tokenizers-0.15.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c861d35e8286a53e06e9e28d030b5a05bcbf5ac9d7229e561e53c352a85b1fc"}, + {file = "tokenizers-0.15.2-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:936bf3842db5b2048eaa53dade907b1160f318e7c90c74bfab86f1e47720bdd6"}, + {file = "tokenizers-0.15.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:620beacc3373277700d0e27718aa8b25f7b383eb8001fba94ee00aeea1459d89"}, + {file = "tokenizers-0.15.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2735ecbbf37e52db4ea970e539fd2d450d213517b77745114f92867f3fc246eb"}, + {file = "tokenizers-0.15.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:473c83c5e2359bb81b0b6fde870b41b2764fcdd36d997485e07e72cc3a62264a"}, + {file = "tokenizers-0.15.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:968fa1fb3c27398b28a4eca1cbd1e19355c4d3a6007f7398d48826bbe3a0f728"}, + {file = "tokenizers-0.15.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:865c60ae6eaebdde7da66191ee9b7db52e542ed8ee9d2c653b6d190a9351b980"}, + {file = "tokenizers-0.15.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7c0d8b52664ab2d4a8d6686eb5effc68b78608a9008f086a122a7b2996befbab"}, + {file = "tokenizers-0.15.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f33dfbdec3784093a9aebb3680d1f91336c56d86cc70ddf88708251da1fe9064"}, + {file = "tokenizers-0.15.2-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:d44ba80988ff9424e33e0a49445072ac7029d8c0e1601ad25a0ca5f41ed0c1d6"}, + {file = "tokenizers-0.15.2-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:dce74266919b892f82b1b86025a613956ea0ea62a4843d4c4237be2c5498ed3a"}, + {file = "tokenizers-0.15.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0ef06b9707baeb98b316577acb04f4852239d856b93e9ec3a299622f6084e4be"}, + {file = "tokenizers-0.15.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c73e2e74bbb07910da0d37c326869f34113137b23eadad3fc00856e6b3d9930c"}, + {file = "tokenizers-0.15.2-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4eeb12daf02a59e29f578a865f55d87cd103ce62bd8a3a5874f8fdeaa82e336b"}, + {file = "tokenizers-0.15.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ba9f6895af58487ca4f54e8a664a322f16c26bbb442effd01087eba391a719e"}, + {file = "tokenizers-0.15.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ccec77aa7150e38eec6878a493bf8c263ff1fa8a62404e16c6203c64c1f16a26"}, + {file = "tokenizers-0.15.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3f40604f5042ff210ba82743dda2b6aa3e55aa12df4e9f2378ee01a17e2855e"}, + {file = "tokenizers-0.15.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5645938a42d78c4885086767c70923abad047163d809c16da75d6b290cb30bbe"}, + {file = "tokenizers-0.15.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:05a77cbfebe28a61ab5c3891f9939cc24798b63fa236d84e5f29f3a85a200c00"}, + {file = "tokenizers-0.15.2-cp37-none-win32.whl", hash = "sha256:361abdc068e8afe9c5b818769a48624687fb6aaed49636ee39bec4e95e1a215b"}, + {file = "tokenizers-0.15.2-cp37-none-win_amd64.whl", hash = "sha256:7ef789f83eb0f9baeb4d09a86cd639c0a5518528f9992f38b28e819df397eb06"}, + {file = "tokenizers-0.15.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:4fe1f74a902bee74a3b25aff180fbfbf4f8b444ab37c4d496af7afd13a784ed2"}, + {file = "tokenizers-0.15.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4c4b89038a684f40a6b15d6b09f49650ac64d951ad0f2a3ea9169687bbf2a8ba"}, + {file = "tokenizers-0.15.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d05a1b06f986d41aed5f2de464c003004b2df8aaf66f2b7628254bcbfb72a438"}, + {file = "tokenizers-0.15.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:508711a108684111ec8af89d3a9e9e08755247eda27d0ba5e3c50e9da1600f6d"}, + {file = "tokenizers-0.15.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:daa348f02d15160cb35439098ac96e3a53bacf35885072611cd9e5be7d333daa"}, + {file = "tokenizers-0.15.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:494fdbe5932d3416de2a85fc2470b797e6f3226c12845cadf054dd906afd0442"}, + {file = "tokenizers-0.15.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2d60f5246f4da9373f75ff18d64c69cbf60c3bca597290cea01059c336d2470"}, + {file = "tokenizers-0.15.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93268e788825f52de4c7bdcb6ebc1fcd4a5442c02e730faa9b6b08f23ead0e24"}, + {file = "tokenizers-0.15.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6fc7083ab404019fc9acafe78662c192673c1e696bd598d16dc005bd663a5cf9"}, + {file = "tokenizers-0.15.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:41e39b41e5531d6b2122a77532dbea60e171ef87a3820b5a3888daa847df4153"}, + {file = "tokenizers-0.15.2-cp38-none-win32.whl", hash = "sha256:06cd0487b1cbfabefb2cc52fbd6b1f8d4c37799bd6c6e1641281adaa6b2504a7"}, + {file = "tokenizers-0.15.2-cp38-none-win_amd64.whl", hash = "sha256:5179c271aa5de9c71712e31cb5a79e436ecd0d7532a408fa42a8dbfa4bc23fd9"}, + {file = "tokenizers-0.15.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:82f8652a74cc107052328b87ea8b34291c0f55b96d8fb261b3880216a9f9e48e"}, + {file = "tokenizers-0.15.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:02458bee6f5f3139f1ebbb6d042b283af712c0981f5bc50edf771d6b762d5e4f"}, + {file = "tokenizers-0.15.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c9a09cd26cca2e1c349f91aa665309ddb48d71636370749414fbf67bc83c5343"}, + {file = "tokenizers-0.15.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:158be8ea8554e5ed69acc1ce3fbb23a06060bd4bbb09029431ad6b9a466a7121"}, + {file = "tokenizers-0.15.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ddba9a2b0c8c81633eca0bb2e1aa5b3a15362b1277f1ae64176d0f6eba78ab1"}, + {file = "tokenizers-0.15.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ef5dd1d39797044642dbe53eb2bc56435308432e9c7907728da74c69ee2adca"}, + {file = "tokenizers-0.15.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:454c203164e07a860dbeb3b1f4a733be52b0edbb4dd2e5bd75023ffa8b49403a"}, + {file = "tokenizers-0.15.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cf6b7f1d4dc59af960e6ffdc4faffe6460bbfa8dce27a58bf75755ffdb2526d"}, + {file = "tokenizers-0.15.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2ef09bbc16519f6c25d0c7fc0c6a33a6f62923e263c9d7cca4e58b8c61572afb"}, + {file = "tokenizers-0.15.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c9a2ebdd2ad4ec7a68e7615086e633857c85e2f18025bd05d2a4399e6c5f7169"}, + {file = "tokenizers-0.15.2-cp39-none-win32.whl", hash = "sha256:918fbb0eab96fe08e72a8c2b5461e9cce95585d82a58688e7f01c2bd546c79d0"}, + {file = "tokenizers-0.15.2-cp39-none-win_amd64.whl", hash = "sha256:524e60da0135e106b254bd71f0659be9f89d83f006ea9093ce4d1fab498c6d0d"}, + {file = "tokenizers-0.15.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6a9b648a58281c4672212fab04e60648fde574877d0139cd4b4f93fe28ca8944"}, + {file = "tokenizers-0.15.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:7c7d18b733be6bbca8a55084027f7be428c947ddf871c500ee603e375013ffba"}, + {file = "tokenizers-0.15.2-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:13ca3611de8d9ddfbc4dc39ef54ab1d2d4aaa114ac8727dfdc6a6ec4be017378"}, + {file = "tokenizers-0.15.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:237d1bf3361cf2e6463e6c140628e6406766e8b27274f5fcc62c747ae3c6f094"}, + {file = "tokenizers-0.15.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67a0fe1e49e60c664915e9fb6b0cb19bac082ab1f309188230e4b2920230edb3"}, + {file = "tokenizers-0.15.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4e022fe65e99230b8fd89ebdfea138c24421f91c1a4f4781a8f5016fd5cdfb4d"}, + {file = "tokenizers-0.15.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d857be2df69763362ac699f8b251a8cd3fac9d21893de129bc788f8baaef2693"}, + {file = "tokenizers-0.15.2-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:708bb3e4283177236309e698da5fcd0879ce8fd37457d7c266d16b550bcbbd18"}, + {file = "tokenizers-0.15.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:64c35e09e9899b72a76e762f9854e8750213f67567787d45f37ce06daf57ca78"}, + {file = "tokenizers-0.15.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1257f4394be0d3b00de8c9e840ca5601d0a4a8438361ce9c2b05c7d25f6057b"}, + {file = "tokenizers-0.15.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02272fe48280e0293a04245ca5d919b2c94a48b408b55e858feae9618138aeda"}, + {file = "tokenizers-0.15.2-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dc3ad9ebc76eabe8b1d7c04d38be884b8f9d60c0cdc09b0aa4e3bcf746de0388"}, + {file = "tokenizers-0.15.2-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:32e16bdeffa7c4f46bf2152172ca511808b952701d13e7c18833c0b73cb5c23f"}, + {file = "tokenizers-0.15.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fb16ba563d59003028b678d2361a27f7e4ae0ab29c7a80690efa20d829c81fdb"}, + {file = "tokenizers-0.15.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:2277c36d2d6cdb7876c274547921a42425b6810d38354327dd65a8009acf870c"}, + {file = "tokenizers-0.15.2-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1cf75d32e8d250781940d07f7eece253f2fe9ecdb1dc7ba6e3833fa17b82fcbc"}, + {file = "tokenizers-0.15.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1b3b31884dc8e9b21508bb76da80ebf7308fdb947a17affce815665d5c4d028"}, + {file = "tokenizers-0.15.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b10122d8d8e30afb43bb1fe21a3619f62c3e2574bff2699cf8af8b0b6c5dc4a3"}, + {file = "tokenizers-0.15.2-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d88b96ff0fe8e91f6ef01ba50b0d71db5017fa4e3b1d99681cec89a85faf7bf7"}, + {file = "tokenizers-0.15.2-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:37aaec5a52e959892870a7c47cef80c53797c0db9149d458460f4f31e2fb250e"}, + {file = "tokenizers-0.15.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e2ea752f2b0fe96eb6e2f3adbbf4d72aaa1272079b0dfa1145507bd6a5d537e6"}, + {file = "tokenizers-0.15.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:4b19a808d8799fda23504a5cd31d2f58e6f52f140380082b352f877017d6342b"}, + {file = "tokenizers-0.15.2-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:64c86e5e068ac8b19204419ed8ca90f9d25db20578f5881e337d203b314f4104"}, + {file = "tokenizers-0.15.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de19c4dc503c612847edf833c82e9f73cd79926a384af9d801dcf93f110cea4e"}, + {file = "tokenizers-0.15.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea09acd2fe3324174063d61ad620dec3bcf042b495515f27f638270a7d466e8b"}, + {file = "tokenizers-0.15.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cf27fd43472e07b57cf420eee1e814549203d56de00b5af8659cb99885472f1f"}, + {file = "tokenizers-0.15.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:7ca22bd897537a0080521445d91a58886c8c04084a6a19e6c78c586e0cfa92a5"}, + {file = "tokenizers-0.15.2.tar.gz", hash = "sha256:e6e9c6e019dd5484be5beafc775ae6c925f4c69a3487040ed09b45e13df2cb91"}, ] [package.dependencies] @@ -3281,13 +3281,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uvicorn" -version = "0.27.0.post1" +version = "0.27.1" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.8" files = [ - {file = "uvicorn-0.27.0.post1-py3-none-any.whl", hash = "sha256:4b85ba02b8a20429b9b205d015cbeb788a12da527f731811b643fd739ef90d5f"}, - {file = "uvicorn-0.27.0.post1.tar.gz", hash = "sha256:54898fcd80c13ff1cd28bf77b04ec9dbd8ff60c5259b499b4b12bb0917f22907"}, + {file = "uvicorn-0.27.1-py3-none-any.whl", hash = "sha256:5c89da2f3895767472a35556e539fd59f7edbe9b1e9c0e1c99eebeadc61838e4"}, + {file = "uvicorn-0.27.1.tar.gz", hash = "sha256:3d9a267296243532db80c83a959a3400502165ade2c1338dea4e67915fd4745a"}, ] [package.dependencies] diff --git a/machine-learning/pyproject.toml b/machine-learning/pyproject.toml index 7831ed36f3..f2d11a7d29 100644 --- a/machine-learning/pyproject.toml +++ b/machine-learning/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "machine-learning" -version = "1.94.1" +version = "1.97.0" description = "" authors = ["Hau Tran "] readme = "README.md" @@ -82,10 +82,10 @@ warn_untyped_fields = true [tool.ruff] line-length = 120 target-version = "py311" -select = ["E", "F", "I"] -[tool.ruff.per-file-ignores] -"test_main.py" = ["F403"] +[tool.ruff.lint] +select = ["E", "F", "I"] +per-file-ignores = { "test_main.py" = ["F403"] } [tool.black] line-length = 120 diff --git a/machine-learning/start.sh b/machine-learning/start.sh index d522f11435..082bf205c4 100755 --- a/machine-learning/start.sh +++ b/machine-learning/start.sh @@ -1,6 +1,7 @@ #!/usr/bin/env sh -export LD_PRELOAD="/usr/lib/$(arch)-linux-gnu/libmimalloc.so.2" +lib_path="/usr/lib/$(arch)-linux-gnu/libmimalloc.so.2" +export LD_PRELOAD="$lib_path" export LD_BIND_NOW=1 : "${MACHINE_LEARNING_HOST:=0.0.0.0}" @@ -10,8 +11,8 @@ export LD_BIND_NOW=1 gunicorn app.main:app \ -k app.config.CustomUvicornWorker \ - -w $MACHINE_LEARNING_WORKERS \ - -b $MACHINE_LEARNING_HOST:$MACHINE_LEARNING_PORT \ - -t $MACHINE_LEARNING_WORKER_TIMEOUT \ + -w "$MACHINE_LEARNING_WORKERS" \ + -b "$MACHINE_LEARNING_HOST":"$MACHINE_LEARNING_PORT" \ + -t "$MACHINE_LEARNING_WORKER_TIMEOUT" \ --log-config-json log_conf.json \ --graceful-timeout 0 diff --git a/misc/release/pump-version.sh b/misc/release/pump-version.sh index 3342372fc6..7d3f268f39 100755 --- a/misc/release/pump-version.sh +++ b/misc/release/pump-version.sh @@ -1,4 +1,4 @@ -#/bin/bash +#!/usr/bin/env bash # # Pump one or both of the server/mobile versions in appropriate files @@ -25,10 +25,10 @@ while getopts 's:m:' flag; do esac done -CURRENT_SERVER=$(cat server/package.json | jq -r '.version') -MAJOR=$(echo $CURRENT_SERVER | cut -d '.' -f1) -MINOR=$(echo $CURRENT_SERVER | cut -d '.' -f2) -PATCH=$(echo $CURRENT_SERVER | cut -d '.' -f3) +CURRENT_SERVER=$(jq -r '.version' server/package.json) +MAJOR=$(echo "$CURRENT_SERVER" | cut -d '.' -f1) +MINOR=$(echo "$CURRENT_SERVER" | cut -d '.' -f2) +PATCH=$(echo "$CURRENT_SERVER" | cut -d '.' -f3) if [[ $SERVER_PUMP == "major" ]]; then MAJOR=$((MAJOR + 1)) @@ -48,7 +48,7 @@ fi NEXT_SERVER=$MAJOR.$MINOR.$PATCH -CURRENT_MOBILE=$(cat mobile/pubspec.yaml | grep "^version: .*+[0-9]\+$" | cut -d "+" -f2) +CURRENT_MOBILE=$(grep "^version: .*+[0-9]\+$" mobile/pubspec.yaml | cut -d "+" -f2) NEXT_MOBILE=$CURRENT_MOBILE if [[ $MOBILE_PUMP == "true" ]]; then set $((NEXT_MOBILE++)) @@ -61,9 +61,11 @@ fi if [ "$CURRENT_SERVER" != "$NEXT_SERVER" ]; then echo "Pumping Server: $CURRENT_SERVER => $NEXT_SERVER" - npm --prefix server version $SERVER_PUMP + npm --prefix server version "$SERVER_PUMP" + npm --prefix web version "$SERVER_PUMP" + npm --prefix open-api/typescript-sdk version "$SERVER_PUMP" make open-api - poetry --directory machine-learning version $SERVER_PUMP + poetry --directory machine-learning version "$SERVER_PUMP" fi if [ "$CURRENT_MOBILE" != "$NEXT_MOBILE" ]; then @@ -75,4 +77,4 @@ sed -i "s/version_number: \"$CURRENT_SERVER\"$/version_number: \"$NEXT_SERVER\"/ sed -i "s/\"android\.injected\.version\.code\" => $CURRENT_MOBILE,/\"android\.injected\.version\.code\" => $NEXT_MOBILE,/" mobile/android/fastlane/Fastfile sed -i "s/^version: $CURRENT_SERVER+$CURRENT_MOBILE$/version: $NEXT_SERVER+$NEXT_MOBILE/" mobile/pubspec.yaml -echo "IMMICH_VERSION=v$NEXT_SERVER" >>$GITHUB_ENV +echo "IMMICH_VERSION=v$NEXT_SERVER" >>"$GITHUB_ENV" diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml index 05ce70ffe3..0b08791dc8 100644 --- a/mobile/analysis_options.yaml +++ b/mobile/analysis_options.yaml @@ -33,10 +33,10 @@ linter: # https://dart.dev/guides/language/analysis-options analyzer: exclude: - - openapi/ - - openapi/test/ + - openapi/** + - openapi/test/** - lib/generated_plugin_registrant.dart - + plugins: - custom_lint diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile index 5fd375b24f..6e051103d1 100644 --- a/mobile/android/fastlane/Fastfile +++ b/mobile/android/fastlane/Fastfile @@ -35,8 +35,8 @@ platform :android do task: 'bundle', build_type: 'Release', properties: { - "android.injected.version.code" => 121, - "android.injected.version.name" => "1.94.1", + "android.injected.version.code" => 125, + "android.injected.version.name" => "1.97.0", } ) upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab') diff --git a/mobile/android/fastlane/report.xml b/mobile/android/fastlane/report.xml index 91392a1d16..575edaa4e4 100644 --- a/mobile/android/fastlane/report.xml +++ b/mobile/android/fastlane/report.xml @@ -5,17 +5,17 @@ - + - + - + diff --git a/mobile/ios/Runner.xcodeproj/project.pbxproj b/mobile/ios/Runner.xcodeproj/project.pbxproj index a798d3685a..457160ad35 100644 --- a/mobile/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/ios/Runner.xcodeproj/project.pbxproj @@ -379,7 +379,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 137; + CURRENT_PROJECT_VERSION = 141; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; @@ -515,7 +515,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 137; + CURRENT_PROJECT_VERSION = 141; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; @@ -543,7 +543,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 137; + CURRENT_PROJECT_VERSION = 141; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; diff --git a/mobile/ios/Runner/Info.plist b/mobile/ios/Runner/Info.plist index 40164d254a..6d745e984c 100644 --- a/mobile/ios/Runner/Info.plist +++ b/mobile/ios/Runner/Info.plist @@ -55,11 +55,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.94.1 + 1.97.0 CFBundleSignature ???? CFBundleVersion - 137 + 141 FLTEnableImpeller ITSAppUsesNonExemptEncryption diff --git a/mobile/ios/ci_scripts/ci_post_clone.sh b/mobile/ios/ci_scripts/ci_post_clone.sh index ea244484bb..fe282ae3fe 100755 --- a/mobile/ios/ci_scripts/ci_post_clone.sh +++ b/mobile/ios/ci_scripts/ci_post_clone.sh @@ -1,10 +1,10 @@ #!/usr/bin/env sh # The default execution directory of this script is the ci_scripts directory. -cd $CI_WORKSPACE/mobile +cd "$CI_WORKSPACE"/mobile || exit # Install Flutter using git. -git clone https://github.com/flutter/flutter.git --depth 1 -b stable $HOME/flutter +git clone https://github.com/flutter/flutter.git --depth 1 -b stable "$HOME"/flutter export PATH="$PATH:$HOME/flutter/bin" # Install Flutter artifacts for iOS (--ios), or macOS (--macos) platforms. @@ -14,7 +14,7 @@ flutter precache --ios flutter pub get # Install CocoaPods using Homebrew. -HOMEBREW_NO_AUTO_UPDATE=1 # disable homebrew's automatic updates. +export HOMEBREW_NO_AUTO_UPDATE=1 # disable homebrew's automatic updates. brew install cocoapods # Install CocoaPods dependencies. diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index 1f34c07c71..caf1dd8e68 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -19,7 +19,7 @@ platform :ios do desc "iOS Beta" lane :beta do increment_version_number( - version_number: "1.94.1" + version_number: "1.97.0" ) increment_build_number( build_number: latest_testflight_build_number + 1, diff --git a/mobile/ios/fastlane/report.xml b/mobile/ios/fastlane/report.xml index c3b5ebbffe..129dddb341 100644 --- a/mobile/ios/fastlane/report.xml +++ b/mobile/ios/fastlane/report.xml @@ -5,32 +5,32 @@ - + - + - + - + - + - + diff --git a/mobile/lib/extensions/asyncvalue_extensions.dart b/mobile/lib/extensions/asyncvalue_extensions.dart index c616835a81..af02ff13c2 100644 --- a/mobile/lib/extensions/asyncvalue_extensions.dart +++ b/mobile/lib/extensions/asyncvalue_extensions.dart @@ -30,7 +30,7 @@ extension LogOnError on AsyncValue { } if (hasError && !hasValue) { - _asyncErrorLogger.severe("$error", error, stackTrace); + _asyncErrorLogger.severe('Could not load value', error, stackTrace); return onError?.call(error, stackTrace) ?? ScaffoldErrorBody(errorMsg: error?.toString()); } diff --git a/mobile/lib/extensions/response_extensions.dart b/mobile/lib/extensions/response_extensions.dart new file mode 100644 index 0000000000..7fec41d07c --- /dev/null +++ b/mobile/lib/extensions/response_extensions.dart @@ -0,0 +1,5 @@ +import 'package:http/http.dart'; + +extension LoggerExtension on Response { + String toLoggerString() => "Status: $statusCode $reasonPhrase\n\n$body"; +} diff --git a/mobile/lib/mixins/error_logger.mixin.dart b/mobile/lib/mixins/error_logger.mixin.dart index 38837a716f..9b2bc6f98e 100644 --- a/mobile/lib/mixins/error_logger.mixin.dart +++ b/mobile/lib/mixins/error_logger.mixin.dart @@ -10,13 +10,14 @@ mixin ErrorLoggerMixin { /// Else, logs the error to the overrided logger and returns an AsyncError<> AsyncFuture guardError( Future Function() fn, { + required String errorMessage, Level logLevel = Level.SEVERE, }) async { try { final result = await fn(); return AsyncData(result); } catch (error, stackTrace) { - logger.log(logLevel, "$error", error, stackTrace); + logger.log(logLevel, errorMessage, error, stackTrace); return AsyncError(error, stackTrace); } } @@ -26,12 +27,13 @@ mixin ErrorLoggerMixin { Future logError( Future Function() fn, { required T defaultValue, + required String errorMessage, Level logLevel = Level.SEVERE, }) async { try { return await fn(); } catch (error, stackTrace) { - logger.log(logLevel, "$error", error, stackTrace); + logger.log(logLevel, errorMessage, error, stackTrace); } return defaultValue; } diff --git a/mobile/lib/modules/activities/services/activity.service.dart b/mobile/lib/modules/activities/services/activity.service.dart index db35c17aee..cde98f73ae 100644 --- a/mobile/lib/modules/activities/services/activity.service.dart +++ b/mobile/lib/modules/activities/services/activity.service.dart @@ -24,6 +24,7 @@ class ActivityService with ErrorLoggerMixin { return list != null ? list.map(Activity.fromDto).toList() : []; }, defaultValue: [], + errorMessage: "Failed to get all activities for album $albumId", ); } @@ -35,6 +36,7 @@ class ActivityService with ErrorLoggerMixin { return dto?.comments ?? 0; }, defaultValue: 0, + errorMessage: "Failed to statistics for album $albumId", ); } @@ -45,6 +47,7 @@ class ActivityService with ErrorLoggerMixin { return true; }, defaultValue: false, + errorMessage: "Failed to delete activity", ); } @@ -54,21 +57,24 @@ class ActivityService with ErrorLoggerMixin { String? assetId, String? comment, }) async { - return guardError(() async { - final dto = await _apiService.activityApi.createActivity( - ActivityCreateDto( - albumId: albumId, - type: type == ActivityType.comment - ? ReactionType.comment - : ReactionType.like, - assetId: assetId, - comment: comment, - ), - ); - if (dto != null) { - return Activity.fromDto(dto); - } - throw NoResponseDtoError(); - }); + return guardError( + () async { + final dto = await _apiService.activityApi.createActivity( + ActivityCreateDto( + albumId: albumId, + type: type == ActivityType.comment + ? ReactionType.comment + : ReactionType.like, + assetId: assetId, + comment: comment, + ), + ); + if (dto != null) { + return Activity.fromDto(dto); + } + throw NoResponseDtoError(); + }, + errorMessage: "Failed to create $type for album $albumId", + ); } } diff --git a/mobile/lib/modules/album/ui/album_thumbnail_card.dart b/mobile/lib/modules/album/ui/album_thumbnail_card.dart index da36a4d544..266acf77bc 100644 --- a/mobile/lib/modules/album/ui/album_thumbnail_card.dart +++ b/mobile/lib/modules/album/ui/album_thumbnail_card.dart @@ -4,7 +4,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/object_extensions.dart'; import 'package:immich_mobile/modules/album/models/album.model.dart'; import 'package:immich_mobile/shared/models/store.dart'; -import 'package:immich_mobile/shared/ui/immich_image.dart'; +import 'package:immich_mobile/shared/ui/immich_thumbnail.dart'; class AlbumThumbnailCard extends StatelessWidget { final Album album; @@ -77,8 +77,8 @@ class AlbumThumbnailCard extends StatelessWidget { ), ) // Thumbnail image - : ImmichImage( - album.thumbnail, + : ImmichThumbnail( + asset: album.thumbnail, width: cardSize, height: cardSize, ), diff --git a/mobile/lib/modules/album/ui/shared_album_thumbnail_image.dart b/mobile/lib/modules/album/ui/shared_album_thumbnail_image.dart index f70c706f35..5a27def4c9 100644 --- a/mobile/lib/modules/album/ui/shared_album_thumbnail_image.dart +++ b/mobile/lib/modules/album/ui/shared_album_thumbnail_image.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/shared/models/asset.dart'; -import 'package:immich_mobile/shared/ui/immich_image.dart'; +import 'package:immich_mobile/shared/ui/immich_thumbnail.dart'; class SharedAlbumThumbnailImage extends HookConsumerWidget { final Asset asset; @@ -16,8 +16,8 @@ class SharedAlbumThumbnailImage extends HookConsumerWidget { }, child: Stack( children: [ - ImmichImage.thumbnail( - asset, + ImmichThumbnail( + asset: asset, width: 500, height: 500, ), diff --git a/mobile/lib/modules/album/views/sharing_page.dart b/mobile/lib/modules/album/views/sharing_page.dart index aa5e3d4599..a23fbe5f9a 100644 --- a/mobile/lib/modules/album/views/sharing_page.dart +++ b/mobile/lib/modules/album/views/sharing_page.dart @@ -13,7 +13,7 @@ import 'package:immich_mobile/modules/partner/ui/partner_list.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/shared/providers/user.provider.dart'; import 'package:immich_mobile/shared/ui/immich_app_bar.dart'; -import 'package:immich_mobile/shared/ui/immich_image.dart'; +import 'package:immich_mobile/shared/ui/immich_thumbnail.dart'; @RoutePage() class SharingPage extends HookConsumerWidget { @@ -75,8 +75,8 @@ class SharingPage extends HookConsumerWidget { contentPadding: const EdgeInsets.symmetric(horizontal: 12), leading: ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(8)), - child: ImmichImage.thumbnail( - album.thumbnail, + child: ImmichThumbnail( + asset: album.thumbnail, width: 60, height: 60, ), diff --git a/mobile/lib/modules/asset_viewer/hooks/chewiew_controller_hook.dart b/mobile/lib/modules/asset_viewer/hooks/chewiew_controller_hook.dart new file mode 100644 index 0000000000..224eb838e7 --- /dev/null +++ b/mobile/lib/modules/asset_viewer/hooks/chewiew_controller_hook.dart @@ -0,0 +1,179 @@ +import 'dart:async'; + +import 'package:chewie/chewie.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:immich_mobile/shared/models/asset.dart'; +import 'package:immich_mobile/shared/models/store.dart'; +import 'package:video_player/video_player.dart'; +import 'package:immich_mobile/shared/models/store.dart' as store; +import 'package:wakelock_plus/wakelock_plus.dart'; + +/// Provides the initialized video player controller +/// If the asset is local, use the local file +/// Otherwise, use a video player with a URL +ChewieController? useChewieController( + Asset asset, { + EdgeInsets controlsSafeAreaMinimum = const EdgeInsets.only( + bottom: 100, + ), + bool showOptions = true, + bool showControlsOnInitialize = false, + bool autoPlay = true, + bool autoInitialize = true, + bool allowFullScreen = false, + bool allowedScreenSleep = false, + bool showControls = true, + Widget? customControls, + Widget? placeholder, + Duration hideControlsTimer = const Duration(seconds: 1), + VoidCallback? onPlaying, + VoidCallback? onPaused, + VoidCallback? onVideoEnded, +}) { + return use( + _ChewieControllerHook( + asset: asset, + placeholder: placeholder, + showOptions: showOptions, + controlsSafeAreaMinimum: controlsSafeAreaMinimum, + autoPlay: autoPlay, + allowFullScreen: allowFullScreen, + customControls: customControls, + hideControlsTimer: hideControlsTimer, + showControlsOnInitialize: showControlsOnInitialize, + showControls: showControls, + autoInitialize: autoInitialize, + allowedScreenSleep: allowedScreenSleep, + onPlaying: onPlaying, + onPaused: onPaused, + onVideoEnded: onVideoEnded, + ), + ); +} + +class _ChewieControllerHook extends Hook { + final Asset asset; + final EdgeInsets controlsSafeAreaMinimum; + final bool showOptions; + final bool showControlsOnInitialize; + final bool autoPlay; + final bool autoInitialize; + final bool allowFullScreen; + final bool allowedScreenSleep; + final bool showControls; + final Widget? customControls; + final Widget? placeholder; + final Duration hideControlsTimer; + final VoidCallback? onPlaying; + final VoidCallback? onPaused; + final VoidCallback? onVideoEnded; + + const _ChewieControllerHook({ + required this.asset, + this.controlsSafeAreaMinimum = const EdgeInsets.only( + bottom: 100, + ), + this.showOptions = true, + this.showControlsOnInitialize = false, + this.autoPlay = true, + this.autoInitialize = true, + this.allowFullScreen = false, + this.allowedScreenSleep = false, + this.showControls = true, + this.customControls, + this.placeholder, + this.hideControlsTimer = const Duration(seconds: 3), + this.onPlaying, + this.onPaused, + this.onVideoEnded, + }); + + @override + createState() => _ChewieControllerHookState(); +} + +class _ChewieControllerHookState + extends HookState { + ChewieController? chewieController; + VideoPlayerController? videoPlayerController; + + @override + void initHook() async { + super.initHook(); + unawaited(_initialize()); + } + + @override + void dispose() { + chewieController?.dispose(); + videoPlayerController?.dispose(); + super.dispose(); + } + + @override + ChewieController? build(BuildContext context) { + return chewieController; + } + + /// Initializes the chewie controller and video player controller + Future _initialize() async { + if (hook.asset.isLocal && hook.asset.livePhotoVideoId == null) { + // Use a local file for the video player controller + final file = await hook.asset.local!.file; + if (file == null) { + throw Exception('No file found for the video'); + } + videoPlayerController = VideoPlayerController.file(file); + } else { + // Use a network URL for the video player controller + final serverEndpoint = store.Store.get(store.StoreKey.serverEndpoint); + final String videoUrl = hook.asset.livePhotoVideoId != null + ? '$serverEndpoint/asset/file/${hook.asset.livePhotoVideoId}' + : '$serverEndpoint/asset/file/${hook.asset.remoteId}'; + + final url = Uri.parse(videoUrl); + final accessToken = store.Store.get(StoreKey.accessToken); + + videoPlayerController = VideoPlayerController.networkUrl( + url, + httpHeaders: {"x-immich-user-token": accessToken}, + ); + } + + videoPlayerController!.addListener(() { + final value = videoPlayerController!.value; + if (value.isPlaying) { + WakelockPlus.enable(); + hook.onPlaying?.call(); + } else if (!value.isPlaying) { + WakelockPlus.disable(); + hook.onPaused?.call(); + } + + if (value.position == value.duration) { + WakelockPlus.disable(); + hook.onVideoEnded?.call(); + } + }); + + await videoPlayerController!.initialize(); + + setState(() { + chewieController = ChewieController( + videoPlayerController: videoPlayerController!, + controlsSafeAreaMinimum: hook.controlsSafeAreaMinimum, + showOptions: hook.showOptions, + showControlsOnInitialize: hook.showControlsOnInitialize, + autoPlay: hook.autoPlay, + autoInitialize: hook.autoInitialize, + allowFullScreen: hook.allowFullScreen, + allowedScreenSleep: hook.allowedScreenSleep, + showControls: hook.showControls, + customControls: hook.customControls, + placeholder: hook.placeholder, + hideControlsTimer: hook.hideControlsTimer, + ); + }); + } +} diff --git a/mobile/lib/modules/asset_viewer/image_providers/immich_local_image_provider.dart b/mobile/lib/modules/asset_viewer/image_providers/immich_local_image_provider.dart index 4c1e9fc5c8..3094c69076 100644 --- a/mobile/lib/modules/asset_viewer/image_providers/immich_local_image_provider.dart +++ b/mobile/lib/modules/asset_viewer/image_providers/immich_local_image_provider.dart @@ -11,7 +11,7 @@ import 'package:photo_manager/photo_manager.dart'; /// The local image provider for an asset /// Only viable -class ImmichLocalImageProvider extends ImageProvider { +class ImmichLocalImageProvider extends ImageProvider { final Asset asset; ImmichLocalImageProvider({ @@ -21,15 +21,18 @@ class ImmichLocalImageProvider extends ImageProvider { /// Converts an [ImageProvider]'s settings plus an [ImageConfiguration] to a key /// that describes the precise image to load. @override - Future obtainKey(ImageConfiguration configuration) { - return SynchronousFuture(asset); + Future obtainKey(ImageConfiguration configuration) { + return SynchronousFuture(this); } @override - ImageStreamCompleter loadImage(Asset key, ImageDecoderCallback decode) { + ImageStreamCompleter loadImage( + ImmichLocalImageProvider key, + ImageDecoderCallback decode, + ) { final chunkEvents = StreamController(); return MultiImageStreamCompleter( - codec: _codec(key, decode, chunkEvents), + codec: _codec(key.asset, decode, chunkEvents), scale: 1.0, chunkEvents: chunkEvents.stream, informationCollector: () sync* { @@ -82,11 +85,6 @@ class ImmichLocalImageProvider extends ImageProvider { yield codec; } catch (error) { throw StateError("Loading asset ${asset.fileName} failed"); - } finally { - if (Platform.isIOS) { - // Clean up this file - await file.delete(); - } } } } diff --git a/mobile/lib/modules/asset_viewer/image_providers/immich_local_thumbnail_provider.dart b/mobile/lib/modules/asset_viewer/image_providers/immich_local_thumbnail_provider.dart new file mode 100644 index 0000000000..bb86cfafda --- /dev/null +++ b/mobile/lib/modules/asset_viewer/image_providers/immich_local_thumbnail_provider.dart @@ -0,0 +1,86 @@ +import 'dart:async'; +import 'dart:ui' as ui; + +import 'package:cached_network_image/cached_network_image.dart'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/painting.dart'; +import 'package:immich_mobile/shared/models/asset.dart'; +import 'package:photo_manager/photo_manager.dart'; + +/// The local image provider for an asset +/// Only viable +class ImmichLocalThumbnailProvider extends ImageProvider { + final Asset asset; + final int height; + final int width; + + ImmichLocalThumbnailProvider({ + required this.asset, + this.height = 256, + this.width = 256, + }) : assert(asset.local != null, 'Only usable when asset.local is set'); + + /// Converts an [ImageProvider]'s settings plus an [ImageConfiguration] to a key + /// that describes the precise image to load. + @override + Future obtainKey(ImageConfiguration configuration) { + return SynchronousFuture(asset); + } + + @override + ImageStreamCompleter loadImage(Asset key, ImageDecoderCallback decode) { + final chunkEvents = StreamController(); + return MultiImageStreamCompleter( + codec: _codec(key, decode, chunkEvents), + scale: 1.0, + chunkEvents: chunkEvents.stream, + informationCollector: () sync* { + yield ErrorDescription(asset.fileName); + }, + ); + } + + // Streams in each stage of the image as we ask for it + Stream _codec( + Asset key, + ImageDecoderCallback decode, + StreamController chunkEvents, + ) async* { + // Load a small thumbnail + final thumbBytes = await asset.local?.thumbnailDataWithSize( + const ThumbnailSize.square(32), + quality: 75, + ); + if (thumbBytes != null) { + final buffer = await ui.ImmutableBuffer.fromUint8List(thumbBytes); + final codec = await decode(buffer); + yield codec; + } else { + debugPrint("Loading thumb for ${asset.fileName} failed"); + } + + final normalThumbBytes = + await asset.local?.thumbnailDataWithSize(ThumbnailSize(width, height)); + if (normalThumbBytes == null) { + throw StateError( + "Loading thumb for local photo ${asset.fileName} failed", + ); + } + final buffer = await ui.ImmutableBuffer.fromUint8List(normalThumbBytes); + final codec = await decode(buffer); + yield codec; + + chunkEvents.close(); + } + + @override + bool operator ==(Object other) { + if (other is! ImmichLocalThumbnailProvider) return false; + if (identical(this, other)) return true; + return asset == other.asset; + } + + @override + int get hashCode => asset.hashCode; +} diff --git a/mobile/lib/modules/asset_viewer/image_providers/immich_remote_image_provider.dart b/mobile/lib/modules/asset_viewer/image_providers/immich_remote_image_provider.dart index 9f9af7aded..d9fbd80485 100644 --- a/mobile/lib/modules/asset_viewer/image_providers/immich_remote_image_provider.dart +++ b/mobile/lib/modules/asset_viewer/image_providers/immich_remote_image_provider.dart @@ -13,10 +13,13 @@ import 'package:immich_mobile/shared/models/store.dart'; import 'package:immich_mobile/utils/image_url_builder.dart'; /// Our Image Provider HTTP client to make the request -final _httpClient = HttpClient()..autoUncompress = false; +final _httpClient = HttpClient() + ..autoUncompress = false + ..maxConnectionsPerHost = 10; /// The remote image provider -class ImmichRemoteImageProvider extends ImageProvider { +class ImmichRemoteImageProvider + extends ImageProvider { /// The [Asset.remoteId] of the asset to fetch final String assetId; @@ -32,16 +35,20 @@ class ImmichRemoteImageProvider extends ImageProvider { /// Converts an [ImageProvider]'s settings plus an [ImageConfiguration] to a key /// that describes the precise image to load. @override - Future obtainKey(ImageConfiguration configuration) { - return SynchronousFuture('$assetId,$isThumbnail'); + Future obtainKey( + ImageConfiguration configuration, + ) { + return SynchronousFuture(this); } @override - ImageStreamCompleter loadImage(String key, ImageDecoderCallback decode) { - final id = key.split(',').first; + ImageStreamCompleter loadImage( + ImmichRemoteImageProvider key, + ImageDecoderCallback decode, + ) { final chunkEvents = StreamController(); return MultiImageStreamCompleter( - codec: _codec(id, decode, chunkEvents), + codec: _codec(key, decode, chunkEvents), scale: 1.0, chunkEvents: chunkEvents.stream, ); @@ -61,14 +68,14 @@ class ImmichRemoteImageProvider extends ImageProvider { // Streams in each stage of the image as we ask for it Stream _codec( - String key, + ImmichRemoteImageProvider key, ImageDecoderCallback decode, StreamController chunkEvents, ) async* { // Load a preview to the chunk events - if (_loadPreview || isThumbnail) { + if (_loadPreview || key.isThumbnail) { final preview = getThumbnailUrlForRemoteId( - assetId, + key.assetId, type: api.ThumbnailFormat.WEBP, ); @@ -80,14 +87,14 @@ class ImmichRemoteImageProvider extends ImageProvider { } // Guard thumnbail rendering - if (isThumbnail) { + if (key.isThumbnail) { await chunkEvents.close(); return; } // Load the higher resolution version of the image final url = getThumbnailUrlForRemoteId( - assetId, + key.assetId, type: api.ThumbnailFormat.JPEG, ); final codec = await _loadFromUri(Uri.parse(url), decode, chunkEvents); @@ -96,7 +103,7 @@ class ImmichRemoteImageProvider extends ImageProvider { // Load the final remote image if (_useOriginal) { // Load the original image - final url = getImageUrlFromId(assetId); + final url = getImageUrlFromId(key.assetId); final codec = await _loadFromUri(Uri.parse(url), decode, chunkEvents); yield codec; } @@ -137,7 +144,7 @@ class ImmichRemoteImageProvider extends ImageProvider { bool operator ==(Object other) { if (other is! ImmichRemoteImageProvider) return false; if (identical(this, other)) return true; - return assetId == other.assetId; + return assetId == other.assetId && isThumbnail == other.isThumbnail; } @override diff --git a/mobile/lib/modules/asset_viewer/image_providers/immich_remote_thumbnail_provider.dart b/mobile/lib/modules/asset_viewer/image_providers/immich_remote_thumbnail_provider.dart index 8332d8d3d7..92b85b3472 100644 --- a/mobile/lib/modules/asset_viewer/image_providers/immich_remote_thumbnail_provider.dart +++ b/mobile/lib/modules/asset_viewer/image_providers/immich_remote_thumbnail_provider.dart @@ -12,14 +12,17 @@ import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/models/store.dart'; import 'package:immich_mobile/utils/image_url_builder.dart'; +/// Our HTTP client to make the request +final _httpClient = HttpClient() + ..autoUncompress = false + ..maxConnectionsPerHost = 100; + /// The remote image provider -class ImmichRemoteThumbnailProvider extends ImageProvider { +class ImmichRemoteThumbnailProvider + extends ImageProvider { /// The [Asset.remoteId] of the asset to fetch final String assetId; - /// Our HTTP client to make the request - final _httpClient = HttpClient()..autoUncompress = false; - ImmichRemoteThumbnailProvider({ required this.assetId, }); @@ -27,12 +30,17 @@ class ImmichRemoteThumbnailProvider extends ImageProvider { /// Converts an [ImageProvider]'s settings plus an [ImageConfiguration] to a key /// that describes the precise image to load. @override - Future obtainKey(ImageConfiguration configuration) { - return SynchronousFuture(assetId); + Future obtainKey( + ImageConfiguration configuration, + ) { + return SynchronousFuture(this); } @override - ImageStreamCompleter loadImage(String key, ImageDecoderCallback decode) { + ImageStreamCompleter loadImage( + ImmichRemoteThumbnailProvider key, + ImageDecoderCallback decode, + ) { final chunkEvents = StreamController(); return MultiImageStreamCompleter( codec: _codec(key, decode, chunkEvents), @@ -43,13 +51,13 @@ class ImmichRemoteThumbnailProvider extends ImageProvider { // Streams in each stage of the image as we ask for it Stream _codec( - String key, + ImmichRemoteThumbnailProvider key, ImageDecoderCallback decode, StreamController chunkEvents, ) async* { // Load a preview to the chunk events final preview = getThumbnailUrlForRemoteId( - assetId, + key.assetId, type: api.ThumbnailFormat.WEBP, ); diff --git a/mobile/lib/modules/asset_viewer/services/image_viewer.service.dart b/mobile/lib/modules/asset_viewer/services/image_viewer.service.dart index db527c6e23..54682fdeeb 100644 --- a/mobile/lib/modules/asset_viewer/services/image_viewer.service.dart +++ b/mobile/lib/modules/asset_viewer/services/image_viewer.service.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/response_extensions.dart'; import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/providers/api.provider.dart'; import 'package:immich_mobile/shared/services/api.service.dart'; @@ -39,7 +40,8 @@ class ImageViewerService { final failedResponse = imageResponse.statusCode != 200 ? imageResponse : motionReponse; _log.severe( - "Motion asset download failed with status - ${failedResponse.statusCode} and response - ${failedResponse.body}", + "Motion asset download failed", + failedResponse.toLoggerString(), ); return false; } @@ -75,9 +77,7 @@ class ImageViewerService { .downloadFileWithHttpInfo(asset.remoteId!); if (res.statusCode != 200) { - _log.severe( - "Asset download failed with status - ${res.statusCode} and response - ${res.body}", - ); + _log.severe("Asset download failed", res.toLoggerString()); return false; } @@ -98,7 +98,7 @@ class ImageViewerService { return entity != null; } } catch (error, stack) { - _log.severe("Error saving file ${error.toString()}", error, stack); + _log.severe("Error saving downloaded asset", error, stack); return false; } finally { // Clear temp files diff --git a/mobile/lib/modules/asset_viewer/ui/advanced_bottom_sheet.dart b/mobile/lib/modules/asset_viewer/ui/advanced_bottom_sheet.dart index fe241fa652..9425aa303b 100644 --- a/mobile/lib/modules/asset_viewer/ui/advanced_bottom_sheet.dart +++ b/mobile/lib/modules/asset_viewer/ui/advanced_bottom_sheet.dart @@ -12,92 +12,85 @@ class AdvancedBottomSheet extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return SingleChildScrollView( - child: Card( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(15), - topRight: Radius.circular(15), - ), - ), - margin: const EdgeInsets.all(0), - child: Container( - margin: const EdgeInsets.symmetric(horizontal: 8.0), - child: LayoutBuilder( - builder: (context, constraints) { - // One column - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const SizedBox(height: 32.0), - const Align( - child: Text( - "ADVANCED INFO", - style: TextStyle(fontSize: 12.0), - ), + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 8.0), + child: LayoutBuilder( + builder: (context, constraints) { + // One column + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Align( + child: Text( + "ADVANCED INFO", + style: TextStyle(fontSize: 12.0), ), - const SizedBox(height: 32.0), - Container( - decoration: BoxDecoration( - color: context.isDarkTheme - ? Colors.grey[900] - : Colors.grey[200], - borderRadius: BorderRadius.circular(15.0), + ), + const SizedBox(height: 32.0), + Container( + decoration: BoxDecoration( + color: context.isDarkTheme + ? Colors.grey[900] + : Colors.grey[200], + borderRadius: BorderRadius.circular(15.0), + ), + child: Padding( + padding: const EdgeInsets.only( + right: 16.0, + left: 16, + top: 8, + bottom: 16, ), - child: Padding( - padding: const EdgeInsets.only( - right: 16.0, - left: 16, - top: 8, - bottom: 16, - ), - child: ListView( - shrinkWrap: true, - children: [ - Align( - alignment: Alignment.centerRight, - child: IconButton( - onPressed: () { - Clipboard.setData( - ClipboardData(text: assetDetail.toString()), - ).then((_) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - "Copied to clipboard", - style: context.textTheme.bodyLarge - ?.copyWith( - color: context.primaryColor, - ), + child: ListView( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + children: [ + Align( + alignment: Alignment.centerRight, + child: IconButton( + onPressed: () { + Clipboard.setData( + ClipboardData( + text: assetDetail.toString(), + ), + ).then((_) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + "Copied to clipboard", + style: + context.textTheme.bodyLarge?.copyWith( + color: context.primaryColor, ), ), - ); - }); - }, - icon: Icon( - Icons.copy, - size: 16.0, - color: context.primaryColor, - ), + ), + ); + }); + }, + icon: Icon( + Icons.copy, + size: 16.0, + color: context.primaryColor, ), ), - SelectableText( - assetDetail.toString(), - style: const TextStyle( - fontSize: 12.0, - fontWeight: FontWeight.bold, - fontFamily: "Inconsolata", - ), - showCursor: true, + ), + SelectableText( + assetDetail.toString(), + style: const TextStyle( + fontSize: 12.0, + fontWeight: FontWeight.bold, + fontFamily: "Inconsolata", ), - ], - ), + showCursor: true, + ), + ], ), ), - const SizedBox(height: 32.0), - ], - ); - }, - ), + ), + const SizedBox(height: 32.0), + ], + ); + }, ), ), ); diff --git a/mobile/lib/modules/asset_viewer/ui/description_input.dart b/mobile/lib/modules/asset_viewer/ui/description_input.dart index c5972a822d..c5bae07cde 100644 --- a/mobile/lib/modules/asset_viewer/ui/description_input.dart +++ b/mobile/lib/modules/asset_viewer/ui/description_input.dart @@ -48,7 +48,7 @@ class DescriptionInput extends HookConsumerWidget { ); } catch (error, stack) { hasError.value = true; - _log.severe("Error updating description $error", error, stack); + _log.severe("Error updating description", error, stack); ImmichToast.show( context: context, msg: "description_input_submit_error".tr(), diff --git a/mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart b/mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart index 45d73dc71c..3c6d5f2b6c 100644 --- a/mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart +++ b/mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart @@ -10,7 +10,6 @@ import 'package:immich_mobile/modules/asset_viewer/ui/description_input.dart'; import 'package:immich_mobile/modules/map/widgets/map_thumbnail.dart'; import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/providers/asset.provider.dart'; -import 'package:immich_mobile/shared/ui/drag_sheet.dart'; import 'package:immich_mobile/utils/selection_handlers.dart'; import 'package:immich_mobile/utils/bytes_units.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; @@ -126,20 +125,6 @@ class ExifBottomSheet extends HookConsumerWidget { return text.isNotEmpty ? text : null; } - buildDragHeader() { - return const Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 12), - Align( - alignment: Alignment.center, - child: CustomDraggingHandle(), - ), - SizedBox(height: 12), - ], - ); - } - buildLocation() { // Guard no lat/lng if (!hasCoordinates()) { @@ -341,86 +326,69 @@ class ExifBottomSheet extends HookConsumerWidget { ); } - return GestureDetector( - onTap: () { - // FocusScope.of(context).unfocus(); - }, - child: SingleChildScrollView( - child: Card( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(15), - topRight: Radius.circular(15), - ), - ), - margin: const EdgeInsets.all(0), - child: Container( - margin: const EdgeInsets.symmetric(horizontal: 16.0), - child: LayoutBuilder( - builder: (context, constraints) { - if (constraints.maxWidth > 600) { - // Two column - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 12.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - buildDragHeader(), - buildDate(), - if (asset.isRemote) DescriptionInput(asset: asset), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Flexible( - flex: hasCoordinates() ? 5 : 0, - child: Padding( - padding: const EdgeInsets.only(right: 8.0), - child: buildLocation(), - ), - ), - Flexible( - flex: 5, - child: Padding( - padding: const EdgeInsets.only(left: 8.0), - child: buildDetail(), - ), - ), - ], + return SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 16.0), + child: LayoutBuilder( + builder: (context, constraints) { + if (constraints.maxWidth > 600) { + // Two column + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + buildDate(), + if (asset.isRemote) DescriptionInput(asset: asset), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.only(right: 8.0), + child: buildLocation(), ), - const SizedBox(height: 50), - ], - ), - ); - } + ), + ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 300), + child: Padding( + padding: const EdgeInsets.only(left: 8.0), + child: buildDetail(), + ), + ), + ], + ), + const SizedBox(height: 50), + ], + ); + } - // One column - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - buildDragHeader(), - buildDate(), - assetWithExif.when( - data: (data) => DescriptionInput(asset: data), - error: (error, stackTrace) => Icon( - Icons.image_not_supported_outlined, - color: context.primaryColor, - ), - loading: () => const SizedBox( - width: 75, - height: 75, - child: CircularProgressIndicator.adaptive(), - ), - ), - buildLocation(), - SizedBox(height: hasCoordinates() ? 16.0 : 6.0), - buildDetail(), - const SizedBox(height: 50), - ], - ); - }, - ), - ), + // One column + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + buildDate(), + assetWithExif.when( + data: (data) => DescriptionInput(asset: data), + error: (error, stackTrace) => Icon( + Icons.image_not_supported_outlined, + color: context.primaryColor, + ), + loading: () => const SizedBox( + width: 75, + height: 75, + child: CircularProgressIndicator.adaptive(), + ), + ), + buildLocation(), + SizedBox(height: hasCoordinates() ? 16.0 : 6.0), + buildDetail(), + const SizedBox(height: 50), + ], + ); + }, ), ), ); diff --git a/mobile/lib/modules/asset_viewer/ui/video_player_controls.dart b/mobile/lib/modules/asset_viewer/ui/video_player_controls.dart index 781e84e458..bfc45b8a35 100644 --- a/mobile/lib/modules/asset_viewer/ui/video_player_controls.dart +++ b/mobile/lib/modules/asset_viewer/ui/video_player_controls.dart @@ -7,7 +7,7 @@ import 'package:immich_mobile/modules/asset_viewer/providers/show_controls.provi import 'package:immich_mobile/modules/asset_viewer/providers/video_player_controls_provider.dart'; import 'package:immich_mobile/modules/asset_viewer/providers/video_player_value_provider.dart'; import 'package:immich_mobile/modules/asset_viewer/ui/center_play_button.dart'; -import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; +import 'package:immich_mobile/shared/ui/delayed_loading_indicator.dart'; import 'package:video_player/video_player.dart'; class VideoPlayerControls extends ConsumerStatefulWidget { @@ -66,7 +66,9 @@ class VideoPlayerControlsState extends ConsumerState children: [ if (_displayBufferingIndicator) const Center( - child: ImmichLoadingIndicator(), + child: DelayedLoadingIndicator( + fadeInDuration: Duration(milliseconds: 400), + ), ) else _buildHitArea(), @@ -79,6 +81,7 @@ class VideoPlayerControlsState extends ConsumerState @override void dispose() { _dispose(); + super.dispose(); } @@ -92,6 +95,7 @@ class VideoPlayerControlsState extends ConsumerState final oldController = _chewieController; _chewieController = ChewieController.of(context); controller = chewieController.videoPlayerController; + _latestValue = controller.value; if (oldController != chewieController) { _dispose(); @@ -106,12 +110,10 @@ class VideoPlayerControlsState extends ConsumerState return GestureDetector( onTap: () { - if (_latestValue.isPlaying) { - ref.read(showControlsProvider.notifier).show = false; - } else { + if (!_latestValue.isPlaying) { _playPause(); - ref.read(showControlsProvider.notifier).show = false; } + ref.read(showControlsProvider.notifier).show = false; }, child: CenterPlayButton( backgroundColor: Colors.black54, @@ -131,10 +133,11 @@ class VideoPlayerControlsState extends ConsumerState } Future _initialize() async { + ref.read(showControlsProvider.notifier).show = false; _mute(ref.read(videoPlayerControlsProvider.select((value) => value.mute))); - controller.addListener(_updateState); _latestValue = controller.value; + controller.addListener(_updateState); if (controller.value.isPlaying || chewieController.autoPlay) { _startHideTimer(); @@ -167,9 +170,8 @@ class VideoPlayerControlsState extends ConsumerState } void _startHideTimer() { - final hideControlsTimer = chewieController.hideControlsTimer.isNegative - ? ChewieController.defaultHideControlsTimer - : chewieController.hideControlsTimer; + final hideControlsTimer = chewieController.hideControlsTimer; + _hideTimer?.cancel(); _hideTimer = Timer(hideControlsTimer, () { ref.read(showControlsProvider.notifier).show = false; }); diff --git a/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart b/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart index 6fa41c8124..bff0f7b796 100644 --- a/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart +++ b/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart @@ -1,8 +1,9 @@ +import 'dart:async'; import 'dart:io'; import 'dart:math'; +import 'dart:ui' as ui; import 'package:easy_localization/easy_localization.dart'; import 'package:auto_route/auto_route.dart'; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart' hide Store; @@ -11,6 +12,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/modules/album/models/album.model.dart'; import 'package:immich_mobile/modules/album/providers/current_album.provider.dart'; +import 'package:immich_mobile/modules/asset_viewer/image_providers/immich_remote_image_provider.dart'; import 'package:immich_mobile/modules/asset_viewer/providers/asset_stack.provider.dart'; import 'package:immich_mobile/modules/asset_viewer/providers/current_asset.provider.dart'; import 'package:immich_mobile/modules/asset_viewer/providers/show_controls.provider.dart'; @@ -27,13 +29,13 @@ import 'package:immich_mobile/modules/backup/providers/manual_upload.provider.da import 'package:immich_mobile/modules/home/ui/upload_dialog.dart'; import 'package:immich_mobile/modules/partner/providers/partner.provider.dart'; import 'package:immich_mobile/routing/router.dart'; -import 'package:immich_mobile/shared/models/store.dart'; import 'package:immich_mobile/modules/home/ui/delete_dialog.dart'; import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; import 'package:immich_mobile/shared/providers/server_info.provider.dart'; import 'package:immich_mobile/shared/providers/user.provider.dart'; import 'package:immich_mobile/shared/ui/immich_image.dart'; +import 'package:immich_mobile/shared/ui/immich_thumbnail.dart'; import 'package:immich_mobile/shared/ui/immich_toast.dart'; import 'package:immich_mobile/shared/ui/photo_view/photo_view_gallery.dart'; import 'package:immich_mobile/shared/ui/photo_view/src/photo_view_computed_scale.dart'; @@ -132,14 +134,15 @@ class GalleryViewerPage extends HookConsumerWidget { void toggleFavorite(Asset asset) => ref.read(assetProvider.notifier).toggleFavorite([asset]); - void precacheNextImage(int index) { + Future precacheNextImage(int index) async { void onError(Object exception, StackTrace? stackTrace) { // swallow error silently debugPrint('Error precaching next image: $exception, $stackTrace'); } + if (index < totalAssets && index >= 0) { final asset = loadAsset(index); - precacheImage( + await precacheImage( ImmichImage.imageProvider(asset: asset), context, onError: onError, @@ -153,10 +156,11 @@ class GalleryViewerPage extends HookConsumerWidget { borderRadius: BorderRadius.all(Radius.circular(15.0)), ), barrierColor: Colors.transparent, - backgroundColor: Colors.transparent, isScrollControlled: true, - useSafeArea: true, + showDragHandle: true, + enableDrag: true, context: context, + useSafeArea: true, builder: (context) { return Padding( padding: EdgeInsets.only( @@ -480,15 +484,9 @@ class GalleryViewerPage extends HookConsumerWidget { ), child: ClipRRect( borderRadius: BorderRadius.circular(4), - child: CachedNetworkImage( + child: Image( fit: BoxFit.cover, - imageUrl: - '${Store.get(StoreKey.serverEndpoint)}/asset/thumbnail/$assetId', - httpHeaders: { - "x-immich-user-token": Store.get(StoreKey.accessToken), - }, - errorWidget: (context, url, error) => - const Icon(Icons.image_not_supported_outlined), + image: ImmichRemoteImageProvider(assetId: assetId!), ), ), ), @@ -703,6 +701,33 @@ class GalleryViewerPage extends HookConsumerWidget { ); } + useEffect( + () { + if (ref.read(showControlsProvider)) { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + } else { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive); + } + return null; + }, + [], + ); + + useEffect( + () { + // No need to await this + unawaited( + // Delay this a bit so we can finish loading the page + Future.delayed(const Duration(milliseconds: 400)).then( + // Precache the next image + (_) => precacheNextImage(currentIndex.value + 1), + ), + ); + return null; + }, + [], + ); + ref.listen(showControlsProvider, (_, show) { if (show) { SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); @@ -727,9 +752,22 @@ class GalleryViewerPage extends HookConsumerWidget { isZoomed.value = state != PhotoViewScaleState.initial; ref.read(showControlsProvider.notifier).show = !isZoomed.value; }, - loadingBuilder: (context, event, index) => ImmichImage.thumbnail( - asset(), - fit: BoxFit.contain, + loadingBuilder: (context, event, index) => ClipRect( + child: Stack( + fit: StackFit.expand, + children: [ + BackdropFilter( + filter: ui.ImageFilter.blur( + sigmaX: 10, + sigmaY: 10, + ), + ), + ImmichThumbnail( + asset: asset(), + fit: BoxFit.contain, + ), + ], + ), ), pageController: controller, scrollPhysics: isZoomed.value @@ -740,12 +778,16 @@ class GalleryViewerPage extends HookConsumerWidget { ), itemCount: totalAssets, scrollDirection: Axis.horizontal, - onPageChanged: (value) { + onPageChanged: (value) async { final next = currentIndex.value < value ? value + 1 : value - 1; - precacheNextImage(next); + HapticFeedback.selectionClick(); currentIndex.value = value; stackIndex.value = -1; - HapticFeedback.selectionClick(); + + // Wait for page change animation to finish + await Future.delayed(const Duration(milliseconds: 400)); + // Then precache the next image + unawaited(precacheNextImage(next)); }, builder: (context, index) { final a = @@ -793,7 +835,9 @@ class GalleryViewerPage extends HookConsumerWidget { minScale: 1.0, basePosition: Alignment.center, child: VideoViewerPage( - onPlaying: () => isPlayingVideo.value = true, + onPlaying: () { + isPlayingVideo.value = true; + }, onPaused: () => WidgetsBinding.instance.addPostFrameCallback( (_) => isPlayingVideo.value = false, @@ -802,7 +846,7 @@ class GalleryViewerPage extends HookConsumerWidget { isMotionVideo: isPlayingMotionVideo.value, placeholder: Image( image: provider, - fit: BoxFit.fitWidth, + fit: BoxFit.contain, height: context.height, width: context.width, alignment: Alignment.center, diff --git a/mobile/lib/modules/asset_viewer/views/video_viewer_page.dart b/mobile/lib/modules/asset_viewer/views/video_viewer_page.dart index 72aa397f67..0da2bc52db 100644 --- a/mobile/lib/modules/asset_viewer/views/video_viewer_page.dart +++ b/mobile/lib/modules/asset_viewer/views/video_viewer_page.dart @@ -1,23 +1,15 @@ -import 'dart:io'; - import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:chewie/chewie.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/modules/asset_viewer/models/image_viewer_page_state.model.dart'; -import 'package:immich_mobile/modules/asset_viewer/providers/image_viewer_page_state.provider.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:immich_mobile/modules/asset_viewer/hooks/chewiew_controller_hook.dart'; import 'package:immich_mobile/modules/asset_viewer/ui/video_player_controls.dart'; import 'package:immich_mobile/shared/models/asset.dart'; -import 'package:immich_mobile/shared/models/store.dart'; -import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; -import 'package:photo_manager/photo_manager.dart'; -import 'package:video_player/video_player.dart'; -import 'package:wakelock_plus/wakelock_plus.dart'; +import 'package:immich_mobile/shared/ui/delayed_loading_indicator.dart'; @RoutePage() // ignore: must_be_immutable -class VideoViewerPage extends HookConsumerWidget { +class VideoViewerPage extends HookWidget { final Asset asset; final bool isMotionVideo; final Widget? placeholder; @@ -42,211 +34,53 @@ class VideoViewerPage extends HookConsumerWidget { }); @override - Widget build(BuildContext context, WidgetRef ref) { - if (asset.isLocal && asset.livePhotoVideoId == null) { - final AsyncValue videoFile = ref.watch(_fileFamily(asset.local!)); - return AnimatedSwitcher( - duration: const Duration(milliseconds: 200), - child: videoFile.when( - data: (data) => VideoPlayer( - file: data, - isMotionVideo: false, - onVideoEnded: () {}, - ), - error: (error, stackTrace) => Icon( - Icons.image_not_supported_outlined, - color: context.primaryColor, - ), - loading: () => showDownloadingIndicator - ? const Center(child: ImmichLoadingIndicator()) - : Container(), - ), - ); - } - final downloadAssetStatus = - ref.watch(imageViewerStateProvider).downloadAssetStatus; - final String videoUrl = isMotionVideo - ? '${Store.get(StoreKey.serverEndpoint)}/asset/file/${asset.livePhotoVideoId}' - : '${Store.get(StoreKey.serverEndpoint)}/asset/file/${asset.remoteId}'; - - return Stack( - children: [ - VideoPlayer( - url: videoUrl, - accessToken: Store.get(StoreKey.accessToken), - isMotionVideo: isMotionVideo, - onVideoEnded: onVideoEnded, - onPaused: onPaused, - onPlaying: onPlaying, - placeholder: placeholder, - hideControlsTimer: hideControlsTimer, - showControls: showControls, - showDownloadingIndicator: showDownloadingIndicator, - ), - AnimatedOpacity( - duration: const Duration(milliseconds: 400), - opacity: (downloadAssetStatus == DownloadAssetStatus.loading && - showDownloadingIndicator) - ? 1.0 - : 0.0, - child: SizedBox( - height: context.height, - width: context.width, - child: const Center( - child: ImmichLoadingIndicator(), - ), - ), - ), - ], - ); - } -} - -final _fileFamily = - FutureProvider.family((ref, entity) async { - final file = await entity.file; - if (file == null) { - throw Exception(); - } - return file; -}); - -class VideoPlayer extends StatefulWidget { - final String? url; - final String? accessToken; - final File? file; - final bool isMotionVideo; - final VoidCallback? onVideoEnded; - final Duration hideControlsTimer; - final bool showControls; - - final Function()? onPlaying; - final Function()? onPaused; - - /// The placeholder to show while the video is loading - /// usually, a thumbnail of the video - final Widget? placeholder; - - final bool showDownloadingIndicator; - - const VideoPlayer({ - super.key, - this.url, - this.accessToken, - this.file, - this.onVideoEnded, - required this.isMotionVideo, - this.onPlaying, - this.onPaused, - this.placeholder, - this.hideControlsTimer = const Duration( - seconds: 5, - ), - this.showControls = true, - this.showDownloadingIndicator = true, - }); - - @override - State createState() => _VideoPlayerState(); -} - -class _VideoPlayerState extends State { - late VideoPlayerController videoPlayerController; - ChewieController? chewieController; - - @override - void initState() { - super.initState(); - initializePlayer(); - - videoPlayerController.addListener(() { - if (videoPlayerController.value.isInitialized) { - if (videoPlayerController.value.isPlaying) { - WakelockPlus.enable(); - widget.onPlaying?.call(); - } else if (!videoPlayerController.value.isPlaying) { - WakelockPlus.disable(); - widget.onPaused?.call(); - } - - if (videoPlayerController.value.position == - videoPlayerController.value.duration) { - WakelockPlus.disable(); - widget.onVideoEnded?.call(); - } - } - }); - } - - Future initializePlayer() async { - try { - videoPlayerController = widget.file == null - ? VideoPlayerController.networkUrl( - Uri.parse(widget.url!), - httpHeaders: {"x-immich-user-token": widget.accessToken ?? ""}, - ) - : VideoPlayerController.file(widget.file!); - - await videoPlayerController.initialize(); - _createChewieController(); - setState(() {}); - } catch (e) { - debugPrint("ERROR initialize video player $e"); - } - } - - _createChewieController() { - chewieController = ChewieController( + Widget build(BuildContext context) { + final controller = useChewieController( + asset, controlsSafeAreaMinimum: const EdgeInsets.only( bottom: 100, ), - showOptions: true, - showControlsOnInitialize: false, - videoPlayerController: videoPlayerController, - autoPlay: true, - autoInitialize: true, - allowFullScreen: false, - allowedScreenSleep: false, - showControls: widget.showControls && !widget.isMotionVideo, + placeholder: placeholder, + showControls: showControls && !isMotionVideo, + hideControlsTimer: hideControlsTimer, customControls: const VideoPlayerControls(), - hideControlsTimer: widget.hideControlsTimer, + onPlaying: onPlaying, + onPaused: onPaused, + onVideoEnded: onVideoEnded, + ); + + // Loading + return PopScope( + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 400), + child: Builder( + builder: (context) { + if (controller == null) { + return Stack( + children: [ + if (placeholder != null) placeholder!, + const Positioned.fill( + child: Center( + child: DelayedLoadingIndicator( + fadeInDuration: Duration(milliseconds: 500), + ), + ), + ), + ], + ); + } + + final size = MediaQuery.of(context).size; + return SizedBox( + height: size.height, + width: size.width, + child: Chewie( + controller: controller, + ), + ); + }, + ), + ), ); } - - @override - void dispose() { - super.dispose(); - videoPlayerController.pause(); - videoPlayerController.dispose(); - chewieController?.dispose(); - } - - @override - Widget build(BuildContext context) { - if (chewieController?.videoPlayerController.value.isInitialized == true) { - return SizedBox( - height: context.height, - width: context.width, - child: Chewie( - controller: chewieController!, - ), - ); - } else { - return SizedBox( - height: context.height, - width: context.width, - child: Center( - child: Stack( - children: [ - if (widget.placeholder != null) widget.placeholder!, - if (widget.showDownloadingIndicator) - const Center( - child: ImmichLoadingIndicator(), - ), - ], - ), - ), - ); - } - } } diff --git a/mobile/lib/modules/home/providers/upload_profile_image.provider.dart b/mobile/lib/modules/home/providers/upload_profile_image.provider.dart index 66060da383..a0dc925902 100644 --- a/mobile/lib/modules/home/providers/upload_profile_image.provider.dart +++ b/mobile/lib/modules/home/providers/upload_profile_image.provider.dart @@ -88,7 +88,7 @@ class UploadProfileImageNotifier var res = await _userSErvice.uploadProfileImage(file); if (res != null) { - debugPrint("Succesfully upload profile image"); + debugPrint("Successfully upload profile image"); state = state.copyWith( status: UploadProfileStatus.success, profileImagePath: res.profileImagePath, diff --git a/mobile/lib/modules/home/ui/asset_grid/immich_asset_grid_view.dart b/mobile/lib/modules/home/ui/asset_grid/immich_asset_grid_view.dart index bd671ca6ba..8a63108167 100644 --- a/mobile/lib/modules/home/ui/asset_grid/immich_asset_grid_view.dart +++ b/mobile/lib/modules/home/ui/asset_grid/immich_asset_grid_view.dart @@ -10,6 +10,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/collection_extensions.dart'; import 'package:immich_mobile/modules/asset_viewer/providers/scroll_notifier.provider.dart'; import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_image.dart'; +import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_placeholder.dart'; import 'package:immich_mobile/shared/models/asset.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; @@ -127,184 +128,6 @@ class ImmichAssetGridViewState extends State { assets.firstWhereOrNull((e) => !_selectedAssets.contains(e)) == null; } - Widget _buildThumbnailOrPlaceholder(Asset asset, int index) { - return ThumbnailImage( - asset: asset, - index: index, - loadAsset: widget.renderList.loadAsset, - totalAssets: widget.renderList.totalAssets, - multiselectEnabled: widget.selectionActive, - isSelected: widget.selectionActive && _selectedAssets.contains(asset), - onSelect: () => _selectAssets([asset]), - onDeselect: widget.canDeselect || - widget.preselectedAssets == null || - !widget.preselectedAssets!.contains(asset) - ? () => _deselectAssets([asset]) - : null, - useGrayBoxPlaceholder: true, - showStorageIndicator: widget.showStorageIndicator, - heroOffset: widget.heroOffset, - showStack: widget.showStack, - ); - } - - Widget _buildAssetRow( - Key key, - BuildContext context, - List assets, - int absoluteOffset, - double width, - ) { - // Default: All assets have the same width - final widthDistribution = List.filled(assets.length, 1.0); - - if (widget.dynamicLayout) { - final aspectRatios = - assets.map((e) => (e.width ?? 1) / (e.height ?? 1)).toList(); - final meanAspectRatio = aspectRatios.sum / assets.length; - - // 1: mean width - // 0.5: width < mean - threshold - // 1.5: width > mean + threshold - final arConfiguration = aspectRatios.map((e) { - if (e - meanAspectRatio > 0.3) return 1.5; - if (e - meanAspectRatio < -0.3) return 0.5; - return 1.0; - }); - - // Normalize: - final sum = arConfiguration.sum; - widthDistribution.setRange( - 0, - widthDistribution.length, - arConfiguration.map((e) => (e * assets.length) / sum), - ); - } - return Row( - key: key, - children: assets.mapIndexed((int index, Asset asset) { - final bool last = index + 1 == widget.assetsPerRow; - return Container( - key: ValueKey(index), - width: width * widthDistribution[index], - height: width, - margin: EdgeInsets.only( - bottom: widget.margin, - right: last ? 0.0 : widget.margin, - ), - child: _buildThumbnailOrPlaceholder(asset, absoluteOffset + index), - ); - }).toList(), - ); - } - - Widget _buildTitle( - BuildContext context, - String title, - List assets, - ) { - return GroupDividerTitle( - text: title, - multiselectEnabled: widget.selectionActive, - onSelect: () => _selectAssets(assets), - onDeselect: () => _deselectAssets(assets), - selected: _allAssetsSelected(assets), - ); - } - - Widget _buildMonthTitle(BuildContext context, DateTime date) { - final monthFormat = DateTime.now().year == date.year - ? DateFormat.MMMM() - : DateFormat.yMMMM(); - final String title = monthFormat.format(date); - return Padding( - key: Key("month-$title"), - padding: const EdgeInsets.only(left: 12.0, top: 24.0), - child: Text( - title, - style: const TextStyle( - fontSize: 26, - fontWeight: FontWeight.w500, - ), - ), - ); - } - - Widget _buildPlaceHolderRow(Key key, int num, double width, double height) { - return Row( - key: key, - children: [ - for (int i = 0; i < num; i++) - Container( - key: ValueKey(i), - width: width, - height: height, - margin: EdgeInsets.only( - bottom: widget.margin, - right: i + 1 == num ? 0.0 : widget.margin, - ), - color: Colors.grey, - ), - ], - ); - } - - Widget _buildSection( - BuildContext context, - RenderAssetGridElement section, - bool scrolling, - ) { - return LayoutBuilder( - builder: (context, constraints) { - final width = constraints.maxWidth / widget.assetsPerRow - - widget.margin * (widget.assetsPerRow - 1) / widget.assetsPerRow; - final rows = - (section.count + widget.assetsPerRow - 1) ~/ widget.assetsPerRow; - final List assetsToRender = scrolling - ? [] - : widget.renderList.loadAssets(section.offset, section.count); - return Column( - key: ValueKey(section.offset), - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (section.type == RenderAssetGridElementType.monthTitle) - _buildMonthTitle(context, section.date), - if (section.type == RenderAssetGridElementType.groupDividerTitle || - section.type == RenderAssetGridElementType.monthTitle) - _buildTitle( - context, - section.title!, - scrolling - ? [] - : widget.renderList - .loadAssets(section.offset, section.totalCount), - ), - for (int i = 0; i < rows; i++) - scrolling - ? _buildPlaceHolderRow( - ValueKey(i), - i + 1 == rows - ? section.count - i * widget.assetsPerRow - : widget.assetsPerRow, - width, - width, - ) - : _buildAssetRow( - ValueKey(i), - context, - assetsToRender.nestedSlice( - i * widget.assetsPerRow, - min((i + 1) * widget.assetsPerRow, section.count), - ), - section.offset + i * widget.assetsPerRow, - width, - ), - ], - ); - }, - ); - } - Widget _itemBuilder(BuildContext c, int position) { int index = position; if (widget.topWidget != null) { @@ -314,8 +137,23 @@ class ImmichAssetGridViewState extends State { index--; } - final item = widget.renderList.elements[index]; - return _buildSection(c, item, _scrolling); + final section = widget.renderList.elements[index]; + return _Section( + showStorageIndicator: widget.showStorageIndicator, + selectedAssets: _selectedAssets, + selectionActive: widget.selectionActive, + section: section, + margin: widget.margin, + renderList: widget.renderList, + assetsPerRow: widget.assetsPerRow, + scrolling: _scrolling, + dynamicLayout: widget.dynamicLayout, + selectAssets: _selectAssets, + deselectAssets: _deselectAssets, + allAssetsSelected: _allAssetsSelected, + showStack: widget.showStack, + heroOffset: widget.heroOffset, + ); } Text _labelBuilder(int pos) { @@ -452,7 +290,7 @@ class ImmichAssetGridViewState extends State { final now = Timeline.now; if (now > (_hapticFeedbackTS + feedbackInterval)) { _hapticFeedbackTS = now; - HapticFeedback.heavyImpact(); + HapticFeedback.mediumImpact(); } } } @@ -485,3 +323,292 @@ class ImmichAssetGridViewState extends State { ); } } + +/// A single row of all placeholder widgets +class _PlaceholderRow extends StatelessWidget { + final int number; + final double width; + final double height; + final double margin; + + const _PlaceholderRow({ + super.key, + required this.number, + required this.width, + required this.height, + required this.margin, + }); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + for (int i = 0; i < number; i++) + ThumbnailPlaceholder( + key: ValueKey(i), + width: width, + height: height, + margin: EdgeInsets.only( + bottom: margin, + right: i + 1 == number ? 0.0 : margin, + ), + ), + ], + ); + } +} + +/// A section for the render grid +class _Section extends StatelessWidget { + final RenderAssetGridElement section; + final Set selectedAssets; + final bool scrolling; + final double margin; + final int assetsPerRow; + final RenderList renderList; + final bool selectionActive; + final bool dynamicLayout; + final Function(List) selectAssets; + final Function(List) deselectAssets; + final bool Function(List) allAssetsSelected; + final bool showStack; + final int heroOffset; + final bool showStorageIndicator; + + const _Section({ + required this.section, + required this.scrolling, + required this.margin, + required this.assetsPerRow, + required this.renderList, + required this.selectionActive, + required this.dynamicLayout, + required this.selectAssets, + required this.deselectAssets, + required this.allAssetsSelected, + required this.selectedAssets, + required this.showStack, + required this.heroOffset, + required this.showStorageIndicator, + }); + + @override + Widget build( + BuildContext context, + ) { + return LayoutBuilder( + builder: (context, constraints) { + final width = constraints.maxWidth / assetsPerRow - + margin * (assetsPerRow - 1) / assetsPerRow; + final rows = (section.count + assetsPerRow - 1) ~/ assetsPerRow; + final List assetsToRender = scrolling + ? [] + : renderList.loadAssets(section.offset, section.count); + return Column( + key: ValueKey(section.offset), + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (section.type == RenderAssetGridElementType.monthTitle) + _MonthTitle(date: section.date), + if (section.type == RenderAssetGridElementType.groupDividerTitle || + section.type == RenderAssetGridElementType.monthTitle) + _Title( + selectionActive: selectionActive, + title: section.title!, + assets: scrolling + ? [] + : renderList.loadAssets(section.offset, section.totalCount), + allAssetsSelected: allAssetsSelected, + selectAssets: selectAssets, + deselectAssets: deselectAssets, + ), + for (int i = 0; i < rows; i++) + scrolling + ? _PlaceholderRow( + key: ValueKey(i), + number: i + 1 == rows + ? section.count - i * assetsPerRow + : assetsPerRow, + width: width, + height: width, + margin: margin, + ) + : _AssetRow( + key: ValueKey(i), + assets: assetsToRender.nestedSlice( + i * assetsPerRow, + min((i + 1) * assetsPerRow, section.count), + ), + absoluteOffset: section.offset + i * assetsPerRow, + width: width, + assetsPerRow: assetsPerRow, + margin: margin, + dynamicLayout: dynamicLayout, + renderList: renderList, + selectedAssets: selectedAssets, + isSelectionActive: selectionActive, + showStack: showStack, + heroOffset: heroOffset, + showStorageIndicator: showStorageIndicator, + selectionActive: selectionActive, + onSelect: (asset) => selectAssets([asset]), + onDeselect: (asset) => deselectAssets([asset]), + ), + ], + ); + }, + ); + } +} + +/// The month title row for a section +class _MonthTitle extends StatelessWidget { + final DateTime date; + + const _MonthTitle({ + required this.date, + }); + + @override + Widget build(BuildContext context) { + final monthFormat = DateTime.now().year == date.year + ? DateFormat.MMMM() + : DateFormat.yMMMM(); + final String title = monthFormat.format(date); + return Padding( + key: Key("month-$title"), + padding: const EdgeInsets.only(left: 12.0, top: 24.0), + child: Text( + title, + style: const TextStyle( + fontSize: 26, + fontWeight: FontWeight.w500, + ), + ), + ); + } +} + +/// A title row +class _Title extends StatelessWidget { + final String title; + final List assets; + final bool selectionActive; + final Function(List) selectAssets; + final Function(List) deselectAssets; + final Function(List) allAssetsSelected; + + const _Title({ + required this.title, + required this.assets, + required this.selectionActive, + required this.selectAssets, + required this.deselectAssets, + required this.allAssetsSelected, + }); + + @override + Widget build(BuildContext context) { + return GroupDividerTitle( + text: title, + multiselectEnabled: selectionActive, + onSelect: () => selectAssets(assets), + onDeselect: () => deselectAssets(assets), + selected: allAssetsSelected(assets), + ); + } +} + +/// The row of assets +class _AssetRow extends StatelessWidget { + final List assets; + final Set selectedAssets; + final int absoluteOffset; + final double width; + final bool dynamicLayout; + final double margin; + final int assetsPerRow; + final RenderList renderList; + final bool selectionActive; + final bool showStorageIndicator; + final int heroOffset; + final bool showStack; + final Function(Asset)? onSelect; + final Function(Asset)? onDeselect; + final bool isSelectionActive; + + const _AssetRow({ + super.key, + required this.assets, + required this.absoluteOffset, + required this.width, + required this.dynamicLayout, + required this.margin, + required this.assetsPerRow, + required this.renderList, + required this.selectionActive, + required this.showStorageIndicator, + required this.heroOffset, + required this.showStack, + required this.isSelectionActive, + required this.selectedAssets, + this.onSelect, + this.onDeselect, + }); + + @override + Widget build(BuildContext context) { + // Default: All assets have the same width + final widthDistribution = List.filled(assets.length, 1.0); + + if (dynamicLayout) { + final aspectRatios = + assets.map((e) => (e.width ?? 1) / (e.height ?? 1)).toList(); + final meanAspectRatio = aspectRatios.sum / assets.length; + + // 1: mean width + // 0.5: width < mean - threshold + // 1.5: width > mean + threshold + final arConfiguration = aspectRatios.map((e) { + if (e - meanAspectRatio > 0.3) return 1.5; + if (e - meanAspectRatio < -0.3) return 0.5; + return 1.0; + }); + + // Normalize: + final sum = arConfiguration.sum; + widthDistribution.setRange( + 0, + widthDistribution.length, + arConfiguration.map((e) => (e * assets.length) / sum), + ); + } + return Row( + key: key, + children: assets.mapIndexed((int index, Asset asset) { + final bool last = index + 1 == assetsPerRow; + return Container( + width: width * widthDistribution[index], + height: width, + margin: EdgeInsets.only( + bottom: margin, + right: last ? 0.0 : margin, + ), + child: ThumbnailImage( + asset: asset, + index: absoluteOffset + index, + loadAsset: renderList.loadAsset, + totalAssets: renderList.totalAssets, + multiselectEnabled: selectionActive, + isSelected: isSelectionActive && selectedAssets.contains(asset), + onSelect: () => onSelect?.call(asset), + onDeselect: () => onDeselect?.call(asset), + showStorageIndicator: showStorageIndicator, + heroOffset: heroOffset, + showStack: showStack, + ), + ); + }).toList(), + ); + } +} diff --git a/mobile/lib/modules/home/ui/asset_grid/thumbnail_image.dart b/mobile/lib/modules/home/ui/asset_grid/thumbnail_image.dart index 6b0e83e527..a194bc2ade 100644 --- a/mobile/lib/modules/home/ui/asset_grid/thumbnail_image.dart +++ b/mobile/lib/modules/home/ui/asset_grid/thumbnail_image.dart @@ -4,7 +4,7 @@ import 'package:flutter/services.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/shared/models/asset.dart'; -import 'package:immich_mobile/shared/ui/immich_image.dart'; +import 'package:immich_mobile/shared/ui/immich_thumbnail.dart'; import 'package:immich_mobile/utils/storage_indicator.dart'; import 'package:isar/isar.dart'; @@ -15,7 +15,6 @@ class ThumbnailImage extends StatelessWidget { final int totalAssets; final bool showStorageIndicator; final bool showStack; - final bool useGrayBoxPlaceholder; final bool isSelected; final bool multiselectEnabled; final Function? onSelect; @@ -30,7 +29,6 @@ class ThumbnailImage extends StatelessWidget { required this.totalAssets, this.showStorageIndicator = true, this.showStack = false, - this.useGrayBoxPlaceholder = false, this.isSelected = false, this.multiselectEnabled = false, this.onDeselect, @@ -136,8 +134,10 @@ class ThumbnailImage extends StatelessWidget { tag: isFromDto ? '${asset.remoteId}-$heroOffset' : asset.id + heroOffset, - child: ImmichImage.thumbnail( - asset, + child: ImmichThumbnail( + asset: asset, + height: 250, + width: 250, ), ), ); diff --git a/mobile/lib/modules/home/ui/asset_grid/thumbnail_placeholder.dart b/mobile/lib/modules/home/ui/asset_grid/thumbnail_placeholder.dart new file mode 100644 index 0000000000..d762704835 --- /dev/null +++ b/mobile/lib/modules/home/ui/asset_grid/thumbnail_placeholder.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; + +class ThumbnailPlaceholder extends StatelessWidget { + final EdgeInsets margin; + final double width; + final double height; + + const ThumbnailPlaceholder({ + super.key, + this.margin = EdgeInsets.zero, + this.width = 250, + this.height = 250, + }); + + static const _brightColors = [ + Color(0xFFF1F3F4), + Color(0xFFB4B6B8), + ]; + + static const _darkColors = [ + Color(0xFF3B3F42), + Color(0xFF2B2F32), + ]; + + @override + Widget build(BuildContext context) { + return Container( + width: width, + height: height, + margin: margin, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: context.isDarkTheme ? _darkColors : _brightColors, + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + ); + } +} diff --git a/mobile/lib/modules/home/ui/control_bottom_app_bar.dart b/mobile/lib/modules/home/ui/control_bottom_app_bar.dart index 0dd764e755..1f10cdc971 100644 --- a/mobile/lib/modules/home/ui/control_bottom_app_bar.dart +++ b/mobile/lib/modules/home/ui/control_bottom_app_bar.dart @@ -186,35 +186,47 @@ class ControlBottomAppBar extends ConsumerWidget { ), ), if (hasRemote && onEditTime != null) - ControlBoxButton( - iconData: Icons.edit_calendar_outlined, - label: "control_bottom_app_bar_edit_time".tr(), - onPressed: enabled ? onEditTime : null, + ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 95), + child: ControlBoxButton( + iconData: Icons.edit_calendar_outlined, + label: "control_bottom_app_bar_edit_time".tr(), + onPressed: enabled ? onEditTime : null, + ), ), if (hasRemote && onEditLocation != null) - ControlBoxButton( - iconData: Icons.edit_location_alt_outlined, - label: "control_bottom_app_bar_edit_location".tr(), - onPressed: enabled ? onEditLocation : null, + ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 90), + child: ControlBoxButton( + iconData: Icons.edit_location_alt_outlined, + label: "control_bottom_app_bar_edit_location".tr(), + onPressed: enabled ? onEditLocation : null, + ), ), if (!selectionAssetState.hasLocal && selectionAssetState.selectedCount > 1 && onStack != null) - ControlBoxButton( - iconData: Icons.filter_none_rounded, - label: "control_bottom_app_bar_stack".tr(), - onPressed: enabled ? onStack : null, + ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 90), + child: ControlBoxButton( + iconData: Icons.filter_none_rounded, + label: "control_bottom_app_bar_stack".tr(), + onPressed: enabled ? onStack : null, + ), ), if (onRemoveFromAlbum != null) - ControlBoxButton( - iconData: Icons.delete_sweep_rounded, - label: 'album_viewer_appbar_share_remove'.tr(), - onPressed: enabled ? onRemoveFromAlbum : null, + ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 90), + child: ControlBoxButton( + iconData: Icons.delete_sweep_rounded, + label: 'album_viewer_appbar_share_remove'.tr(), + onPressed: enabled ? onRemoveFromAlbum : null, + ), ), if (selectionAssetState.hasLocal) ControlBoxButton( iconData: Icons.backup_outlined, - label: "Upload", + label: "control_bottom_app_bar_upload".tr(), onPressed: enabled ? () => showDialog( context: context, @@ -230,9 +242,9 @@ class ControlBottomAppBar extends ConsumerWidget { } return DraggableScrollableSheet( - initialChildSize: hasRemote ? 0.30 : bottomPadding, + initialChildSize: hasRemote ? 0.35 : bottomPadding, minChildSize: bottomPadding, - maxChildSize: hasRemote ? 0.60 : bottomPadding, + maxChildSize: hasRemote ? 0.65 : bottomPadding, snap: true, builder: ( BuildContext context, @@ -257,9 +269,9 @@ class ControlBottomAppBar extends ConsumerWidget { children: [ const SizedBox(height: 12), const CustomDraggingHandle(), - const SizedBox(height: 24), + const SizedBox(height: 12), SizedBox( - height: 90, + height: 100, child: ListView( shrinkWrap: true, scrollDirection: Axis.horizontal, diff --git a/mobile/lib/modules/login/providers/authentication.provider.dart b/mobile/lib/modules/login/providers/authentication.provider.dart index f2512a6763..4abac52d37 100644 --- a/mobile/lib/modules/login/providers/authentication.provider.dart +++ b/mobile/lib/modules/login/providers/authentication.provider.dart @@ -108,7 +108,7 @@ class AuthenticationNotifier extends StateNotifier { .then((_) => log.info("Logout was successful for $userEmail")) .onError( (error, stackTrace) => - log.severe("Error logging out $userEmail", error, stackTrace), + log.severe("Logout failed for $userEmail", error, stackTrace), ); await Future.wait([ @@ -129,8 +129,8 @@ class AuthenticationNotifier extends StateNotifier { shouldChangePassword: false, isAuthenticated: false, ); - } catch (e) { - log.severe("Error logging out $e"); + } catch (e, stack) { + log.severe('Logout failed', e, stack); } } diff --git a/mobile/lib/modules/login/services/oauth.service.dart b/mobile/lib/modules/login/services/oauth.service.dart index 8f34c968eb..952c6fa8d6 100644 --- a/mobile/lib/modules/login/services/oauth.service.dart +++ b/mobile/lib/modules/login/services/oauth.service.dart @@ -36,7 +36,7 @@ class OAuthService { ), ); } catch (e, stack) { - log.severe("Error performing oAuthLogin: ${e.toString()}", e, stack); + log.severe("OAuth login failed", e, stack); return null; } } diff --git a/mobile/lib/modules/map/providers/map_state.provider.dart b/mobile/lib/modules/map/providers/map_state.provider.dart index de6265c233..f1d1a4dde4 100644 --- a/mobile/lib/modules/map/providers/map_state.provider.dart +++ b/mobile/lib/modules/map/providers/map_state.provider.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/response_extensions.dart'; import 'package:immich_mobile/modules/map/models/map_state.model.dart'; import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; @@ -51,7 +52,8 @@ class MapStateNotifier extends _$MapStateNotifier { lightStyleFetched: AsyncError(lightResponse.body, StackTrace.current), ); _log.severe( - "Cannot fetch map light style with status - ${lightResponse.statusCode} and response - ${lightResponse.body}", + "Cannot fetch map light style", + lightResponse.toLoggerString(), ); return; } @@ -77,9 +79,7 @@ class MapStateNotifier extends _$MapStateNotifier { state = state.copyWith( darkStyleFetched: AsyncError(darkResponse.body, StackTrace.current), ); - _log.severe( - "Cannot fetch map dark style with status - ${darkResponse.statusCode} and response - ${darkResponse.body}", - ); + _log.severe("Cannot fetch map dark style", darkResponse.toLoggerString()); return; } diff --git a/mobile/lib/modules/map/services/map.service.dart b/mobile/lib/modules/map/services/map.service.dart index b3a904cbf1..0a5036056a 100644 --- a/mobile/lib/modules/map/services/map.service.dart +++ b/mobile/lib/modules/map/services/map.service.dart @@ -28,6 +28,7 @@ class MapSerivce with ErrorLoggerMixin { return markers?.map(MapMarker.fromDto) ?? []; }, defaultValue: [], + errorMessage: "Failed to get map markers", ); } } diff --git a/mobile/lib/modules/map/utils/map_utils.dart b/mobile/lib/modules/map/utils/map_utils.dart index 46af81ce1d..f6e8349f51 100644 --- a/mobile/lib/modules/map/utils/map_utils.dart +++ b/mobile/lib/modules/map/utils/map_utils.dart @@ -105,10 +105,8 @@ class MapUtils { timeLimit: const Duration(seconds: 5), ); return (currentUserLocation, null); - } catch (error) { - _log.severe( - "Cannot get user's current location due to ${error.toString()}", - ); + } catch (error, stack) { + _log.severe("Cannot get user's current location", error, stack); return (null, LocationPermission.unableToDetermine); } } diff --git a/mobile/lib/modules/map/widgets/map_asset_grid.dart b/mobile/lib/modules/map/widgets/map_asset_grid.dart index d1f187e258..ad90d36ed1 100644 --- a/mobile/lib/modules/map/widgets/map_asset_grid.dart +++ b/mobile/lib/modules/map/widgets/map_asset_grid.dart @@ -147,7 +147,7 @@ class MapAssetGrid extends HookConsumerWidget { }, error: (error, stackTrace) { log.warning( - "Cannot get assets in the current map bounds $error", + "Cannot get assets in the current map bounds", error, stackTrace, ); diff --git a/mobile/lib/modules/memories/services/memory.service.dart b/mobile/lib/modules/memories/services/memory.service.dart index 8d2cd226a4..8ee203e6c9 100644 --- a/mobile/lib/modules/memories/services/memory.service.dart +++ b/mobile/lib/modules/memories/services/memory.service.dart @@ -47,7 +47,7 @@ class MemoryService { return memories.isNotEmpty ? memories : null; } catch (error, stack) { - log.severe("Cannot get memories ${error.toString()}", error, stack); + log.severe("Cannot get memories", error, stack); return null; } } diff --git a/mobile/lib/modules/memories/ui/memory_card.dart b/mobile/lib/modules/memories/ui/memory_card.dart index b5f6ab46e0..af57c272ae 100644 --- a/mobile/lib/modules/memories/ui/memory_card.dart +++ b/mobile/lib/modules/memories/ui/memory_card.dart @@ -1,13 +1,12 @@ import 'dart:ui'; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/modules/asset_viewer/views/video_viewer_page.dart'; import 'package:immich_mobile/shared/models/asset.dart'; -import 'package:immich_mobile/shared/models/store.dart'; +import 'package:immich_mobile/shared/ui/hooks/blurhash_hook.dart'; import 'package:immich_mobile/shared/ui/immich_image.dart'; -import 'package:immich_mobile/utils/image_url_builder.dart'; class MemoryCard extends StatelessWidget { final Asset asset; @@ -23,8 +22,6 @@ class MemoryCard extends StatelessWidget { super.key, }); - String get accessToken => Store.get(StoreKey.accessToken); - @override Widget build(BuildContext context) { return Card( @@ -39,32 +36,15 @@ class MemoryCard extends StatelessWidget { clipBehavior: Clip.hardEdge, child: Stack( children: [ - ImageFiltered( - imageFilter: ImageFilter.blur(sigmaX: 30, sigmaY: 30), - child: Container( - decoration: BoxDecoration( - image: DecorationImage( - image: CachedNetworkImageProvider( - getThumbnailUrl( - asset, - ), - cacheKey: getThumbnailCacheKey( - asset, - ), - headers: {"x-immich-user-token": accessToken}, - ), - fit: BoxFit.cover, - ), - ), - child: Container(color: Colors.black.withOpacity(0.2)), - ), + SizedBox.expand( + child: _BlurredBackdrop(asset: asset), ), LayoutBuilder( builder: (context, constraints) { // Determine the fit using the aspect ratio - BoxFit fit = BoxFit.fitWidth; + BoxFit fit = BoxFit.contain; if (asset.width != null && asset.height != null) { - final aspectRatio = asset.height! / asset.width!; + final aspectRatio = asset.width! / asset.height!; final phoneAspectRatio = constraints.maxWidth / constraints.maxHeight; // Look for a 25% difference in either direction @@ -120,3 +100,50 @@ class MemoryCard extends StatelessWidget { ); } } + +class _BlurredBackdrop extends HookWidget { + final Asset asset; + + const _BlurredBackdrop({required this.asset}); + + @override + Widget build(BuildContext context) { + final blurhash = useBlurHashRef(asset).value; + if (blurhash != null) { + // Use a nice cheap blur hash image decoration + return Container( + decoration: BoxDecoration( + image: DecorationImage( + image: MemoryImage( + blurhash, + ), + fit: BoxFit.cover, + ), + ), + child: Container( + color: Colors.black.withOpacity(0.2), + ), + ); + } else { + // Fall back to using a more expensive image filtered + // Since the ImmichImage is already precached, we can + // safely use that as the image provider + return ImageFiltered( + imageFilter: ImageFilter.blur(sigmaX: 30, sigmaY: 30), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: ImmichImage.imageProvider( + asset: asset, + ), + fit: BoxFit.cover, + ), + ), + child: Container( + color: Colors.black.withOpacity(0.2), + ), + ), + ); + } + } +} diff --git a/mobile/lib/modules/memories/ui/memory_lane.dart b/mobile/lib/modules/memories/ui/memory_lane.dart index 6b11e668db..eb72c15e8e 100644 --- a/mobile/lib/modules/memories/ui/memory_lane.dart +++ b/mobile/lib/modules/memories/ui/memory_lane.dart @@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_placeholder.dart'; import 'package:immich_mobile/modules/memories/providers/memory.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/shared/ui/immich_image.dart'; @@ -60,7 +61,10 @@ class MemoryLane extends HookConsumerWidget { fit: BoxFit.cover, width: 130, height: 200, - useGrayBoxPlaceholder: true, + placeholder: const ThumbnailPlaceholder( + width: 130, + height: 200, + ), ), ), ), diff --git a/mobile/lib/modules/memories/ui/memory_progress_indicator.dart b/mobile/lib/modules/memories/ui/memory_progress_indicator.dart index 697d910a4e..0ee3893cb9 100644 --- a/mobile/lib/modules/memories/ui/memory_progress_indicator.dart +++ b/mobile/lib/modules/memories/ui/memory_progress_indicator.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:immich_mobile/constants/immich_colors.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; class MemoryProgressIndicator extends StatelessWidget { /// The number of ticks in the progress indicator @@ -39,9 +38,9 @@ class MemoryProgressIndicator extends StatelessWidget { decoration: BoxDecoration( border: i == 0 ? null - : Border( + : const Border( left: BorderSide( - color: context.colorScheme.onSecondaryContainer, + color: Colors.black, width: 1, ), ), diff --git a/mobile/lib/modules/memories/views/memory_page.dart b/mobile/lib/modules/memories/views/memory_page.dart index 199af835c9..d06bb959ee 100644 --- a/mobile/lib/modules/memories/views/memory_page.dart +++ b/mobile/lib/modules/memories/views/memory_page.dart @@ -109,25 +109,13 @@ class MemoryPage extends HookConsumerWidget { asset = memories[nextMemoryIndex].assets.first; } - // Gets the thumbnail url and precaches it - final precaches = >[]; - - precaches.addAll([ - precacheImage( - ImmichImage.imageProvider( - asset: asset, - ), - context, + // Precache the asset + await precacheImage( + ImmichImage.imageProvider( + asset: asset, ), - precacheImage( - ImmichImage.imageProvider( - asset: asset, - isThumbnail: true, - ), - context, - ), - ]); - await Future.wait(precaches); + context, + ); } // Precache the next page right away if we are on the first page @@ -136,11 +124,14 @@ class MemoryPage extends HookConsumerWidget { .then((_) => precacheAsset(1)); } - onAssetChanged(int otherIndex) { + Future onAssetChanged(int otherIndex) async { HapticFeedback.selectionClick(); currentAssetPage.value = otherIndex; - precacheAsset(otherIndex + 1); updateProgressText(); + // Wait for page change animation to finish + await Future.delayed(const Duration(milliseconds: 400)); + // And then precache the next asset + await precacheAsset(otherIndex + 1); } /* Notification listener is used instead of OnPageChanged callback since OnPageChanged is called diff --git a/mobile/lib/modules/partner/services/partner.service.dart b/mobile/lib/modules/partner/services/partner.service.dart index 32e500353b..d1e40076c7 100644 --- a/mobile/lib/modules/partner/services/partner.service.dart +++ b/mobile/lib/modules/partner/services/partner.service.dart @@ -40,7 +40,7 @@ class PartnerService { return userDtos.map((u) => User.fromPartnerDto(u)).toList(); } } catch (e) { - _log.warning("failed to get partners for direction $direction:\n$e"); + _log.warning("Failed to get partners for direction $direction", e); } return null; } @@ -51,7 +51,7 @@ class PartnerService { partner.isPartnerSharedBy = false; await _db.writeTxn(() => _db.users.put(partner)); } catch (e) { - _log.warning("failed to remove partner ${partner.id}:\n$e"); + _log.warning("Failed to remove partner ${partner.id}", e); return false; } return true; @@ -66,7 +66,7 @@ class PartnerService { return true; } } catch (e) { - _log.warning("failed to add partner ${partner.id}:\n$e"); + _log.warning("Failed to add partner ${partner.id}", e); } return false; } @@ -81,7 +81,7 @@ class PartnerService { return true; } } catch (e) { - _log.warning("failed to update partner ${partner.id}:\n$e"); + _log.warning("Failed to update partner ${partner.id}", e); } return false; } diff --git a/mobile/lib/modules/shared_link/services/shared_link.service.dart b/mobile/lib/modules/shared_link/services/shared_link.service.dart index 3ea1d411b2..62f431580c 100644 --- a/mobile/lib/modules/shared_link/services/shared_link.service.dart +++ b/mobile/lib/modules/shared_link/services/shared_link.service.dart @@ -22,7 +22,7 @@ class SharedLinkService { ? AsyncData(list.map(SharedLink.fromDto).toList()) : const AsyncData([]); } catch (e, stack) { - _log.severe("failed to fetch shared links - $e"); + _log.severe("Failed to fetch shared links", e, stack); return AsyncError(e, stack); } } @@ -31,7 +31,7 @@ class SharedLinkService { try { return await _apiService.sharedLinkApi.removeSharedLink(id); } catch (e) { - _log.severe("failed to delete shared link id - $id with error - $e"); + _log.severe("Failed to delete shared link id - $id", e); } } @@ -81,7 +81,7 @@ class SharedLinkService { } } } catch (e) { - _log.severe("failed to create shared link with error - $e"); + _log.severe("Failed to create shared link", e); } return null; } @@ -113,7 +113,7 @@ class SharedLinkService { return SharedLink.fromDto(responseDto); } } catch (e) { - _log.severe("failed to update shared link id - $id with error - $e"); + _log.severe("Failed to update shared link id - $id", e); } return null; } diff --git a/mobile/lib/modules/trash/providers/trashed_asset.provider.dart b/mobile/lib/modules/trash/providers/trashed_asset.provider.dart index 177e7d2d4c..165d1c0f74 100644 --- a/mobile/lib/modules/trash/providers/trashed_asset.provider.dart +++ b/mobile/lib/modules/trash/providers/trashed_asset.provider.dart @@ -44,7 +44,7 @@ class TrashNotifier extends StateNotifier { .read(syncServiceProvider) .handleRemoteAssetRemoval(idsToRemove.cast().toList()); } catch (error, stack) { - _log.severe("Cannot empty trash ${error.toString()}", error, stack); + _log.severe("Cannot empty trash", error, stack); } } @@ -70,7 +70,7 @@ class TrashNotifier extends StateNotifier { return isRemoved; } catch (error, stack) { - _log.severe("Cannot empty trash ${error.toString()}", error, stack); + _log.severe("Cannot remove assets", error, stack); } return false; } @@ -93,7 +93,7 @@ class TrashNotifier extends StateNotifier { return true; } } catch (error, stack) { - _log.severe("Cannot restore trash ${error.toString()}", error, stack); + _log.severe("Cannot restore assets", error, stack); } return false; } @@ -123,7 +123,7 @@ class TrashNotifier extends StateNotifier { await _db.assets.putAll(updatedAssets); }); } catch (error, stack) { - _log.severe("Cannot restore trash ${error.toString()}", error, stack); + _log.severe("Cannot restore trash", error, stack); } } } diff --git a/mobile/lib/modules/trash/services/trash.service.dart b/mobile/lib/modules/trash/services/trash.service.dart index 9a9ff5d0b6..96b07ca20f 100644 --- a/mobile/lib/modules/trash/services/trash.service.dart +++ b/mobile/lib/modules/trash/services/trash.service.dart @@ -25,7 +25,7 @@ class TrashService { await _apiService.trashApi.restoreAssets(BulkIdsDto(ids: remoteIds)); return true; } catch (error, stack) { - _log.severe("Cannot restore assets ${error.toString()}", error, stack); + _log.severe("Cannot restore assets", error, stack); return false; } } @@ -34,7 +34,7 @@ class TrashService { try { await _apiService.trashApi.emptyTrash(); } catch (error, stack) { - _log.severe("Cannot empty trash ${error.toString()}", error, stack); + _log.severe("Cannot empty trash", error, stack); } } @@ -42,7 +42,7 @@ class TrashService { try { await _apiService.trashApi.restoreTrash(); } catch (error, stack) { - _log.severe("Cannot restore trash ${error.toString()}", error, stack); + _log.severe("Cannot restore trash", error, stack); } } } diff --git a/mobile/lib/routing/auth_guard.dart b/mobile/lib/routing/auth_guard.dart index 6aee9271f4..fe212c4ca9 100644 --- a/mobile/lib/routing/auth_guard.dart +++ b/mobile/lib/routing/auth_guard.dart @@ -1,8 +1,8 @@ import 'dart:io'; import 'package:auto_route/auto_route.dart'; -import 'package:flutter/foundation.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/shared/models/store.dart'; import 'package:immich_mobile/shared/services/api.service.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; @@ -16,28 +16,31 @@ class AuthGuard extends AutoRouteGuard { resolver.next(true); try { - var res = await _apiService.authenticationApi.validateAccessToken(); + // Look in the store for an access token + Store.get(StoreKey.accessToken); + + // Validate the access token with the server + final res = await _apiService.authenticationApi.validateAccessToken(); if (res == null || res.authStatus != true) { // If the access token is invalid, take user back to login - _log.fine("User token is invalid. Redirecting to login"); + _log.fine('User token is invalid. Redirecting to login'); router.replaceAll([const LoginRoute()]); } + } on StoreKeyNotFoundException catch (_) { + // If there is no access token, take us to the login page + _log.warning('No access token in the store.'); + router.replaceAll([const LoginRoute()]); + return; } on ApiException catch (e) { - if (e.code == HttpStatus.badRequest && - e.innerException is SocketException) { - // offline? - _log.fine( - "Unable to validate user token. User may be offline and offline browsing is allowed.", - ); - } else { - debugPrint("Error [onNavigation] ${e.toString()}"); + // On an unauthorized request, take us to the login page + if (e.code == HttpStatus.unauthorized) { + _log.warning("Unauthorized access token."); router.replaceAll([const LoginRoute()]); return; } } catch (e) { - debugPrint("Error [onNavigation] ${e.toString()}"); - router.replaceAll([const LoginRoute()]); - return; + // Otherwise, this is not fatal, but we still log the warning + _log.warning('Error validating access token from server: $e'); } } } diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index f32c75c37e..bfb91f5460 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -1399,7 +1399,7 @@ class VideoViewerRoute extends PageRouteInfo { void Function()? onPaused, Widget? placeholder, bool showControls = true, - Duration hideControlsTimer = const Duration(seconds: 5), + Duration hideControlsTimer = const Duration(milliseconds: 1500), bool showDownloadingIndicator = true, List? children, }) : super( @@ -1435,7 +1435,7 @@ class VideoViewerRouteArgs { this.onPaused, this.placeholder, this.showControls = true, - this.hideControlsTimer = const Duration(seconds: 5), + this.hideControlsTimer = const Duration(milliseconds: 1500), this.showDownloadingIndicator = true, }); diff --git a/mobile/lib/shared/models/asset.dart b/mobile/lib/shared/models/asset.dart index afd49adc6a..3c3c4df82f 100644 --- a/mobile/lib/shared/models/asset.dart +++ b/mobile/lib/shared/models/asset.dart @@ -38,7 +38,8 @@ class Asset { // stack handling to properly handle it stackParentId = remote.stackParentId == remote.id ? null : remote.stackParentId, - stackCount = remote.stackCount; + stackCount = remote.stackCount, + thumbhash = remote.thumbhash; Asset.local(AssetEntity local, List hash) : localId = local.id, @@ -91,6 +92,7 @@ class Asset { this.stackCount = 0, this.isReadOnly = false, this.isOffline = false, + this.thumbhash, }); @ignore @@ -119,6 +121,8 @@ class Asset { /// because Isar cannot sort lists of byte arrays String checksum; + String? thumbhash; + @Index(unique: false, replace: false, type: IndexType.hash) String? remoteId; @@ -171,6 +175,11 @@ class Asset { int? stackCount; + /// Aspect ratio of the asset + @ignore + double? get aspectRatio => + width == null || height == null ? 0 : width! / height!; + /// `true` if this [Asset] is present on the device @ignore bool get isLocal => localId != null; @@ -274,6 +283,7 @@ class Asset { a.exifInfo?.latitude != exifInfo?.latitude || a.exifInfo?.longitude != exifInfo?.longitude || // no local stack count or different count from remote + a.thumbhash != thumbhash || ((stackCount == null && a.stackCount != null) || (stackCount != null && a.stackCount != null && @@ -338,6 +348,7 @@ class Asset { isReadOnly: a.isReadOnly, isOffline: a.isOffline, exifInfo: a.exifInfo?.copyWith(id: id) ?? exifInfo, + thumbhash: a.thumbhash, ); } else { // add only missing values (and set isLocal to true) @@ -374,6 +385,7 @@ class Asset { ExifInfo? exifInfo, String? stackParentId, int? stackCount, + String? thumbhash, }) => Asset( id: id ?? this.id, @@ -398,6 +410,7 @@ class Asset { exifInfo: exifInfo ?? this.exifInfo, stackParentId: stackParentId ?? this.stackParentId, stackCount: stackCount ?? this.stackCount, + thumbhash: thumbhash ?? this.thumbhash, ); Future put(Isar db) async { diff --git a/mobile/lib/shared/models/asset.g.dart b/mobile/lib/shared/models/asset.g.dart index d845b5353a..5912f291b5 100644 --- a/mobile/lib/shared/models/asset.g.dart +++ b/mobile/lib/shared/models/asset.g.dart @@ -102,19 +102,24 @@ const AssetSchema = CollectionSchema( name: r'stackParentId', type: IsarType.string, ), - r'type': PropertySchema( + r'thumbhash': PropertySchema( id: 17, + name: r'thumbhash', + type: IsarType.string, + ), + r'type': PropertySchema( + id: 18, name: r'type', type: IsarType.byte, enumMap: _AssettypeEnumValueMap, ), r'updatedAt': PropertySchema( - id: 18, + id: 19, name: r'updatedAt', type: IsarType.dateTime, ), r'width': PropertySchema( - id: 19, + id: 20, name: r'width', type: IsarType.int, ) @@ -210,6 +215,12 @@ int _assetEstimateSize( bytesCount += 3 + value.length * 3; } } + { + final value = object.thumbhash; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } return bytesCount; } @@ -236,9 +247,10 @@ void _assetSerialize( writer.writeString(offsets[14], object.remoteId); writer.writeLong(offsets[15], object.stackCount); writer.writeString(offsets[16], object.stackParentId); - writer.writeByte(offsets[17], object.type.index); - writer.writeDateTime(offsets[18], object.updatedAt); - writer.writeInt(offsets[19], object.width); + writer.writeString(offsets[17], object.thumbhash); + writer.writeByte(offsets[18], object.type.index); + writer.writeDateTime(offsets[19], object.updatedAt); + writer.writeInt(offsets[20], object.width); } Asset _assetDeserialize( @@ -266,10 +278,11 @@ Asset _assetDeserialize( remoteId: reader.readStringOrNull(offsets[14]), stackCount: reader.readLongOrNull(offsets[15]), stackParentId: reader.readStringOrNull(offsets[16]), - type: _AssettypeValueEnumMap[reader.readByteOrNull(offsets[17])] ?? + thumbhash: reader.readStringOrNull(offsets[17]), + type: _AssettypeValueEnumMap[reader.readByteOrNull(offsets[18])] ?? AssetType.other, - updatedAt: reader.readDateTime(offsets[18]), - width: reader.readIntOrNull(offsets[19]), + updatedAt: reader.readDateTime(offsets[19]), + width: reader.readIntOrNull(offsets[20]), ); return object; } @@ -316,11 +329,13 @@ P _assetDeserializeProp

( case 16: return (reader.readStringOrNull(offset)) as P; case 17: + return (reader.readStringOrNull(offset)) as P; + case 18: return (_AssettypeValueEnumMap[reader.readByteOrNull(offset)] ?? AssetType.other) as P; - case 18: - return (reader.readDateTime(offset)) as P; case 19: + return (reader.readDateTime(offset)) as P; + case 20: return (reader.readIntOrNull(offset)) as P; default: throw IsarError('Unknown property with id $propertyId'); @@ -2078,6 +2093,152 @@ extension AssetQueryFilter on QueryBuilder { }); } + QueryBuilder thumbhashIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'thumbhash', + )); + }); + } + + QueryBuilder thumbhashIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'thumbhash', + )); + }); + } + + QueryBuilder thumbhashEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder thumbhashGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder thumbhashLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder thumbhashBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'thumbhash', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder thumbhashStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder thumbhashEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder thumbhashContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder thumbhashMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'thumbhash', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder thumbhashIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'thumbhash', + value: '', + )); + }); + } + + QueryBuilder thumbhashIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'thumbhash', + value: '', + )); + }); + } + QueryBuilder typeEqualTo( AssetType value) { return QueryBuilder.apply(this, (query) { @@ -2462,6 +2623,18 @@ extension AssetQuerySortBy on QueryBuilder { }); } + QueryBuilder sortByThumbhash() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'thumbhash', Sort.asc); + }); + } + + QueryBuilder sortByThumbhashDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'thumbhash', Sort.desc); + }); + } + QueryBuilder sortByType() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'type', Sort.asc); @@ -2716,6 +2889,18 @@ extension AssetQuerySortThenBy on QueryBuilder { }); } + QueryBuilder thenByThumbhash() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'thumbhash', Sort.asc); + }); + } + + QueryBuilder thenByThumbhashDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'thumbhash', Sort.desc); + }); + } + QueryBuilder thenByType() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'type', Sort.asc); @@ -2864,6 +3049,13 @@ extension AssetQueryWhereDistinct on QueryBuilder { }); } + QueryBuilder distinctByThumbhash( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'thumbhash', caseSensitive: caseSensitive); + }); + } + QueryBuilder distinctByType() { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'type'); @@ -2992,6 +3184,12 @@ extension AssetQueryProperty on QueryBuilder { }); } + QueryBuilder thumbhashProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'thumbhash'); + }); + } + QueryBuilder typeProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'type'); diff --git a/mobile/lib/shared/models/logger_message.model.dart b/mobile/lib/shared/models/logger_message.model.dart index cb1d45a580..f657257eab 100644 --- a/mobile/lib/shared/models/logger_message.model.dart +++ b/mobile/lib/shared/models/logger_message.model.dart @@ -9,6 +9,7 @@ part 'logger_message.model.g.dart'; class LoggerMessage { Id id = Isar.autoIncrement; String message; + String? details; @Enumerated(EnumType.ordinal) LogLevel level = LogLevel.INFO; DateTime createdAt; @@ -17,6 +18,7 @@ class LoggerMessage { LoggerMessage({ required this.message, + required this.details, required this.level, required this.createdAt, required this.context1, diff --git a/mobile/lib/shared/models/logger_message.model.g.dart b/mobile/lib/shared/models/logger_message.model.g.dart index a6b960eece..76c823704c 100644 --- a/mobile/lib/shared/models/logger_message.model.g.dart +++ b/mobile/lib/shared/models/logger_message.model.g.dart @@ -32,14 +32,19 @@ const LoggerMessageSchema = CollectionSchema( name: r'createdAt', type: IsarType.dateTime, ), - r'level': PropertySchema( + r'details': PropertySchema( id: 3, + name: r'details', + type: IsarType.string, + ), + r'level': PropertySchema( + id: 4, name: r'level', type: IsarType.byte, enumMap: _LoggerMessagelevelEnumValueMap, ), r'message': PropertySchema( - id: 4, + id: 5, name: r'message', type: IsarType.string, ) @@ -76,6 +81,12 @@ int _loggerMessageEstimateSize( bytesCount += 3 + value.length * 3; } } + { + final value = object.details; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } bytesCount += 3 + object.message.length * 3; return bytesCount; } @@ -89,8 +100,9 @@ void _loggerMessageSerialize( writer.writeString(offsets[0], object.context1); writer.writeString(offsets[1], object.context2); writer.writeDateTime(offsets[2], object.createdAt); - writer.writeByte(offsets[3], object.level.index); - writer.writeString(offsets[4], object.message); + writer.writeString(offsets[3], object.details); + writer.writeByte(offsets[4], object.level.index); + writer.writeString(offsets[5], object.message); } LoggerMessage _loggerMessageDeserialize( @@ -103,9 +115,10 @@ LoggerMessage _loggerMessageDeserialize( context1: reader.readStringOrNull(offsets[0]), context2: reader.readStringOrNull(offsets[1]), createdAt: reader.readDateTime(offsets[2]), - level: _LoggerMessagelevelValueEnumMap[reader.readByteOrNull(offsets[3])] ?? + details: reader.readStringOrNull(offsets[3]), + level: _LoggerMessagelevelValueEnumMap[reader.readByteOrNull(offsets[4])] ?? LogLevel.ALL, - message: reader.readString(offsets[4]), + message: reader.readString(offsets[5]), ); object.id = id; return object; @@ -125,9 +138,11 @@ P _loggerMessageDeserializeProp

( case 2: return (reader.readDateTime(offset)) as P; case 3: + return (reader.readStringOrNull(offset)) as P; + case 4: return (_LoggerMessagelevelValueEnumMap[reader.readByteOrNull(offset)] ?? LogLevel.ALL) as P; - case 4: + case 5: return (reader.readString(offset)) as P; default: throw IsarError('Unknown property with id $propertyId'); @@ -619,6 +634,160 @@ extension LoggerMessageQueryFilter }); } + QueryBuilder + detailsIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'details', + )); + }); + } + + QueryBuilder + detailsIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'details', + )); + }); + } + + QueryBuilder + detailsEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'details', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + detailsGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'details', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + detailsLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'details', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + detailsBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'details', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + detailsStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'details', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + detailsEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'details', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + detailsContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'details', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + detailsMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'details', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + detailsIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'details', + value: '', + )); + }); + } + + QueryBuilder + detailsIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'details', + value: '', + )); + }); + } + QueryBuilder idEqualTo( Id value) { return QueryBuilder.apply(this, (query) { @@ -913,6 +1082,18 @@ extension LoggerMessageQuerySortBy }); } + QueryBuilder sortByDetails() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'details', Sort.asc); + }); + } + + QueryBuilder sortByDetailsDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'details', Sort.desc); + }); + } + QueryBuilder sortByLevel() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'level', Sort.asc); @@ -979,6 +1160,18 @@ extension LoggerMessageQuerySortThenBy }); } + QueryBuilder thenByDetails() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'details', Sort.asc); + }); + } + + QueryBuilder thenByDetailsDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'details', Sort.desc); + }); + } + QueryBuilder thenById() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'id', Sort.asc); @@ -1038,6 +1231,13 @@ extension LoggerMessageQueryWhereDistinct }); } + QueryBuilder distinctByDetails( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'details', caseSensitive: caseSensitive); + }); + } + QueryBuilder distinctByLevel() { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'level'); @@ -1078,6 +1278,12 @@ extension LoggerMessageQueryProperty }); } + QueryBuilder detailsProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'details'); + }); + } + QueryBuilder levelProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'level'); diff --git a/mobile/lib/shared/providers/websocket.provider.dart b/mobile/lib/shared/providers/websocket.provider.dart index 6b12916d27..89f99dc6df 100644 --- a/mobile/lib/shared/providers/websocket.provider.dart +++ b/mobile/lib/shared/providers/websocket.provider.dart @@ -110,7 +110,8 @@ class WebsocketNotifier extends StateNotifier { final endpoint = Uri.parse(Store.get(StoreKey.serverEndpoint)); final headers = {"x-immich-user-token": accessToken}; if (endpoint.userInfo.isNotEmpty) { - headers["Authorization"] = "Basic ${base64.encode(utf8.encode(endpoint.userInfo))}"; + headers["Authorization"] = + "Basic ${base64.encode(utf8.encode(endpoint.userInfo))}"; } debugPrint("Attempting to connect to websocket"); @@ -161,6 +162,7 @@ class WebsocketNotifier extends StateNotifier { socket.on('on_asset_trash', _handleServerUpdates); socket.on('on_asset_restore', _handleServerUpdates); socket.on('on_asset_update', _handleServerUpdates); + socket.on('on_asset_stack_update', _handleServerUpdates); socket.on('on_asset_hidden', _handleOnAssetHidden); socket.on('on_new_release', _handleReleaseUpdates); } catch (e) { diff --git a/mobile/lib/shared/services/asset.service.dart b/mobile/lib/shared/services/asset.service.dart index 48f8c63524..3086ab9246 100644 --- a/mobile/lib/shared/services/asset.service.dart +++ b/mobile/lib/shared/services/asset.service.dart @@ -63,7 +63,7 @@ class AssetService { /// Returns `null` if the server state did not change, else list of assets Future?> _getRemoteAssets(User user) async { - const int chunkSize = 5000; + const int chunkSize = 10000; try { final DateTime now = DateTime.now().toUtc(); final List allAssets = []; @@ -90,7 +90,7 @@ class AssetService { return allAssets; } catch (error, stack) { log.severe( - 'Error while getting remote assets: ${error.toString()}', + 'Error while getting remote assets', error, stack, ); @@ -117,7 +117,7 @@ class AssetService { ); return true; } catch (error, stack) { - log.severe("Error deleteAssets ${error.toString()}", error, stack); + log.severe("Error while deleting assets", error, stack); } return false; } diff --git a/mobile/lib/shared/services/immich_logger.service.dart b/mobile/lib/shared/services/immich_logger.service.dart index b66177e570..967ab2d5f2 100644 --- a/mobile/lib/shared/services/immich_logger.service.dart +++ b/mobile/lib/shared/services/immich_logger.service.dart @@ -12,7 +12,7 @@ import 'package:share_plus/share_plus.dart'; /// [ImmichLogger] is a custom logger that is built on top of the [logging] package. /// The logs are written to the database and onto console, using `debugPrint` method. /// -/// The logs are deleted when exceeding the `maxLogEntries` (default 200) property +/// The logs are deleted when exceeding the `maxLogEntries` (default 500) property /// in the class. /// /// Logs can be shared by calling the `shareLogs` method, which will open a share dialog @@ -58,6 +58,7 @@ class ImmichLogger { debugPrint('[${record.level.name}] [${record.time}] ${record.message}'); final lm = LoggerMessage( message: record.message, + details: record.error?.toString(), level: record.level.toLogLevel(), createdAt: record.time, context1: record.loggerName, diff --git a/mobile/lib/shared/services/share.service.dart b/mobile/lib/shared/services/share.service.dart index 20ee40d588..be7c0c168d 100644 --- a/mobile/lib/shared/services/share.service.dart +++ b/mobile/lib/shared/services/share.service.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/response_extensions.dart'; import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/providers/api.provider.dart'; import 'package:logging/logging.dart'; @@ -27,7 +28,12 @@ class ShareService { final downloadedXFiles = []; for (var asset in assets) { - if (asset.isRemote) { + if (asset.isLocal) { + // Prefer local assets to share + File? f = await asset.local!.file; + downloadedXFiles.add(XFile(f!.path)); + } else if (asset.isRemote) { + // Download remote asset otherwise final tempDir = await getTemporaryDirectory(); final fileName = asset.fileName; final tempFile = await File('${tempDir.path}/$fileName').create(); @@ -36,16 +42,14 @@ class ShareService { if (res.statusCode != 200) { _log.severe( - "Asset download failed with status - ${res.statusCode} and response - ${res.body}", + "Asset download for ${asset.fileName} failed", + res.toLoggerString(), ); continue; } tempFile.writeAsBytesSync(res.bodyBytes); downloadedXFiles.add(XFile(tempFile.path)); - } else { - File? f = await asset.local!.file; - downloadedXFiles.add(XFile(f!.path)); } } @@ -66,7 +70,7 @@ class ShareService { ); return true; } catch (error) { - _log.severe("Share failed with error $error"); + _log.severe("Share failed", error); } return false; } diff --git a/mobile/lib/shared/services/sync.service.dart b/mobile/lib/shared/services/sync.service.dart index 8d4547340e..d92145235e 100644 --- a/mobile/lib/shared/services/sync.service.dart +++ b/mobile/lib/shared/services/sync.service.dart @@ -127,7 +127,7 @@ class SyncService { try { await _db.writeTxn(() => a.put(_db)); } on IsarError catch (e) { - _log.severe("Failed to put new asset into db: $e"); + _log.severe("Failed to put new asset into db", e); return false; } return true; @@ -160,7 +160,7 @@ class SyncService { } return false; } on IsarError catch (e) { - _log.severe("Failed to sync remote assets to db: $e"); + _log.severe("Failed to sync remote assets to db", e); } return null; } @@ -219,7 +219,7 @@ class SyncService { await _db.writeTxn(() => _db.assets.deleteAll(idsToDelete)); await upsertAssetsWithExif(toAdd + toUpdate); } on IsarError catch (e) { - _log.severe("Failed to sync remote assets to db: $e"); + _log.severe("Failed to sync remote assets to db", e); } await _updateUserAssetsETag(user, now); return true; @@ -351,7 +351,7 @@ class SyncService { }); _log.info("Synced changes of remote album ${album.name} to DB"); } on IsarError catch (e) { - _log.severe("Failed to sync remote album to database $e"); + _log.severe("Failed to sync remote album to database", e); } if (album.shared || dto.shared) { @@ -423,7 +423,7 @@ class SyncService { assert(ok); _log.info("Removed local album $album from DB"); } catch (e) { - _log.severe("Failed to remove local album $album from DB"); + _log.severe("Failed to remove local album $album from DB", e); } } @@ -473,9 +473,7 @@ class SyncService { }); _log.info("Upserted ${assets.length} assets into the DB"); } on IsarError catch (e) { - _log.severe( - "Failed to upsert ${assets.length} assets into the DB: ${e.toString()}", - ); + _log.severe("Failed to upsert ${assets.length} assets into the DB", e); // give details on the errors assets.sort(Asset.compareByOwnerChecksum); final inDb = await _db.assets.getAllByOwnerIdChecksum( diff --git a/mobile/lib/shared/services/user.service.dart b/mobile/lib/shared/services/user.service.dart index 4d398c3a88..ae65ed31db 100644 --- a/mobile/lib/shared/services/user.service.dart +++ b/mobile/lib/shared/services/user.service.dart @@ -42,7 +42,7 @@ class UserService { final dto = await _apiService.userApi.getAllUsers(isAll); return dto?.map(User.fromUserDto).toList(); } catch (e) { - _log.warning("Failed get all users:\n$e"); + _log.warning("Failed get all users", e); return null; } } @@ -65,7 +65,7 @@ class UserService { ), ); } catch (e) { - _log.warning("Failed to upload profile image:\n$e"); + _log.warning("Failed to upload profile image", e); return null; } } diff --git a/mobile/lib/shared/ui/delayed_loading_indicator.dart b/mobile/lib/shared/ui/delayed_loading_indicator.dart new file mode 100644 index 0000000000..e009b660c9 --- /dev/null +++ b/mobile/lib/shared/ui/delayed_loading_indicator.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; + +class DelayedLoadingIndicator extends StatelessWidget { + /// The delay to avoid showing the loading indicator + final Duration delay; + + /// Defaults to using the [ImmichLoadingIndicator] + final Widget? child; + + /// An optional fade in duration to animate the loading + final Duration? fadeInDuration; + + const DelayedLoadingIndicator({ + super.key, + this.delay = const Duration(seconds: 3), + this.child, + this.fadeInDuration, + }); + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: Future.delayed(delay), + builder: (context, snapshot) { + late Widget c; + if (snapshot.connectionState == ConnectionState.done) { + c = child ?? + const ImmichLoadingIndicator( + key: ValueKey('loading'), + ); + } else { + c = Container(key: const ValueKey('hiding')); + } + + return AnimatedSwitcher( + duration: fadeInDuration ?? Duration.zero, + child: c, + ); + }, + ); + } +} diff --git a/mobile/lib/shared/ui/fade_in_placeholder_image.dart b/mobile/lib/shared/ui/fade_in_placeholder_image.dart new file mode 100644 index 0000000000..e0620ea4f0 --- /dev/null +++ b/mobile/lib/shared/ui/fade_in_placeholder_image.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/shared/ui/transparent_image.dart'; + +class FadeInPlaceholderImage extends StatelessWidget { + final Widget placeholder; + final ImageProvider image; + final Duration duration; + final BoxFit fit; + + const FadeInPlaceholderImage({ + super.key, + required this.placeholder, + required this.image, + this.duration = const Duration(milliseconds: 100), + this.fit = BoxFit.cover, + }); + + @override + Widget build(BuildContext context) { + return SizedBox.expand( + child: Stack( + fit: StackFit.expand, + children: [ + placeholder, + FadeInImage( + fadeInDuration: duration, + image: image, + fit: fit, + placeholder: MemoryImage(kTransparentImage), + ), + ], + ), + ); + } +} diff --git a/mobile/lib/shared/ui/hooks/blurhash_hook.dart b/mobile/lib/shared/ui/hooks/blurhash_hook.dart new file mode 100644 index 0000000000..24b3c25e13 --- /dev/null +++ b/mobile/lib/shared/ui/hooks/blurhash_hook.dart @@ -0,0 +1,17 @@ +import 'dart:convert'; +import 'dart:typed_data'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:immich_mobile/shared/models/asset.dart'; +import 'package:thumbhash/thumbhash.dart' as thumbhash; + +ObjectRef useBlurHashRef(Asset? asset) { + if (asset?.thumbhash == null) { + return useRef(null); + } + + final rbga = thumbhash.thumbHashToRGBA( + base64Decode(asset!.thumbhash!), + ); + + return useRef(thumbhash.rgbaToBmp(rbga)); +} diff --git a/mobile/lib/shared/ui/immich_image.dart b/mobile/lib/shared/ui/immich_image.dart index 21418d5274..f06f1726a1 100644 --- a/mobile/lib/shared/ui/immich_image.dart +++ b/mobile/lib/shared/ui/immich_image.dart @@ -1,15 +1,12 @@ -import 'dart:math'; - import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/modules/asset_viewer/image_providers/immich_local_image_provider.dart'; import 'package:immich_mobile/modules/asset_viewer/image_providers/immich_remote_image_provider.dart'; +import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_placeholder.dart'; import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/models/store.dart'; import 'package:octo_image/octo_image.dart'; -import 'package:photo_manager/photo_manager.dart'; -import 'package:photo_manager_image_provider/photo_manager_image_provider.dart'; class ImmichImage extends StatelessWidget { const ImmichImage( @@ -17,40 +14,15 @@ class ImmichImage extends StatelessWidget { this.width, this.height, this.fit = BoxFit.cover, - this.useGrayBoxPlaceholder = false, - this.isThumbnail = false, - this.thumbnailSize = 250, + this.placeholder = const ThumbnailPlaceholder(), super.key, }); final Asset? asset; - final bool useGrayBoxPlaceholder; + final Widget? placeholder; final double? width; final double? height; final BoxFit fit; - final bool isThumbnail; - final int thumbnailSize; - - /// Factory constructor to use the thumbnail variant - factory ImmichImage.thumbnail( - Asset? asset, { - BoxFit fit = BoxFit.cover, - double? width, - double? height, - }) { - // Use the width and height to derive thumbnail size - final thumbnailSize = max(width ?? 250, height ?? 250).toInt(); - - return ImmichImage( - asset, - isThumbnail: true, - fit: fit, - width: width, - height: height, - useGrayBoxPlaceholder: true, - thumbnailSize: thumbnailSize, - ); - } // Helper function to return the image provider for the asset // either by using the asset ID or the asset itself @@ -62,8 +34,6 @@ class ImmichImage extends StatelessWidget { static ImageProvider imageProvider({ Asset? asset, String? assetId, - bool isThumbnail = false, - int thumbnailSize = 250, }) { if (asset == null && assetId == null) { throw Exception('Must supply either asset or assetId'); @@ -72,67 +42,53 @@ class ImmichImage extends StatelessWidget { if (asset == null) { return ImmichRemoteImageProvider( assetId: assetId!, - isThumbnail: isThumbnail, + isThumbnail: false, ); } - if (useLocal(asset) && isThumbnail) { - return AssetEntityImageProvider( - asset.local!, - isOriginal: false, - thumbnailSize: ThumbnailSize.square(thumbnailSize), - ); - } else if (useLocal(asset) && !isThumbnail) { + if (useLocal(asset)) { return ImmichLocalImageProvider( asset: asset, ); } else { return ImmichRemoteImageProvider( assetId: asset.remoteId!, - isThumbnail: isThumbnail, + isThumbnail: false, ); } } + // Whether to use the local asset image provider or a remote one static bool useLocal(Asset asset) => !asset.isRemote || asset.isLocal && !Store.get(StoreKey.preferRemoteImage, false); + @override Widget build(BuildContext context) { - if (asset == null) { return Container( - decoration: const BoxDecoration( - color: Colors.grey, - ), - child: SizedBox( - width: width, - height: height, - child: const Center( - child: Icon(Icons.no_photography), - ), + color: Colors.grey, + width: width, + height: height, + child: const Center( + child: Icon(Icons.no_photography), ), ); } return OctoImage( fadeInDuration: const Duration(milliseconds: 0), - fadeOutDuration: const Duration(milliseconds: 400), + fadeOutDuration: const Duration(milliseconds: 200), placeholderBuilder: (context) { - if (useGrayBoxPlaceholder) { + if (placeholder != null) { // Use the gray box placeholder - return const SizedBox.expand( - child: DecoratedBox( - decoration: BoxDecoration(color: Colors.grey), - ), - ); + return placeholder!; } // No placeholder return const SizedBox(); }, image: ImmichImage.imageProvider( asset: asset, - isThumbnail: isThumbnail, ), width: width, height: height, diff --git a/mobile/lib/shared/ui/immich_thumbnail.dart b/mobile/lib/shared/ui/immich_thumbnail.dart new file mode 100644 index 0000000000..77827348db --- /dev/null +++ b/mobile/lib/shared/ui/immich_thumbnail.dart @@ -0,0 +1,88 @@ +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:immich_mobile/modules/asset_viewer/image_providers/immich_local_thumbnail_provider.dart'; +import 'package:immich_mobile/modules/asset_viewer/image_providers/immich_remote_image_provider.dart'; +import 'package:immich_mobile/shared/models/asset.dart'; +import 'package:immich_mobile/shared/ui/hooks/blurhash_hook.dart'; +import 'package:immich_mobile/shared/ui/immich_image.dart'; +import 'package:immich_mobile/shared/ui/thumbhash_placeholder.dart'; +import 'package:octo_image/octo_image.dart'; + +class ImmichThumbnail extends HookWidget { + const ImmichThumbnail({ + this.asset, + this.width = 250, + this.height = 250, + this.fit = BoxFit.cover, + super.key, + }); + + final Asset? asset; + final double width; + final double height; + final BoxFit fit; + + /// Helper function to return the image provider for the asset thumbnail + /// either by using the asset ID or the asset itself + /// [asset] is the Asset to request, or else use [assetId] to get a remote + /// image provider + static ImageProvider imageProvider({ + Asset? asset, + String? assetId, + int thumbnailSize = 256, + }) { + if (asset == null && assetId == null) { + throw Exception('Must supply either asset or assetId'); + } + + if (asset == null) { + return ImmichRemoteImageProvider( + assetId: assetId!, + isThumbnail: true, + ); + } + + if (ImmichImage.useLocal(asset)) { + return ImmichLocalThumbnailProvider( + asset: asset, + height: thumbnailSize, + width: thumbnailSize, + ); + } else { + return ImmichRemoteImageProvider( + assetId: asset.remoteId!, + isThumbnail: true, + ); + } + } + + @override + Widget build(BuildContext context) { + Uint8List? blurhash = useBlurHashRef(asset).value; + if (asset == null) { + return Container( + color: Colors.grey, + width: width, + height: height, + child: const Center( + child: Icon(Icons.no_photography), + ), + ); + } + + return OctoImage.fromSet( + placeholderFadeInDuration: Duration.zero, + fadeInDuration: Duration.zero, + fadeOutDuration: const Duration(milliseconds: 100), + octoSet: blurHashOrPlaceholder(blurhash), + image: ImmichThumbnail.imageProvider( + asset: asset, + ), + width: width, + height: height, + fit: fit, + ); + } +} diff --git a/mobile/lib/shared/ui/thumbhash_placeholder.dart b/mobile/lib/shared/ui/thumbhash_placeholder.dart new file mode 100644 index 0000000000..0ec64d3760 --- /dev/null +++ b/mobile/lib/shared/ui/thumbhash_placeholder.dart @@ -0,0 +1,48 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_placeholder.dart'; +import 'package:immich_mobile/shared/ui/fade_in_placeholder_image.dart'; +import 'package:octo_image/octo_image.dart'; + +/// Simple set to show [OctoPlaceholder.circularProgressIndicator] as +/// placeholder and [OctoError.icon] as error. +OctoSet blurHashOrPlaceholder( + Uint8List? blurhash, { + BoxFit? fit, + Text? errorMessage, +}) { + return OctoSet( + placeholderBuilder: blurHashPlaceholderBuilder(blurhash, fit: fit), + errorBuilder: blurHashErrorBuilder(blurhash, fit: fit), + ); +} + +OctoPlaceholderBuilder blurHashPlaceholderBuilder( + Uint8List? blurhash, { + BoxFit? fit, +}) { + return (context) => blurhash == null + ? const ThumbnailPlaceholder() + : FadeInPlaceholderImage( + placeholder: const ThumbnailPlaceholder(), + image: MemoryImage(blurhash), + fit: fit ?? BoxFit.cover, + ); +} + +OctoErrorBuilder blurHashErrorBuilder( + Uint8List? blurhash, { + BoxFit? fit, + Text? message, + IconData? icon, + Color? iconColor, + double? iconSize, +}) { + return OctoError.placeholderWithErrorIcon( + blurHashPlaceholderBuilder(blurhash, fit: fit), + message: message, + icon: icon, + iconColor: iconColor, + iconSize: iconSize, + ); +} diff --git a/mobile/lib/shared/views/app_log_detail_page.dart b/mobile/lib/shared/views/app_log_detail_page.dart index 126f46c8ff..6b99d7f0af 100644 --- a/mobile/lib/shared/views/app_log_detail_page.dart +++ b/mobile/lib/shared/views/app_log_detail_page.dart @@ -15,7 +15,7 @@ class AppLogDetailPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { var isDarkTheme = context.isDarkTheme; - buildStackMessage(String stackTrace) { + buildTextWithCopyButton(String header, String text) { return Padding( padding: const EdgeInsets.all(8.0), child: Column( @@ -28,7 +28,7 @@ class AppLogDetailPage extends HookConsumerWidget { Padding( padding: const EdgeInsets.only(bottom: 8.0), child: Text( - "STACK TRACES", + header, style: TextStyle( fontSize: 12.0, color: context.primaryColor, @@ -38,8 +38,7 @@ class AppLogDetailPage extends HookConsumerWidget { ), IconButton( onPressed: () { - Clipboard.setData(ClipboardData(text: stackTrace)) - .then((_) { + Clipboard.setData(ClipboardData(text: text)).then((_) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( @@ -68,73 +67,7 @@ class AppLogDetailPage extends HookConsumerWidget { child: Padding( padding: const EdgeInsets.all(8.0), child: SelectableText( - stackTrace, - style: const TextStyle( - fontSize: 12.0, - fontWeight: FontWeight.bold, - fontFamily: "Inconsolata", - ), - ), - ), - ), - ], - ), - ); - } - - buildLogMessage(String message) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.only(bottom: 8.0), - child: Text( - "MESSAGE", - style: TextStyle( - fontSize: 12.0, - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), - ), - ), - IconButton( - onPressed: () { - Clipboard.setData(ClipboardData(text: message)).then((_) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - "Copied to clipboard", - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), - ), - ), - ); - }); - }, - icon: Icon( - Icons.copy, - size: 16.0, - color: context.primaryColor, - ), - ), - ], - ), - Container( - decoration: BoxDecoration( - color: isDarkTheme ? Colors.grey[900] : Colors.grey[200], - borderRadius: BorderRadius.circular(15.0), - ), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: SelectableText( - message, + text, style: const TextStyle( fontSize: 12.0, fontWeight: FontWeight.bold, @@ -194,11 +127,16 @@ class AppLogDetailPage extends HookConsumerWidget { body: SafeArea( child: ListView( children: [ - buildLogMessage(logMessage.message), + buildTextWithCopyButton("MESSAGE", logMessage.message), + if (logMessage.details != null) + buildTextWithCopyButton("DETAILS", logMessage.details.toString()), if (logMessage.context1 != null) buildLogContext1(logMessage.context1.toString()), if (logMessage.context2 != null) - buildStackMessage(logMessage.context2.toString()), + buildTextWithCopyButton( + "STACK TRACE", + logMessage.context2.toString(), + ), ], ), ), diff --git a/mobile/lib/shared/views/app_log_page.dart b/mobile/lib/shared/views/app_log_page.dart index a0c4553f98..993b25c7cf 100644 --- a/mobile/lib/shared/views/app_log_page.dart +++ b/mobile/lib/shared/views/app_log_page.dart @@ -69,9 +69,9 @@ class AppLogPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: Text( - "Logs - ${logMessages.value.length}", - style: const TextStyle( + title: const Text( + "Logs", + style: TextStyle( fontWeight: FontWeight.bold, fontSize: 16.0, ), @@ -135,29 +135,15 @@ class AppLogPage extends HookConsumerWidget { dense: true, tileColor: getTileColor(logMessage.level), minLeadingWidth: 10, - title: Text.rich( - TextSpan( - children: [ - TextSpan( - text: "#$index ", - style: TextStyle( - color: isDarkTheme ? Colors.white70 : Colors.grey[600], - fontSize: 14.0, - fontWeight: FontWeight.bold, - ), - ), - TextSpan( - text: truncateLogMessage(logMessage.message, 4), - style: const TextStyle( - fontSize: 14.0, - ), - ), - ], + title: Text( + truncateLogMessage(logMessage.message, 4), + style: const TextStyle( + fontSize: 14.0, + fontFamily: "Inconsolata", ), - style: const TextStyle(fontSize: 14.0, fontFamily: "Inconsolata"), ), subtitle: Text( - "[${logMessage.context1}] Logged on ${DateFormat("HH:mm:ss.SSS").format(logMessage.createdAt)}", + "at ${DateFormat("HH:mm:ss.SSS").format(logMessage.createdAt)} in ${logMessage.context1}", style: TextStyle( fontSize: 12.0, color: Colors.grey[600], diff --git a/mobile/lib/shared/views/immich_loading_overlay.dart b/mobile/lib/shared/views/immich_loading_overlay.dart index 85f0123ed9..c600d2a724 100644 --- a/mobile/lib/shared/views/immich_loading_overlay.dart +++ b/mobile/lib/shared/views/immich_loading_overlay.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; +import 'package:immich_mobile/shared/ui/delayed_loading_indicator.dart'; final _loadingEntry = OverlayEntry( builder: (context) => SizedBox.square( @@ -9,7 +9,12 @@ final _loadingEntry = OverlayEntry( child: DecoratedBox( decoration: BoxDecoration(color: context.colorScheme.surface.withAlpha(200)), - child: const Center(child: ImmichLoadingIndicator()), + child: const Center( + child: DelayedLoadingIndicator( + delay: Duration(seconds: 1), + fadeInDuration: Duration(milliseconds: 400), + ), + ), ), ), ); @@ -27,19 +32,19 @@ class _LoadingOverlay extends Hook> { class _LoadingOverlayState extends HookState, _LoadingOverlay> { - late final _isProcessing = ValueNotifier(false)..addListener(_listener); - OverlayEntry? overlayEntry; + late final _isLoading = ValueNotifier(false)..addListener(_listener); + OverlayEntry? _loadingOverlay; void _listener() { setState(() { WidgetsBinding.instance.addPostFrameCallback((_) { - if (_isProcessing.value) { - overlayEntry?.remove(); - overlayEntry = _loadingEntry; + if (_isLoading.value) { + _loadingOverlay?.remove(); + _loadingOverlay = _loadingEntry; Overlay.of(context).insert(_loadingEntry); } else { - overlayEntry?.remove(); - overlayEntry = null; + _loadingOverlay?.remove(); + _loadingOverlay = null; } }); }); @@ -47,17 +52,17 @@ class _LoadingOverlayState @override ValueNotifier build(BuildContext context) { - return _isProcessing; + return _isLoading; } @override void dispose() { - _isProcessing.dispose(); + _isLoading.dispose(); super.dispose(); } @override - Object? get debugValue => _isProcessing.value; + Object? get debugValue => _isLoading.value; @override String get debugLabel => 'useProcessingOverlay<>'; diff --git a/mobile/lib/shared/views/splash_screen.dart b/mobile/lib/shared/views/splash_screen.dart index 8dddb60aaa..3c0d65bde9 100644 --- a/mobile/lib/shared/views/splash_screen.dart +++ b/mobile/lib/shared/views/splash_screen.dart @@ -35,10 +35,10 @@ class SplashScreenPage extends HookConsumerWidget { deviceIsOffline = true; log.fine("Device seems to be offline upon launch"); } else { - log.severe(e); + log.severe("Failed to resolve endpoint", e); } } catch (e) { - log.severe(e); + log.severe("Failed to resolve endpoint", e); } try { @@ -53,7 +53,7 @@ class SplashScreenPage extends HookConsumerWidget { ref.read(authenticationProvider.notifier).logout(); log.severe( - 'Cannot set success login info: $error', + 'Cannot set success login info', error, stackTrace, ); diff --git a/mobile/openapi/.openapi-generator/FILES b/mobile/openapi/.openapi-generator/FILES index aeb3d49cb4..ea413b4870 100644 --- a/mobile/openapi/.openapi-generator/FILES +++ b/mobile/openapi/.openapi-generator/FILES @@ -90,6 +90,7 @@ doc/MapMarkerResponseDto.md doc/MapTheme.md doc/MemoryLaneResponseDto.md doc/MergePersonDto.md +doc/MetadataSearchDto.md doc/ModelType.md doc/OAuthApi.md doc/OAuthAuthorizeResponseDto.md @@ -107,6 +108,7 @@ doc/PersonResponseDto.md doc/PersonStatisticsResponseDto.md doc/PersonUpdateDto.md doc/PersonWithFacesResponseDto.md +doc/PlacesResponseDto.md doc/QueueStatusDto.md doc/ReactionLevel.md doc/ReactionType.md @@ -137,6 +139,7 @@ doc/SharedLinkResponseDto.md doc/SharedLinkType.md doc/SignUpDto.md doc/SmartInfoResponseDto.md +doc/SmartSearchDto.md doc/SystemConfigApi.md doc/SystemConfigDto.md doc/SystemConfigFFmpegDto.md @@ -180,6 +183,9 @@ doc/UserAvatarColor.md doc/UserDto.md doc/UserResponseDto.md doc/ValidateAccessTokenResponseDto.md +doc/ValidateLibraryDto.md +doc/ValidateLibraryImportPathResponseDto.md +doc/ValidateLibraryResponseDto.md doc/VideoCodec.md git_push.sh lib/api.dart @@ -288,6 +294,7 @@ lib/model/map_marker_response_dto.dart lib/model/map_theme.dart lib/model/memory_lane_response_dto.dart lib/model/merge_person_dto.dart +lib/model/metadata_search_dto.dart lib/model/model_type.dart lib/model/o_auth_authorize_response_dto.dart lib/model/o_auth_callback_dto.dart @@ -302,6 +309,7 @@ lib/model/person_response_dto.dart lib/model/person_statistics_response_dto.dart lib/model/person_update_dto.dart lib/model/person_with_faces_response_dto.dart +lib/model/places_response_dto.dart lib/model/queue_status_dto.dart lib/model/reaction_level.dart lib/model/reaction_type.dart @@ -329,6 +337,7 @@ lib/model/shared_link_response_dto.dart lib/model/shared_link_type.dart lib/model/sign_up_dto.dart lib/model/smart_info_response_dto.dart +lib/model/smart_search_dto.dart lib/model/system_config_dto.dart lib/model/system_config_f_fmpeg_dto.dart lib/model/system_config_job_dto.dart @@ -368,6 +377,9 @@ lib/model/user_avatar_color.dart lib/model/user_dto.dart lib/model/user_response_dto.dart lib/model/validate_access_token_response_dto.dart +lib/model/validate_library_dto.dart +lib/model/validate_library_import_path_response_dto.dart +lib/model/validate_library_response_dto.dart lib/model/video_codec.dart pubspec.yaml test/activity_api_test.dart @@ -457,6 +469,7 @@ test/map_marker_response_dto_test.dart test/map_theme_test.dart test/memory_lane_response_dto_test.dart test/merge_person_dto_test.dart +test/metadata_search_dto_test.dart test/model_type_test.dart test/o_auth_api_test.dart test/o_auth_authorize_response_dto_test.dart @@ -474,6 +487,7 @@ test/person_response_dto_test.dart test/person_statistics_response_dto_test.dart test/person_update_dto_test.dart test/person_with_faces_response_dto_test.dart +test/places_response_dto_test.dart test/queue_status_dto_test.dart test/reaction_level_test.dart test/reaction_type_test.dart @@ -504,6 +518,7 @@ test/shared_link_response_dto_test.dart test/shared_link_type_test.dart test/sign_up_dto_test.dart test/smart_info_response_dto_test.dart +test/smart_search_dto_test.dart test/system_config_api_test.dart test/system_config_dto_test.dart test/system_config_f_fmpeg_dto_test.dart @@ -547,4 +562,7 @@ test/user_avatar_color_test.dart test/user_dto_test.dart test/user_response_dto_test.dart test/validate_access_token_response_dto_test.dart +test/validate_library_dto_test.dart +test/validate_library_import_path_response_dto_test.dart +test/validate_library_response_dto_test.dart test/video_codec_test.dart diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 12d20ea13d..b8548c79e6 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -3,7 +3,7 @@ Immich API This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: -- API version: 1.94.1 +- API version: 1.97.0 - Build package: org.openapitools.codegen.languages.DartClientCodegen ## Requirements @@ -135,12 +135,13 @@ Class | Method | HTTP request | Description *JobApi* | [**sendJobCommand**](doc//JobApi.md#sendjobcommand) | **PUT** /jobs/{id} | *LibraryApi* | [**createLibrary**](doc//LibraryApi.md#createlibrary) | **POST** /library | *LibraryApi* | [**deleteLibrary**](doc//LibraryApi.md#deletelibrary) | **DELETE** /library/{id} | -*LibraryApi* | [**getLibraries**](doc//LibraryApi.md#getlibraries) | **GET** /library | -*LibraryApi* | [**getLibraryInfo**](doc//LibraryApi.md#getlibraryinfo) | **GET** /library/{id} | +*LibraryApi* | [**getAllLibraries**](doc//LibraryApi.md#getalllibraries) | **GET** /library | +*LibraryApi* | [**getLibrary**](doc//LibraryApi.md#getlibrary) | **GET** /library/{id} | *LibraryApi* | [**getLibraryStatistics**](doc//LibraryApi.md#getlibrarystatistics) | **GET** /library/{id}/statistics | *LibraryApi* | [**removeOfflineFiles**](doc//LibraryApi.md#removeofflinefiles) | **POST** /library/{id}/removeOffline | *LibraryApi* | [**scanLibrary**](doc//LibraryApi.md#scanlibrary) | **POST** /library/{id}/scan | *LibraryApi* | [**updateLibrary**](doc//LibraryApi.md#updatelibrary) | **PUT** /library/{id} | +*LibraryApi* | [**validate**](doc//LibraryApi.md#validate) | **POST** /library/{id}/validate | *OAuthApi* | [**finishOAuth**](doc//OAuthApi.md#finishoauth) | **POST** /oauth/callback | *OAuthApi* | [**linkOAuthAccount**](doc//OAuthApi.md#linkoauthaccount) | **POST** /oauth/link | *OAuthApi* | [**redirectOAuthToMobile**](doc//OAuthApi.md#redirectoauthtomobile) | **GET** /oauth/mobile-redirect | @@ -163,9 +164,10 @@ Class | Method | HTTP request | Description *SearchApi* | [**getExploreData**](doc//SearchApi.md#getexploredata) | **GET** /search/explore | *SearchApi* | [**getSearchSuggestions**](doc//SearchApi.md#getsearchsuggestions) | **GET** /search/suggestions | *SearchApi* | [**search**](doc//SearchApi.md#search) | **GET** /search | -*SearchApi* | [**searchMetadata**](doc//SearchApi.md#searchmetadata) | **GET** /search/metadata | +*SearchApi* | [**searchMetadata**](doc//SearchApi.md#searchmetadata) | **POST** /search/metadata | *SearchApi* | [**searchPerson**](doc//SearchApi.md#searchperson) | **GET** /search/person | -*SearchApi* | [**searchSmart**](doc//SearchApi.md#searchsmart) | **GET** /search/smart | +*SearchApi* | [**searchPlaces**](doc//SearchApi.md#searchplaces) | **GET** /search/places | +*SearchApi* | [**searchSmart**](doc//SearchApi.md#searchsmart) | **POST** /search/smart | *ServerInfoApi* | [**getServerConfig**](doc//ServerInfoApi.md#getserverconfig) | **GET** /server-info/config | *ServerInfoApi* | [**getServerFeatures**](doc//ServerInfoApi.md#getserverfeatures) | **GET** /server-info/features | *ServerInfoApi* | [**getServerInfo**](doc//ServerInfoApi.md#getserverinfo) | **GET** /server-info | @@ -290,6 +292,7 @@ Class | Method | HTTP request | Description - [MapTheme](doc//MapTheme.md) - [MemoryLaneResponseDto](doc//MemoryLaneResponseDto.md) - [MergePersonDto](doc//MergePersonDto.md) + - [MetadataSearchDto](doc//MetadataSearchDto.md) - [ModelType](doc//ModelType.md) - [OAuthAuthorizeResponseDto](doc//OAuthAuthorizeResponseDto.md) - [OAuthCallbackDto](doc//OAuthCallbackDto.md) @@ -304,6 +307,7 @@ Class | Method | HTTP request | Description - [PersonStatisticsResponseDto](doc//PersonStatisticsResponseDto.md) - [PersonUpdateDto](doc//PersonUpdateDto.md) - [PersonWithFacesResponseDto](doc//PersonWithFacesResponseDto.md) + - [PlacesResponseDto](doc//PlacesResponseDto.md) - [QueueStatusDto](doc//QueueStatusDto.md) - [ReactionLevel](doc//ReactionLevel.md) - [ReactionType](doc//ReactionType.md) @@ -331,6 +335,7 @@ Class | Method | HTTP request | Description - [SharedLinkType](doc//SharedLinkType.md) - [SignUpDto](doc//SignUpDto.md) - [SmartInfoResponseDto](doc//SmartInfoResponseDto.md) + - [SmartSearchDto](doc//SmartSearchDto.md) - [SystemConfigDto](doc//SystemConfigDto.md) - [SystemConfigFFmpegDto](doc//SystemConfigFFmpegDto.md) - [SystemConfigJobDto](doc//SystemConfigJobDto.md) @@ -370,6 +375,9 @@ Class | Method | HTTP request | Description - [UserDto](doc//UserDto.md) - [UserResponseDto](doc//UserResponseDto.md) - [ValidateAccessTokenResponseDto](doc//ValidateAccessTokenResponseDto.md) + - [ValidateLibraryDto](doc//ValidateLibraryDto.md) + - [ValidateLibraryImportPathResponseDto](doc//ValidateLibraryImportPathResponseDto.md) + - [ValidateLibraryResponseDto](doc//ValidateLibraryResponseDto.md) - [VideoCodec](doc//VideoCodec.md) diff --git a/mobile/openapi/doc/AssetApi.md b/mobile/openapi/doc/AssetApi.md index 623d88b388..c65e6a605f 100644 --- a/mobile/openapi/doc/AssetApi.md +++ b/mobile/openapi/doc/AssetApi.md @@ -659,7 +659,7 @@ This endpoint does not need any parameter. [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) # **getMapMarkers** -> List getMapMarkers(fileCreatedAfter, fileCreatedBefore, isArchived, isFavorite) +> List getMapMarkers(fileCreatedAfter, fileCreatedBefore, isArchived, isFavorite, withPartners) @@ -686,9 +686,10 @@ final fileCreatedAfter = 2013-10-20T19:20:30+01:00; // DateTime | final fileCreatedBefore = 2013-10-20T19:20:30+01:00; // DateTime | final isArchived = true; // bool | final isFavorite = true; // bool | +final withPartners = true; // bool | try { - final result = api_instance.getMapMarkers(fileCreatedAfter, fileCreatedBefore, isArchived, isFavorite); + final result = api_instance.getMapMarkers(fileCreatedAfter, fileCreatedBefore, isArchived, isFavorite, withPartners); print(result); } catch (e) { print('Exception when calling AssetApi->getMapMarkers: $e\n'); @@ -703,6 +704,7 @@ Name | Type | Description | Notes **fileCreatedBefore** | **DateTime**| | [optional] **isArchived** | **bool**| | [optional] **isFavorite** | **bool**| | [optional] + **withPartners** | **bool**| | [optional] ### Return type @@ -1034,7 +1036,7 @@ void (empty response body) [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) # **searchAssets** -> List searchAssets(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked) +> List searchAssets(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isNotInAlbum, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, personIds, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked) @@ -1071,6 +1073,7 @@ final isEncoded = true; // bool | final isExternal = true; // bool | final isFavorite = true; // bool | final isMotion = true; // bool | +final isNotInAlbum = true; // bool | final isOffline = true; // bool | final isReadOnly = true; // bool | final isVisible = true; // bool | @@ -1082,6 +1085,7 @@ final order = ; // AssetOrder | final originalFileName = originalFileName_example; // String | final originalPath = originalPath_example; // String | final page = 8.14; // num | +final personIds = []; // List | final resizePath = resizePath_example; // String | final size = 8.14; // num | final state = state_example; // String | @@ -1100,7 +1104,7 @@ final withPeople = true; // bool | final withStacked = true; // bool | try { - final result = api_instance.searchAssets(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked); + final result = api_instance.searchAssets(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isNotInAlbum, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, personIds, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked); print(result); } catch (e) { print('Exception when calling AssetApi->searchAssets: $e\n'); @@ -1125,6 +1129,7 @@ Name | Type | Description | Notes **isExternal** | **bool**| | [optional] **isFavorite** | **bool**| | [optional] **isMotion** | **bool**| | [optional] + **isNotInAlbum** | **bool**| | [optional] **isOffline** | **bool**| | [optional] **isReadOnly** | **bool**| | [optional] **isVisible** | **bool**| | [optional] @@ -1136,6 +1141,7 @@ Name | Type | Description | Notes **originalFileName** | **String**| | [optional] **originalPath** | **String**| | [optional] **page** | **num**| | [optional] + **personIds** | [**List**](String.md)| | [optional] [default to const []] **resizePath** | **String**| | [optional] **size** | **num**| | [optional] **state** | **String**| | [optional] @@ -1147,7 +1153,7 @@ Name | Type | Description | Notes **updatedAfter** | **DateTime**| | [optional] **updatedBefore** | **DateTime**| | [optional] **webpPath** | **String**| | [optional] - **withArchived** | **bool**| | [optional] + **withArchived** | **bool**| | [optional] [default to false] **withDeleted** | **bool**| | [optional] **withExif** | **bool**| | [optional] **withPeople** | **bool**| | [optional] @@ -1395,7 +1401,7 @@ void (empty response body) [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) # **uploadFile** -> AssetFileUploadResponseDto uploadFile(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key, duration, isArchived, isExternal, isFavorite, isOffline, isReadOnly, isVisible, libraryId, livePhotoData, sidecarData) +> AssetFileUploadResponseDto uploadFile(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key, duration, isArchived, isFavorite, isOffline, isReadOnly, isVisible, libraryId, livePhotoData, sidecarData) @@ -1426,7 +1432,6 @@ final fileModifiedAt = 2013-10-20T19:20:30+01:00; // DateTime | final key = key_example; // String | final duration = duration_example; // String | final isArchived = true; // bool | -final isExternal = true; // bool | final isFavorite = true; // bool | final isOffline = true; // bool | final isReadOnly = true; // bool | @@ -1436,7 +1441,7 @@ final livePhotoData = BINARY_DATA_HERE; // MultipartFile | final sidecarData = BINARY_DATA_HERE; // MultipartFile | try { - final result = api_instance.uploadFile(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key, duration, isArchived, isExternal, isFavorite, isOffline, isReadOnly, isVisible, libraryId, livePhotoData, sidecarData); + final result = api_instance.uploadFile(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key, duration, isArchived, isFavorite, isOffline, isReadOnly, isVisible, libraryId, livePhotoData, sidecarData); print(result); } catch (e) { print('Exception when calling AssetApi->uploadFile: $e\n'); @@ -1455,7 +1460,6 @@ Name | Type | Description | Notes **key** | **String**| | [optional] **duration** | **String**| | [optional] **isArchived** | **bool**| | [optional] - **isExternal** | **bool**| | [optional] **isFavorite** | **bool**| | [optional] **isOffline** | **bool**| | [optional] **isReadOnly** | **bool**| | [optional] diff --git a/mobile/openapi/doc/CreateLibraryDto.md b/mobile/openapi/doc/CreateLibraryDto.md index 9e4859cee9..e0caf1c8a5 100644 --- a/mobile/openapi/doc/CreateLibraryDto.md +++ b/mobile/openapi/doc/CreateLibraryDto.md @@ -13,6 +13,7 @@ Name | Type | Description | Notes **isVisible** | **bool** | | [optional] **isWatched** | **bool** | | [optional] **name** | **String** | | [optional] +**ownerId** | **String** | | [optional] **type** | [**LibraryType**](LibraryType.md) | | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/mobile/openapi/doc/CreateUserDto.md b/mobile/openapi/doc/CreateUserDto.md index 716571752c..0dcc8eca1f 100644 --- a/mobile/openapi/doc/CreateUserDto.md +++ b/mobile/openapi/doc/CreateUserDto.md @@ -9,7 +9,6 @@ import 'package:openapi/api.dart'; Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **email** | **String** | | -**externalPath** | **String** | | [optional] **memoriesEnabled** | **bool** | | [optional] **name** | **String** | | **password** | **String** | | diff --git a/mobile/openapi/doc/LibraryApi.md b/mobile/openapi/doc/LibraryApi.md index 5ecd46503a..8a204788bb 100644 --- a/mobile/openapi/doc/LibraryApi.md +++ b/mobile/openapi/doc/LibraryApi.md @@ -11,12 +11,13 @@ Method | HTTP request | Description ------------- | ------------- | ------------- [**createLibrary**](LibraryApi.md#createlibrary) | **POST** /library | [**deleteLibrary**](LibraryApi.md#deletelibrary) | **DELETE** /library/{id} | -[**getLibraries**](LibraryApi.md#getlibraries) | **GET** /library | -[**getLibraryInfo**](LibraryApi.md#getlibraryinfo) | **GET** /library/{id} | +[**getAllLibraries**](LibraryApi.md#getalllibraries) | **GET** /library | +[**getLibrary**](LibraryApi.md#getlibrary) | **GET** /library/{id} | [**getLibraryStatistics**](LibraryApi.md#getlibrarystatistics) | **GET** /library/{id}/statistics | [**removeOfflineFiles**](LibraryApi.md#removeofflinefiles) | **POST** /library/{id}/removeOffline | [**scanLibrary**](LibraryApi.md#scanlibrary) | **POST** /library/{id}/scan | [**updateLibrary**](LibraryApi.md#updatelibrary) | **PUT** /library/{id} | +[**validate**](LibraryApi.md#validate) | **POST** /library/{id}/validate | # **createLibrary** @@ -128,8 +129,8 @@ void (empty response body) [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) -# **getLibraries** -> List getLibraries() +# **getAllLibraries** +> List getAllLibraries(type) @@ -152,17 +153,21 @@ import 'package:openapi/api.dart'; //defaultApiClient.getAuthentication('bearer').setAccessToken(yourTokenGeneratorFunction); final api_instance = LibraryApi(); +final type = ; // LibraryType | try { - final result = api_instance.getLibraries(); + final result = api_instance.getAllLibraries(type); print(result); } catch (e) { - print('Exception when calling LibraryApi->getLibraries: $e\n'); + print('Exception when calling LibraryApi->getAllLibraries: $e\n'); } ``` ### Parameters -This endpoint does not need any parameter. + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **type** | [**LibraryType**](.md)| | [optional] ### Return type @@ -179,8 +184,8 @@ This endpoint does not need any parameter. [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) -# **getLibraryInfo** -> LibraryResponseDto getLibraryInfo(id) +# **getLibrary** +> LibraryResponseDto getLibrary(id) @@ -206,10 +211,10 @@ final api_instance = LibraryApi(); final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | try { - final result = api_instance.getLibraryInfo(id); + final result = api_instance.getLibrary(id); print(result); } catch (e) { - print('Exception when calling LibraryApi->getLibraryInfo: $e\n'); + print('Exception when calling LibraryApi->getLibrary: $e\n'); } ``` @@ -456,3 +461,60 @@ Name | Type | Description | Notes [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) +# **validate** +> ValidateLibraryResponseDto validate(id, validateLibraryDto) + + + +### Example +```dart +import 'package:openapi/api.dart'; +// TODO Configure API key authorization: cookie +//defaultApiClient.getAuthentication('cookie').apiKey = 'YOUR_API_KEY'; +// uncomment below to setup prefix (e.g. Bearer) for API key, if needed +//defaultApiClient.getAuthentication('cookie').apiKeyPrefix = 'Bearer'; +// TODO Configure API key authorization: api_key +//defaultApiClient.getAuthentication('api_key').apiKey = 'YOUR_API_KEY'; +// uncomment below to setup prefix (e.g. Bearer) for API key, if needed +//defaultApiClient.getAuthentication('api_key').apiKeyPrefix = 'Bearer'; +// TODO Configure HTTP Bearer authorization: bearer +// Case 1. Use String Token +//defaultApiClient.getAuthentication('bearer').setAccessToken('YOUR_ACCESS_TOKEN'); +// Case 2. Use Function which generate token. +// String yourTokenGeneratorFunction() { ... } +//defaultApiClient.getAuthentication('bearer').setAccessToken(yourTokenGeneratorFunction); + +final api_instance = LibraryApi(); +final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | +final validateLibraryDto = ValidateLibraryDto(); // ValidateLibraryDto | + +try { + final result = api_instance.validate(id, validateLibraryDto); + print(result); +} catch (e) { + print('Exception when calling LibraryApi->validate: $e\n'); +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **id** | **String**| | + **validateLibraryDto** | [**ValidateLibraryDto**](ValidateLibraryDto.md)| | + +### Return type + +[**ValidateLibraryResponseDto**](ValidateLibraryResponseDto.md) + +### Authorization + +[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer) + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/mobile/openapi/doc/MetadataSearchDto.md b/mobile/openapi/doc/MetadataSearchDto.md new file mode 100644 index 0000000000..d1d098fb0e --- /dev/null +++ b/mobile/openapi/doc/MetadataSearchDto.md @@ -0,0 +1,57 @@ +# openapi.model.MetadataSearchDto + +## Load the model package +```dart +import 'package:openapi/api.dart'; +``` + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**checksum** | **String** | | [optional] +**city** | **String** | | [optional] +**country** | **String** | | [optional] +**createdAfter** | [**DateTime**](DateTime.md) | | [optional] +**createdBefore** | [**DateTime**](DateTime.md) | | [optional] +**deviceAssetId** | **String** | | [optional] +**deviceId** | **String** | | [optional] +**encodedVideoPath** | **String** | | [optional] +**id** | **String** | | [optional] +**isArchived** | **bool** | | [optional] +**isEncoded** | **bool** | | [optional] +**isExternal** | **bool** | | [optional] +**isFavorite** | **bool** | | [optional] +**isMotion** | **bool** | | [optional] +**isNotInAlbum** | **bool** | | [optional] +**isOffline** | **bool** | | [optional] +**isReadOnly** | **bool** | | [optional] +**isVisible** | **bool** | | [optional] +**lensModel** | **String** | | [optional] +**libraryId** | **String** | | [optional] +**make** | **String** | | [optional] +**model** | **String** | | [optional] +**order** | [**AssetOrder**](AssetOrder.md) | | [optional] +**originalFileName** | **String** | | [optional] +**originalPath** | **String** | | [optional] +**page** | **num** | | [optional] +**personIds** | **List** | | [optional] [default to const []] +**resizePath** | **String** | | [optional] +**size** | **num** | | [optional] +**state** | **String** | | [optional] +**takenAfter** | [**DateTime**](DateTime.md) | | [optional] +**takenBefore** | [**DateTime**](DateTime.md) | | [optional] +**trashedAfter** | [**DateTime**](DateTime.md) | | [optional] +**trashedBefore** | [**DateTime**](DateTime.md) | | [optional] +**type** | [**AssetTypeEnum**](AssetTypeEnum.md) | | [optional] +**updatedAfter** | [**DateTime**](DateTime.md) | | [optional] +**updatedBefore** | [**DateTime**](DateTime.md) | | [optional] +**webpPath** | **String** | | [optional] +**withArchived** | **bool** | | [optional] [default to false] +**withDeleted** | **bool** | | [optional] +**withExif** | **bool** | | [optional] +**withPeople** | **bool** | | [optional] +**withStacked** | **bool** | | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/mobile/openapi/doc/PartnerResponseDto.md b/mobile/openapi/doc/PartnerResponseDto.md index 46d16e6b28..ce45b32594 100644 --- a/mobile/openapi/doc/PartnerResponseDto.md +++ b/mobile/openapi/doc/PartnerResponseDto.md @@ -12,7 +12,6 @@ Name | Type | Description | Notes **createdAt** | [**DateTime**](DateTime.md) | | **deletedAt** | [**DateTime**](DateTime.md) | | **email** | **String** | | -**externalPath** | **String** | | **id** | **String** | | **inTimeline** | **bool** | | [optional] **isAdmin** | **bool** | | diff --git a/mobile/openapi/doc/PeopleResponseDto.md b/mobile/openapi/doc/PeopleResponseDto.md index 2f87f19993..78f9b2207c 100644 --- a/mobile/openapi/doc/PeopleResponseDto.md +++ b/mobile/openapi/doc/PeopleResponseDto.md @@ -8,6 +8,7 @@ import 'package:openapi/api.dart'; ## Properties Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- +**hidden** | **int** | | **people** | [**List**](PersonResponseDto.md) | | [default to const []] **total** | **int** | | diff --git a/mobile/openapi/doc/PlacesResponseDto.md b/mobile/openapi/doc/PlacesResponseDto.md new file mode 100644 index 0000000000..a4bf36493c --- /dev/null +++ b/mobile/openapi/doc/PlacesResponseDto.md @@ -0,0 +1,19 @@ +# openapi.model.PlacesResponseDto + +## Load the model package +```dart +import 'package:openapi/api.dart'; +``` + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**admin1name** | **String** | | [optional] +**admin2name** | **String** | | [optional] +**latitude** | **num** | | +**longitude** | **num** | | +**name** | **String** | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/mobile/openapi/doc/SearchApi.md b/mobile/openapi/doc/SearchApi.md index 8c924428f6..f63488222b 100644 --- a/mobile/openapi/doc/SearchApi.md +++ b/mobile/openapi/doc/SearchApi.md @@ -12,9 +12,10 @@ Method | HTTP request | Description [**getExploreData**](SearchApi.md#getexploredata) | **GET** /search/explore | [**getSearchSuggestions**](SearchApi.md#getsearchsuggestions) | **GET** /search/suggestions | [**search**](SearchApi.md#search) | **GET** /search | -[**searchMetadata**](SearchApi.md#searchmetadata) | **GET** /search/metadata | +[**searchMetadata**](SearchApi.md#searchmetadata) | **POST** /search/metadata | [**searchPerson**](SearchApi.md#searchperson) | **GET** /search/person | -[**searchSmart**](SearchApi.md#searchsmart) | **GET** /search/smart | +[**searchPlaces**](SearchApi.md#searchplaces) | **GET** /search/places | +[**searchSmart**](SearchApi.md#searchsmart) | **POST** /search/smart | # **getExploreData** @@ -205,7 +206,7 @@ Name | Type | Description | Notes [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) # **searchMetadata** -> SearchResponseDto searchMetadata(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked) +> SearchResponseDto searchMetadata(metadataSearchDto) @@ -228,50 +229,10 @@ import 'package:openapi/api.dart'; //defaultApiClient.getAuthentication('bearer').setAccessToken(yourTokenGeneratorFunction); final api_instance = SearchApi(); -final checksum = checksum_example; // String | -final city = city_example; // String | -final country = country_example; // String | -final createdAfter = 2013-10-20T19:20:30+01:00; // DateTime | -final createdBefore = 2013-10-20T19:20:30+01:00; // DateTime | -final deviceAssetId = deviceAssetId_example; // String | -final deviceId = deviceId_example; // String | -final encodedVideoPath = encodedVideoPath_example; // String | -final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | -final isArchived = true; // bool | -final isEncoded = true; // bool | -final isExternal = true; // bool | -final isFavorite = true; // bool | -final isMotion = true; // bool | -final isOffline = true; // bool | -final isReadOnly = true; // bool | -final isVisible = true; // bool | -final lensModel = lensModel_example; // String | -final libraryId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | -final make = make_example; // String | -final model = model_example; // String | -final order = ; // AssetOrder | -final originalFileName = originalFileName_example; // String | -final originalPath = originalPath_example; // String | -final page = 8.14; // num | -final resizePath = resizePath_example; // String | -final size = 8.14; // num | -final state = state_example; // String | -final takenAfter = 2013-10-20T19:20:30+01:00; // DateTime | -final takenBefore = 2013-10-20T19:20:30+01:00; // DateTime | -final trashedAfter = 2013-10-20T19:20:30+01:00; // DateTime | -final trashedBefore = 2013-10-20T19:20:30+01:00; // DateTime | -final type = ; // AssetTypeEnum | -final updatedAfter = 2013-10-20T19:20:30+01:00; // DateTime | -final updatedBefore = 2013-10-20T19:20:30+01:00; // DateTime | -final webpPath = webpPath_example; // String | -final withArchived = true; // bool | -final withDeleted = true; // bool | -final withExif = true; // bool | -final withPeople = true; // bool | -final withStacked = true; // bool | +final metadataSearchDto = MetadataSearchDto(); // MetadataSearchDto | try { - final result = api_instance.searchMetadata(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked); + final result = api_instance.searchMetadata(metadataSearchDto); print(result); } catch (e) { print('Exception when calling SearchApi->searchMetadata: $e\n'); @@ -282,47 +243,7 @@ try { Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- - **checksum** | **String**| | [optional] - **city** | **String**| | [optional] - **country** | **String**| | [optional] - **createdAfter** | **DateTime**| | [optional] - **createdBefore** | **DateTime**| | [optional] - **deviceAssetId** | **String**| | [optional] - **deviceId** | **String**| | [optional] - **encodedVideoPath** | **String**| | [optional] - **id** | **String**| | [optional] - **isArchived** | **bool**| | [optional] - **isEncoded** | **bool**| | [optional] - **isExternal** | **bool**| | [optional] - **isFavorite** | **bool**| | [optional] - **isMotion** | **bool**| | [optional] - **isOffline** | **bool**| | [optional] - **isReadOnly** | **bool**| | [optional] - **isVisible** | **bool**| | [optional] - **lensModel** | **String**| | [optional] - **libraryId** | **String**| | [optional] - **make** | **String**| | [optional] - **model** | **String**| | [optional] - **order** | [**AssetOrder**](.md)| | [optional] - **originalFileName** | **String**| | [optional] - **originalPath** | **String**| | [optional] - **page** | **num**| | [optional] - **resizePath** | **String**| | [optional] - **size** | **num**| | [optional] - **state** | **String**| | [optional] - **takenAfter** | **DateTime**| | [optional] - **takenBefore** | **DateTime**| | [optional] - **trashedAfter** | **DateTime**| | [optional] - **trashedBefore** | **DateTime**| | [optional] - **type** | [**AssetTypeEnum**](.md)| | [optional] - **updatedAfter** | **DateTime**| | [optional] - **updatedBefore** | **DateTime**| | [optional] - **webpPath** | **String**| | [optional] - **withArchived** | **bool**| | [optional] - **withDeleted** | **bool**| | [optional] - **withExif** | **bool**| | [optional] - **withPeople** | **bool**| | [optional] - **withStacked** | **bool**| | [optional] + **metadataSearchDto** | [**MetadataSearchDto**](MetadataSearchDto.md)| | ### Return type @@ -334,7 +255,7 @@ Name | Type | Description | Notes ### HTTP request headers - - **Content-Type**: Not defined + - **Content-Type**: application/json - **Accept**: application/json [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) @@ -396,8 +317,8 @@ Name | Type | Description | Notes [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) -# **searchSmart** -> SearchResponseDto searchSmart(query, city, country, createdAfter, createdBefore, deviceId, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, page, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, withArchived, withDeleted, withExif) +# **searchPlaces** +> List searchPlaces(name) @@ -420,43 +341,13 @@ import 'package:openapi/api.dart'; //defaultApiClient.getAuthentication('bearer').setAccessToken(yourTokenGeneratorFunction); final api_instance = SearchApi(); -final query = query_example; // String | -final city = city_example; // String | -final country = country_example; // String | -final createdAfter = 2013-10-20T19:20:30+01:00; // DateTime | -final createdBefore = 2013-10-20T19:20:30+01:00; // DateTime | -final deviceId = deviceId_example; // String | -final isArchived = true; // bool | -final isEncoded = true; // bool | -final isExternal = true; // bool | -final isFavorite = true; // bool | -final isMotion = true; // bool | -final isOffline = true; // bool | -final isReadOnly = true; // bool | -final isVisible = true; // bool | -final lensModel = lensModel_example; // String | -final libraryId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | -final make = make_example; // String | -final model = model_example; // String | -final page = 8.14; // num | -final size = 8.14; // num | -final state = state_example; // String | -final takenAfter = 2013-10-20T19:20:30+01:00; // DateTime | -final takenBefore = 2013-10-20T19:20:30+01:00; // DateTime | -final trashedAfter = 2013-10-20T19:20:30+01:00; // DateTime | -final trashedBefore = 2013-10-20T19:20:30+01:00; // DateTime | -final type = ; // AssetTypeEnum | -final updatedAfter = 2013-10-20T19:20:30+01:00; // DateTime | -final updatedBefore = 2013-10-20T19:20:30+01:00; // DateTime | -final withArchived = true; // bool | -final withDeleted = true; // bool | -final withExif = true; // bool | +final name = name_example; // String | try { - final result = api_instance.searchSmart(query, city, country, createdAfter, createdBefore, deviceId, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, page, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, withArchived, withDeleted, withExif); + final result = api_instance.searchPlaces(name); print(result); } catch (e) { - print('Exception when calling SearchApi->searchSmart: $e\n'); + print('Exception when calling SearchApi->searchPlaces: $e\n'); } ``` @@ -464,41 +355,11 @@ try { Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- - **query** | **String**| | - **city** | **String**| | [optional] - **country** | **String**| | [optional] - **createdAfter** | **DateTime**| | [optional] - **createdBefore** | **DateTime**| | [optional] - **deviceId** | **String**| | [optional] - **isArchived** | **bool**| | [optional] - **isEncoded** | **bool**| | [optional] - **isExternal** | **bool**| | [optional] - **isFavorite** | **bool**| | [optional] - **isMotion** | **bool**| | [optional] - **isOffline** | **bool**| | [optional] - **isReadOnly** | **bool**| | [optional] - **isVisible** | **bool**| | [optional] - **lensModel** | **String**| | [optional] - **libraryId** | **String**| | [optional] - **make** | **String**| | [optional] - **model** | **String**| | [optional] - **page** | **num**| | [optional] - **size** | **num**| | [optional] - **state** | **String**| | [optional] - **takenAfter** | **DateTime**| | [optional] - **takenBefore** | **DateTime**| | [optional] - **trashedAfter** | **DateTime**| | [optional] - **trashedBefore** | **DateTime**| | [optional] - **type** | [**AssetTypeEnum**](.md)| | [optional] - **updatedAfter** | **DateTime**| | [optional] - **updatedBefore** | **DateTime**| | [optional] - **withArchived** | **bool**| | [optional] - **withDeleted** | **bool**| | [optional] - **withExif** | **bool**| | [optional] + **name** | **String**| | ### Return type -[**SearchResponseDto**](SearchResponseDto.md) +[**List**](PlacesResponseDto.md) ### Authorization @@ -511,3 +372,58 @@ Name | Type | Description | Notes [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) +# **searchSmart** +> SearchResponseDto searchSmart(smartSearchDto) + + + +### Example +```dart +import 'package:openapi/api.dart'; +// TODO Configure API key authorization: cookie +//defaultApiClient.getAuthentication('cookie').apiKey = 'YOUR_API_KEY'; +// uncomment below to setup prefix (e.g. Bearer) for API key, if needed +//defaultApiClient.getAuthentication('cookie').apiKeyPrefix = 'Bearer'; +// TODO Configure API key authorization: api_key +//defaultApiClient.getAuthentication('api_key').apiKey = 'YOUR_API_KEY'; +// uncomment below to setup prefix (e.g. Bearer) for API key, if needed +//defaultApiClient.getAuthentication('api_key').apiKeyPrefix = 'Bearer'; +// TODO Configure HTTP Bearer authorization: bearer +// Case 1. Use String Token +//defaultApiClient.getAuthentication('bearer').setAccessToken('YOUR_ACCESS_TOKEN'); +// Case 2. Use Function which generate token. +// String yourTokenGeneratorFunction() { ... } +//defaultApiClient.getAuthentication('bearer').setAccessToken(yourTokenGeneratorFunction); + +final api_instance = SearchApi(); +final smartSearchDto = SmartSearchDto(); // SmartSearchDto | + +try { + final result = api_instance.searchSmart(smartSearchDto); + print(result); +} catch (e) { + print('Exception when calling SearchApi->searchSmart: $e\n'); +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **smartSearchDto** | [**SmartSearchDto**](SmartSearchDto.md)| | + +### Return type + +[**SearchResponseDto**](SearchResponseDto.md) + +### Authorization + +[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer) + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/mobile/openapi/doc/SmartSearchDto.md b/mobile/openapi/doc/SmartSearchDto.md new file mode 100644 index 0000000000..d5f4c40256 --- /dev/null +++ b/mobile/openapi/doc/SmartSearchDto.md @@ -0,0 +1,47 @@ +# openapi.model.SmartSearchDto + +## Load the model package +```dart +import 'package:openapi/api.dart'; +``` + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**city** | **String** | | [optional] +**country** | **String** | | [optional] +**createdAfter** | [**DateTime**](DateTime.md) | | [optional] +**createdBefore** | [**DateTime**](DateTime.md) | | [optional] +**deviceId** | **String** | | [optional] +**isArchived** | **bool** | | [optional] +**isEncoded** | **bool** | | [optional] +**isExternal** | **bool** | | [optional] +**isFavorite** | **bool** | | [optional] +**isMotion** | **bool** | | [optional] +**isNotInAlbum** | **bool** | | [optional] +**isOffline** | **bool** | | [optional] +**isReadOnly** | **bool** | | [optional] +**isVisible** | **bool** | | [optional] +**lensModel** | **String** | | [optional] +**libraryId** | **String** | | [optional] +**make** | **String** | | [optional] +**model** | **String** | | [optional] +**page** | **num** | | [optional] +**personIds** | **List** | | [optional] [default to const []] +**query** | **String** | | +**size** | **num** | | [optional] +**state** | **String** | | [optional] +**takenAfter** | [**DateTime**](DateTime.md) | | [optional] +**takenBefore** | [**DateTime**](DateTime.md) | | [optional] +**trashedAfter** | [**DateTime**](DateTime.md) | | [optional] +**trashedBefore** | [**DateTime**](DateTime.md) | | [optional] +**type** | [**AssetTypeEnum**](AssetTypeEnum.md) | | [optional] +**updatedAfter** | [**DateTime**](DateTime.md) | | [optional] +**updatedBefore** | [**DateTime**](DateTime.md) | | [optional] +**withArchived** | **bool** | | [optional] [default to false] +**withDeleted** | **bool** | | [optional] +**withExif** | **bool** | | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/mobile/openapi/doc/SystemConfigLibraryWatchDto.md b/mobile/openapi/doc/SystemConfigLibraryWatchDto.md index baa0be6b08..43b975e21a 100644 --- a/mobile/openapi/doc/SystemConfigLibraryWatchDto.md +++ b/mobile/openapi/doc/SystemConfigLibraryWatchDto.md @@ -9,8 +9,6 @@ import 'package:openapi/api.dart'; Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **enabled** | **bool** | | -**interval** | **int** | | -**usePolling** | **bool** | | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/mobile/openapi/doc/SystemConfigOAuthDto.md b/mobile/openapi/doc/SystemConfigOAuthDto.md index c02dae9b73..43694f6fc2 100644 --- a/mobile/openapi/doc/SystemConfigOAuthDto.md +++ b/mobile/openapi/doc/SystemConfigOAuthDto.md @@ -13,6 +13,7 @@ Name | Type | Description | Notes **buttonText** | **String** | | **clientId** | **String** | | **clientSecret** | **String** | | +**defaultStorageQuota** | **num** | | **enabled** | **bool** | | **issuerUrl** | **String** | | **mobileOverrideEnabled** | **bool** | | @@ -20,6 +21,7 @@ Name | Type | Description | Notes **scope** | **String** | | **signingAlgorithm** | **String** | | **storageLabelClaim** | **String** | | +**storageQuotaClaim** | **String** | | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/mobile/openapi/doc/UpdateUserDto.md b/mobile/openapi/doc/UpdateUserDto.md index 8c0572d1d2..ef4ebf869b 100644 --- a/mobile/openapi/doc/UpdateUserDto.md +++ b/mobile/openapi/doc/UpdateUserDto.md @@ -10,7 +10,6 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **avatarColor** | [**UserAvatarColor**](UserAvatarColor.md) | | [optional] **email** | **String** | | [optional] -**externalPath** | **String** | | [optional] **id** | **String** | | **isAdmin** | **bool** | | [optional] **memoriesEnabled** | **bool** | | [optional] diff --git a/mobile/openapi/doc/UserResponseDto.md b/mobile/openapi/doc/UserResponseDto.md index 4ea44bb0cb..700a5b849e 100644 --- a/mobile/openapi/doc/UserResponseDto.md +++ b/mobile/openapi/doc/UserResponseDto.md @@ -12,7 +12,6 @@ Name | Type | Description | Notes **createdAt** | [**DateTime**](DateTime.md) | | **deletedAt** | [**DateTime**](DateTime.md) | | **email** | **String** | | -**externalPath** | **String** | | **id** | **String** | | **isAdmin** | **bool** | | **memoriesEnabled** | **bool** | | [optional] diff --git a/mobile/openapi/doc/ValidateLibraryDto.md b/mobile/openapi/doc/ValidateLibraryDto.md new file mode 100644 index 0000000000..13c74a6a66 --- /dev/null +++ b/mobile/openapi/doc/ValidateLibraryDto.md @@ -0,0 +1,16 @@ +# openapi.model.ValidateLibraryDto + +## Load the model package +```dart +import 'package:openapi/api.dart'; +``` + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**exclusionPatterns** | **List** | | [optional] [default to const []] +**importPaths** | **List** | | [optional] [default to const []] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/mobile/openapi/doc/ValidateLibraryImportPathResponseDto.md b/mobile/openapi/doc/ValidateLibraryImportPathResponseDto.md new file mode 100644 index 0000000000..4601d8d2f2 --- /dev/null +++ b/mobile/openapi/doc/ValidateLibraryImportPathResponseDto.md @@ -0,0 +1,17 @@ +# openapi.model.ValidateLibraryImportPathResponseDto + +## Load the model package +```dart +import 'package:openapi/api.dart'; +``` + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**importPath** | **String** | | +**isValid** | **bool** | | [optional] [default to false] +**message** | **String** | | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/mobile/openapi/doc/ValidateLibraryResponseDto.md b/mobile/openapi/doc/ValidateLibraryResponseDto.md new file mode 100644 index 0000000000..7b22fdfe90 --- /dev/null +++ b/mobile/openapi/doc/ValidateLibraryResponseDto.md @@ -0,0 +1,15 @@ +# openapi.model.ValidateLibraryResponseDto + +## Load the model package +```dart +import 'package:openapi/api.dart'; +``` + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**importPaths** | [**List**](ValidateLibraryImportPathResponseDto.md) | | [optional] [default to const []] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index f2903e3516..56bd907e0a 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -127,6 +127,7 @@ part 'model/map_marker_response_dto.dart'; part 'model/map_theme.dart'; part 'model/memory_lane_response_dto.dart'; part 'model/merge_person_dto.dart'; +part 'model/metadata_search_dto.dart'; part 'model/model_type.dart'; part 'model/o_auth_authorize_response_dto.dart'; part 'model/o_auth_callback_dto.dart'; @@ -141,6 +142,7 @@ part 'model/person_response_dto.dart'; part 'model/person_statistics_response_dto.dart'; part 'model/person_update_dto.dart'; part 'model/person_with_faces_response_dto.dart'; +part 'model/places_response_dto.dart'; part 'model/queue_status_dto.dart'; part 'model/reaction_level.dart'; part 'model/reaction_type.dart'; @@ -168,6 +170,7 @@ part 'model/shared_link_response_dto.dart'; part 'model/shared_link_type.dart'; part 'model/sign_up_dto.dart'; part 'model/smart_info_response_dto.dart'; +part 'model/smart_search_dto.dart'; part 'model/system_config_dto.dart'; part 'model/system_config_f_fmpeg_dto.dart'; part 'model/system_config_job_dto.dart'; @@ -207,6 +210,9 @@ part 'model/user_avatar_color.dart'; part 'model/user_dto.dart'; part 'model/user_response_dto.dart'; part 'model/validate_access_token_response_dto.dart'; +part 'model/validate_library_dto.dart'; +part 'model/validate_library_import_path_response_dto.dart'; +part 'model/validate_library_response_dto.dart'; part 'model/video_codec.dart'; diff --git a/mobile/openapi/lib/api/asset_api.dart b/mobile/openapi/lib/api/asset_api.dart index 4dec34d407..786129b457 100644 --- a/mobile/openapi/lib/api/asset_api.dart +++ b/mobile/openapi/lib/api/asset_api.dart @@ -652,7 +652,9 @@ class AssetApi { /// * [bool] isArchived: /// /// * [bool] isFavorite: - Future getMapMarkersWithHttpInfo({ DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? isArchived, bool? isFavorite, }) async { + /// + /// * [bool] withPartners: + Future getMapMarkersWithHttpInfo({ DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? isArchived, bool? isFavorite, bool? withPartners, }) async { // ignore: prefer_const_declarations final path = r'/asset/map-marker'; @@ -675,6 +677,9 @@ class AssetApi { if (isFavorite != null) { queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); } + if (withPartners != null) { + queryParams.addAll(_queryParams('', 'withPartners', withPartners)); + } const contentTypes = []; @@ -699,8 +704,10 @@ class AssetApi { /// * [bool] isArchived: /// /// * [bool] isFavorite: - Future?> getMapMarkers({ DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? isArchived, bool? isFavorite, }) async { - final response = await getMapMarkersWithHttpInfo( fileCreatedAfter: fileCreatedAfter, fileCreatedBefore: fileCreatedBefore, isArchived: isArchived, isFavorite: isFavorite, ); + /// + /// * [bool] withPartners: + Future?> getMapMarkers({ DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? isArchived, bool? isFavorite, bool? withPartners, }) async { + final response = await getMapMarkersWithHttpInfo( fileCreatedAfter: fileCreatedAfter, fileCreatedBefore: fileCreatedBefore, isArchived: isArchived, isFavorite: isFavorite, withPartners: withPartners, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -1133,6 +1140,8 @@ class AssetApi { /// /// * [bool] isMotion: /// + /// * [bool] isNotInAlbum: + /// /// * [bool] isOffline: /// /// * [bool] isReadOnly: @@ -1155,6 +1164,8 @@ class AssetApi { /// /// * [num] page: /// + /// * [List] personIds: + /// /// * [String] resizePath: /// /// * [num] size: @@ -1186,7 +1197,7 @@ class AssetApi { /// * [bool] withPeople: /// /// * [bool] withStacked: - Future searchAssetsWithHttpInfo({ String? checksum, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceAssetId, String? deviceId, String? encodedVideoPath, String? id, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, AssetOrder? order, String? originalFileName, String? originalPath, num? page, String? resizePath, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, String? webpPath, bool? withArchived, bool? withDeleted, bool? withExif, bool? withPeople, bool? withStacked, }) async { + Future searchAssetsWithHttpInfo({ String? checksum, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceAssetId, String? deviceId, String? encodedVideoPath, String? id, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, AssetOrder? order, String? originalFileName, String? originalPath, num? page, List? personIds, String? resizePath, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, String? webpPath, bool? withArchived, bool? withDeleted, bool? withExif, bool? withPeople, bool? withStacked, }) async { // ignore: prefer_const_declarations final path = r'/assets'; @@ -1239,6 +1250,9 @@ class AssetApi { if (isMotion != null) { queryParams.addAll(_queryParams('', 'isMotion', isMotion)); } + if (isNotInAlbum != null) { + queryParams.addAll(_queryParams('', 'isNotInAlbum', isNotInAlbum)); + } if (isOffline != null) { queryParams.addAll(_queryParams('', 'isOffline', isOffline)); } @@ -1272,6 +1286,9 @@ class AssetApi { if (page != null) { queryParams.addAll(_queryParams('', 'page', page)); } + if (personIds != null) { + queryParams.addAll(_queryParams('multi', 'personIds', personIds)); + } if (resizePath != null) { queryParams.addAll(_queryParams('', 'resizePath', resizePath)); } @@ -1365,6 +1382,8 @@ class AssetApi { /// /// * [bool] isMotion: /// + /// * [bool] isNotInAlbum: + /// /// * [bool] isOffline: /// /// * [bool] isReadOnly: @@ -1387,6 +1406,8 @@ class AssetApi { /// /// * [num] page: /// + /// * [List] personIds: + /// /// * [String] resizePath: /// /// * [num] size: @@ -1418,8 +1439,8 @@ class AssetApi { /// * [bool] withPeople: /// /// * [bool] withStacked: - Future?> searchAssets({ String? checksum, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceAssetId, String? deviceId, String? encodedVideoPath, String? id, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, AssetOrder? order, String? originalFileName, String? originalPath, num? page, String? resizePath, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, String? webpPath, bool? withArchived, bool? withDeleted, bool? withExif, bool? withPeople, bool? withStacked, }) async { - final response = await searchAssetsWithHttpInfo( checksum: checksum, city: city, country: country, createdAfter: createdAfter, createdBefore: createdBefore, deviceAssetId: deviceAssetId, deviceId: deviceId, encodedVideoPath: encodedVideoPath, id: id, isArchived: isArchived, isEncoded: isEncoded, isExternal: isExternal, isFavorite: isFavorite, isMotion: isMotion, isOffline: isOffline, isReadOnly: isReadOnly, isVisible: isVisible, lensModel: lensModel, libraryId: libraryId, make: make, model: model, order: order, originalFileName: originalFileName, originalPath: originalPath, page: page, resizePath: resizePath, size: size, state: state, takenAfter: takenAfter, takenBefore: takenBefore, trashedAfter: trashedAfter, trashedBefore: trashedBefore, type: type, updatedAfter: updatedAfter, updatedBefore: updatedBefore, webpPath: webpPath, withArchived: withArchived, withDeleted: withDeleted, withExif: withExif, withPeople: withPeople, withStacked: withStacked, ); + Future?> searchAssets({ String? checksum, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceAssetId, String? deviceId, String? encodedVideoPath, String? id, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, AssetOrder? order, String? originalFileName, String? originalPath, num? page, List? personIds, String? resizePath, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, String? webpPath, bool? withArchived, bool? withDeleted, bool? withExif, bool? withPeople, bool? withStacked, }) async { + final response = await searchAssetsWithHttpInfo( checksum: checksum, city: city, country: country, createdAfter: createdAfter, createdBefore: createdBefore, deviceAssetId: deviceAssetId, deviceId: deviceId, encodedVideoPath: encodedVideoPath, id: id, isArchived: isArchived, isEncoded: isEncoded, isExternal: isExternal, isFavorite: isFavorite, isMotion: isMotion, isNotInAlbum: isNotInAlbum, isOffline: isOffline, isReadOnly: isReadOnly, isVisible: isVisible, lensModel: lensModel, libraryId: libraryId, make: make, model: model, order: order, originalFileName: originalFileName, originalPath: originalPath, page: page, personIds: personIds, resizePath: resizePath, size: size, state: state, takenAfter: takenAfter, takenBefore: takenBefore, trashedAfter: trashedAfter, trashedBefore: trashedBefore, type: type, updatedAfter: updatedAfter, updatedBefore: updatedBefore, webpPath: webpPath, withArchived: withArchived, withDeleted: withDeleted, withExif: withExif, withPeople: withPeople, withStacked: withStacked, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -1655,8 +1676,6 @@ class AssetApi { /// /// * [bool] isArchived: /// - /// * [bool] isExternal: - /// /// * [bool] isFavorite: /// /// * [bool] isOffline: @@ -1670,7 +1689,7 @@ class AssetApi { /// * [MultipartFile] livePhotoData: /// /// * [MultipartFile] sidecarData: - Future uploadFileWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, bool? isArchived, bool? isExternal, bool? isFavorite, bool? isOffline, bool? isReadOnly, bool? isVisible, String? libraryId, MultipartFile? livePhotoData, MultipartFile? sidecarData, }) async { + Future uploadFileWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, bool? isArchived, bool? isFavorite, bool? isOffline, bool? isReadOnly, bool? isVisible, String? libraryId, MultipartFile? livePhotoData, MultipartFile? sidecarData, }) async { // ignore: prefer_const_declarations final path = r'/asset/upload'; @@ -1718,10 +1737,6 @@ class AssetApi { hasFields = true; mp.fields[r'isArchived'] = parameterToString(isArchived); } - if (isExternal != null) { - hasFields = true; - mp.fields[r'isExternal'] = parameterToString(isExternal); - } if (isFavorite != null) { hasFields = true; mp.fields[r'isFavorite'] = parameterToString(isFavorite); @@ -1785,8 +1800,6 @@ class AssetApi { /// /// * [bool] isArchived: /// - /// * [bool] isExternal: - /// /// * [bool] isFavorite: /// /// * [bool] isOffline: @@ -1800,8 +1813,8 @@ class AssetApi { /// * [MultipartFile] livePhotoData: /// /// * [MultipartFile] sidecarData: - Future uploadFile(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, bool? isArchived, bool? isExternal, bool? isFavorite, bool? isOffline, bool? isReadOnly, bool? isVisible, String? libraryId, MultipartFile? livePhotoData, MultipartFile? sidecarData, }) async { - final response = await uploadFileWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, duration: duration, isArchived: isArchived, isExternal: isExternal, isFavorite: isFavorite, isOffline: isOffline, isReadOnly: isReadOnly, isVisible: isVisible, libraryId: libraryId, livePhotoData: livePhotoData, sidecarData: sidecarData, ); + Future uploadFile(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, bool? isArchived, bool? isFavorite, bool? isOffline, bool? isReadOnly, bool? isVisible, String? libraryId, MultipartFile? livePhotoData, MultipartFile? sidecarData, }) async { + final response = await uploadFileWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, duration: duration, isArchived: isArchived, isFavorite: isFavorite, isOffline: isOffline, isReadOnly: isReadOnly, isVisible: isVisible, libraryId: libraryId, livePhotoData: livePhotoData, sidecarData: sidecarData, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/library_api.dart b/mobile/openapi/lib/api/library_api.dart index 23de593eac..befd0aeeff 100644 --- a/mobile/openapi/lib/api/library_api.dart +++ b/mobile/openapi/lib/api/library_api.dart @@ -104,7 +104,10 @@ class LibraryApi { } /// Performs an HTTP 'GET /library' operation and returns the [Response]. - Future getLibrariesWithHttpInfo() async { + /// Parameters: + /// + /// * [LibraryType] type: + Future getAllLibrariesWithHttpInfo({ LibraryType? type, }) async { // ignore: prefer_const_declarations final path = r'/library'; @@ -115,6 +118,10 @@ class LibraryApi { final headerParams = {}; final formParams = {}; + if (type != null) { + queryParams.addAll(_queryParams('', 'type', type)); + } + const contentTypes = []; @@ -129,8 +136,11 @@ class LibraryApi { ); } - Future?> getLibraries() async { - final response = await getLibrariesWithHttpInfo(); + /// Parameters: + /// + /// * [LibraryType] type: + Future?> getAllLibraries({ LibraryType? type, }) async { + final response = await getAllLibrariesWithHttpInfo( type: type, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -151,7 +161,7 @@ class LibraryApi { /// Parameters: /// /// * [String] id (required): - Future getLibraryInfoWithHttpInfo(String id,) async { + Future getLibraryWithHttpInfo(String id,) async { // ignore: prefer_const_declarations final path = r'/library/{id}' .replaceAll('{id}', id); @@ -180,8 +190,8 @@ class LibraryApi { /// Parameters: /// /// * [String] id (required): - Future getLibraryInfo(String id,) async { - final response = await getLibraryInfoWithHttpInfo(id,); + Future getLibrary(String id,) async { + final response = await getLibraryWithHttpInfo(id,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -378,4 +388,56 @@ class LibraryApi { } return null; } + + /// Performs an HTTP 'POST /library/{id}/validate' operation and returns the [Response]. + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [ValidateLibraryDto] validateLibraryDto (required): + Future validateWithHttpInfo(String id, ValidateLibraryDto validateLibraryDto,) async { + // ignore: prefer_const_declarations + final path = r'/library/{id}/validate' + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody = validateLibraryDto; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = ['application/json']; + + + return apiClient.invokeAPI( + path, + 'POST', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [ValidateLibraryDto] validateLibraryDto (required): + Future validate(String id, ValidateLibraryDto validateLibraryDto,) async { + final response = await validateWithHttpInfo(id, validateLibraryDto,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'ValidateLibraryResponseDto',) as ValidateLibraryResponseDto; + + } + return null; + } } diff --git a/mobile/openapi/lib/api/search_api.dart b/mobile/openapi/lib/api/search_api.dart index 16d2b3003e..3a0bc56bb6 100644 --- a/mobile/openapi/lib/api/search_api.dart +++ b/mobile/openapi/lib/api/search_api.dart @@ -254,231 +254,27 @@ class SearchApi { return null; } - /// Performs an HTTP 'GET /search/metadata' operation and returns the [Response]. + /// Performs an HTTP 'POST /search/metadata' operation and returns the [Response]. /// Parameters: /// - /// * [String] checksum: - /// - /// * [String] city: - /// - /// * [String] country: - /// - /// * [DateTime] createdAfter: - /// - /// * [DateTime] createdBefore: - /// - /// * [String] deviceAssetId: - /// - /// * [String] deviceId: - /// - /// * [String] encodedVideoPath: - /// - /// * [String] id: - /// - /// * [bool] isArchived: - /// - /// * [bool] isEncoded: - /// - /// * [bool] isExternal: - /// - /// * [bool] isFavorite: - /// - /// * [bool] isMotion: - /// - /// * [bool] isOffline: - /// - /// * [bool] isReadOnly: - /// - /// * [bool] isVisible: - /// - /// * [String] lensModel: - /// - /// * [String] libraryId: - /// - /// * [String] make: - /// - /// * [String] model: - /// - /// * [AssetOrder] order: - /// - /// * [String] originalFileName: - /// - /// * [String] originalPath: - /// - /// * [num] page: - /// - /// * [String] resizePath: - /// - /// * [num] size: - /// - /// * [String] state: - /// - /// * [DateTime] takenAfter: - /// - /// * [DateTime] takenBefore: - /// - /// * [DateTime] trashedAfter: - /// - /// * [DateTime] trashedBefore: - /// - /// * [AssetTypeEnum] type: - /// - /// * [DateTime] updatedAfter: - /// - /// * [DateTime] updatedBefore: - /// - /// * [String] webpPath: - /// - /// * [bool] withArchived: - /// - /// * [bool] withDeleted: - /// - /// * [bool] withExif: - /// - /// * [bool] withPeople: - /// - /// * [bool] withStacked: - Future searchMetadataWithHttpInfo({ String? checksum, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceAssetId, String? deviceId, String? encodedVideoPath, String? id, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, AssetOrder? order, String? originalFileName, String? originalPath, num? page, String? resizePath, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, String? webpPath, bool? withArchived, bool? withDeleted, bool? withExif, bool? withPeople, bool? withStacked, }) async { + /// * [MetadataSearchDto] metadataSearchDto (required): + Future searchMetadataWithHttpInfo(MetadataSearchDto metadataSearchDto,) async { // ignore: prefer_const_declarations final path = r'/search/metadata'; // ignore: prefer_final_locals - Object? postBody; + Object? postBody = metadataSearchDto; final queryParams = []; final headerParams = {}; final formParams = {}; - if (checksum != null) { - queryParams.addAll(_queryParams('', 'checksum', checksum)); - } - if (city != null) { - queryParams.addAll(_queryParams('', 'city', city)); - } - if (country != null) { - queryParams.addAll(_queryParams('', 'country', country)); - } - if (createdAfter != null) { - queryParams.addAll(_queryParams('', 'createdAfter', createdAfter)); - } - if (createdBefore != null) { - queryParams.addAll(_queryParams('', 'createdBefore', createdBefore)); - } - if (deviceAssetId != null) { - queryParams.addAll(_queryParams('', 'deviceAssetId', deviceAssetId)); - } - if (deviceId != null) { - queryParams.addAll(_queryParams('', 'deviceId', deviceId)); - } - if (encodedVideoPath != null) { - queryParams.addAll(_queryParams('', 'encodedVideoPath', encodedVideoPath)); - } - if (id != null) { - queryParams.addAll(_queryParams('', 'id', id)); - } - if (isArchived != null) { - queryParams.addAll(_queryParams('', 'isArchived', isArchived)); - } - if (isEncoded != null) { - queryParams.addAll(_queryParams('', 'isEncoded', isEncoded)); - } - if (isExternal != null) { - queryParams.addAll(_queryParams('', 'isExternal', isExternal)); - } - if (isFavorite != null) { - queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); - } - if (isMotion != null) { - queryParams.addAll(_queryParams('', 'isMotion', isMotion)); - } - if (isOffline != null) { - queryParams.addAll(_queryParams('', 'isOffline', isOffline)); - } - if (isReadOnly != null) { - queryParams.addAll(_queryParams('', 'isReadOnly', isReadOnly)); - } - if (isVisible != null) { - queryParams.addAll(_queryParams('', 'isVisible', isVisible)); - } - if (lensModel != null) { - queryParams.addAll(_queryParams('', 'lensModel', lensModel)); - } - if (libraryId != null) { - queryParams.addAll(_queryParams('', 'libraryId', libraryId)); - } - if (make != null) { - queryParams.addAll(_queryParams('', 'make', make)); - } - if (model != null) { - queryParams.addAll(_queryParams('', 'model', model)); - } - if (order != null) { - queryParams.addAll(_queryParams('', 'order', order)); - } - if (originalFileName != null) { - queryParams.addAll(_queryParams('', 'originalFileName', originalFileName)); - } - if (originalPath != null) { - queryParams.addAll(_queryParams('', 'originalPath', originalPath)); - } - if (page != null) { - queryParams.addAll(_queryParams('', 'page', page)); - } - if (resizePath != null) { - queryParams.addAll(_queryParams('', 'resizePath', resizePath)); - } - if (size != null) { - queryParams.addAll(_queryParams('', 'size', size)); - } - if (state != null) { - queryParams.addAll(_queryParams('', 'state', state)); - } - if (takenAfter != null) { - queryParams.addAll(_queryParams('', 'takenAfter', takenAfter)); - } - if (takenBefore != null) { - queryParams.addAll(_queryParams('', 'takenBefore', takenBefore)); - } - if (trashedAfter != null) { - queryParams.addAll(_queryParams('', 'trashedAfter', trashedAfter)); - } - if (trashedBefore != null) { - queryParams.addAll(_queryParams('', 'trashedBefore', trashedBefore)); - } - if (type != null) { - queryParams.addAll(_queryParams('', 'type', type)); - } - if (updatedAfter != null) { - queryParams.addAll(_queryParams('', 'updatedAfter', updatedAfter)); - } - if (updatedBefore != null) { - queryParams.addAll(_queryParams('', 'updatedBefore', updatedBefore)); - } - if (webpPath != null) { - queryParams.addAll(_queryParams('', 'webpPath', webpPath)); - } - if (withArchived != null) { - queryParams.addAll(_queryParams('', 'withArchived', withArchived)); - } - if (withDeleted != null) { - queryParams.addAll(_queryParams('', 'withDeleted', withDeleted)); - } - if (withExif != null) { - queryParams.addAll(_queryParams('', 'withExif', withExif)); - } - if (withPeople != null) { - queryParams.addAll(_queryParams('', 'withPeople', withPeople)); - } - if (withStacked != null) { - queryParams.addAll(_queryParams('', 'withStacked', withStacked)); - } - - const contentTypes = []; + const contentTypes = ['application/json']; return apiClient.invokeAPI( path, - 'GET', + 'POST', queryParams, postBody, headerParams, @@ -489,89 +285,9 @@ class SearchApi { /// Parameters: /// - /// * [String] checksum: - /// - /// * [String] city: - /// - /// * [String] country: - /// - /// * [DateTime] createdAfter: - /// - /// * [DateTime] createdBefore: - /// - /// * [String] deviceAssetId: - /// - /// * [String] deviceId: - /// - /// * [String] encodedVideoPath: - /// - /// * [String] id: - /// - /// * [bool] isArchived: - /// - /// * [bool] isEncoded: - /// - /// * [bool] isExternal: - /// - /// * [bool] isFavorite: - /// - /// * [bool] isMotion: - /// - /// * [bool] isOffline: - /// - /// * [bool] isReadOnly: - /// - /// * [bool] isVisible: - /// - /// * [String] lensModel: - /// - /// * [String] libraryId: - /// - /// * [String] make: - /// - /// * [String] model: - /// - /// * [AssetOrder] order: - /// - /// * [String] originalFileName: - /// - /// * [String] originalPath: - /// - /// * [num] page: - /// - /// * [String] resizePath: - /// - /// * [num] size: - /// - /// * [String] state: - /// - /// * [DateTime] takenAfter: - /// - /// * [DateTime] takenBefore: - /// - /// * [DateTime] trashedAfter: - /// - /// * [DateTime] trashedBefore: - /// - /// * [AssetTypeEnum] type: - /// - /// * [DateTime] updatedAfter: - /// - /// * [DateTime] updatedBefore: - /// - /// * [String] webpPath: - /// - /// * [bool] withArchived: - /// - /// * [bool] withDeleted: - /// - /// * [bool] withExif: - /// - /// * [bool] withPeople: - /// - /// * [bool] withStacked: - Future searchMetadata({ String? checksum, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceAssetId, String? deviceId, String? encodedVideoPath, String? id, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, AssetOrder? order, String? originalFileName, String? originalPath, num? page, String? resizePath, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, String? webpPath, bool? withArchived, bool? withDeleted, bool? withExif, bool? withPeople, bool? withStacked, }) async { - final response = await searchMetadataWithHttpInfo( checksum: checksum, city: city, country: country, createdAfter: createdAfter, createdBefore: createdBefore, deviceAssetId: deviceAssetId, deviceId: deviceId, encodedVideoPath: encodedVideoPath, id: id, isArchived: isArchived, isEncoded: isEncoded, isExternal: isExternal, isFavorite: isFavorite, isMotion: isMotion, isOffline: isOffline, isReadOnly: isReadOnly, isVisible: isVisible, lensModel: lensModel, libraryId: libraryId, make: make, model: model, order: order, originalFileName: originalFileName, originalPath: originalPath, page: page, resizePath: resizePath, size: size, state: state, takenAfter: takenAfter, takenBefore: takenBefore, trashedAfter: trashedAfter, trashedBefore: trashedBefore, type: type, updatedAfter: updatedAfter, updatedBefore: updatedBefore, webpPath: webpPath, withArchived: withArchived, withDeleted: withDeleted, withExif: withExif, withPeople: withPeople, withStacked: withStacked, ); + /// * [MetadataSearchDto] metadataSearchDto (required): + Future searchMetadata(MetadataSearchDto metadataSearchDto,) async { + final response = await searchMetadataWithHttpInfo(metadataSearchDto,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -644,73 +360,13 @@ class SearchApi { return null; } - /// Performs an HTTP 'GET /search/smart' operation and returns the [Response]. + /// Performs an HTTP 'GET /search/places' operation and returns the [Response]. /// Parameters: /// - /// * [String] query (required): - /// - /// * [String] city: - /// - /// * [String] country: - /// - /// * [DateTime] createdAfter: - /// - /// * [DateTime] createdBefore: - /// - /// * [String] deviceId: - /// - /// * [bool] isArchived: - /// - /// * [bool] isEncoded: - /// - /// * [bool] isExternal: - /// - /// * [bool] isFavorite: - /// - /// * [bool] isMotion: - /// - /// * [bool] isOffline: - /// - /// * [bool] isReadOnly: - /// - /// * [bool] isVisible: - /// - /// * [String] lensModel: - /// - /// * [String] libraryId: - /// - /// * [String] make: - /// - /// * [String] model: - /// - /// * [num] page: - /// - /// * [num] size: - /// - /// * [String] state: - /// - /// * [DateTime] takenAfter: - /// - /// * [DateTime] takenBefore: - /// - /// * [DateTime] trashedAfter: - /// - /// * [DateTime] trashedBefore: - /// - /// * [AssetTypeEnum] type: - /// - /// * [DateTime] updatedAfter: - /// - /// * [DateTime] updatedBefore: - /// - /// * [bool] withArchived: - /// - /// * [bool] withDeleted: - /// - /// * [bool] withExif: - Future searchSmartWithHttpInfo(String query, { String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceId, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, num? page, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, bool? withArchived, bool? withDeleted, bool? withExif, }) async { + /// * [String] name (required): + Future searchPlacesWithHttpInfo(String name,) async { // ignore: prefer_const_declarations - final path = r'/search/smart'; + final path = r'/search/places'; // ignore: prefer_final_locals Object? postBody; @@ -719,97 +375,7 @@ class SearchApi { final headerParams = {}; final formParams = {}; - if (city != null) { - queryParams.addAll(_queryParams('', 'city', city)); - } - if (country != null) { - queryParams.addAll(_queryParams('', 'country', country)); - } - if (createdAfter != null) { - queryParams.addAll(_queryParams('', 'createdAfter', createdAfter)); - } - if (createdBefore != null) { - queryParams.addAll(_queryParams('', 'createdBefore', createdBefore)); - } - if (deviceId != null) { - queryParams.addAll(_queryParams('', 'deviceId', deviceId)); - } - if (isArchived != null) { - queryParams.addAll(_queryParams('', 'isArchived', isArchived)); - } - if (isEncoded != null) { - queryParams.addAll(_queryParams('', 'isEncoded', isEncoded)); - } - if (isExternal != null) { - queryParams.addAll(_queryParams('', 'isExternal', isExternal)); - } - if (isFavorite != null) { - queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); - } - if (isMotion != null) { - queryParams.addAll(_queryParams('', 'isMotion', isMotion)); - } - if (isOffline != null) { - queryParams.addAll(_queryParams('', 'isOffline', isOffline)); - } - if (isReadOnly != null) { - queryParams.addAll(_queryParams('', 'isReadOnly', isReadOnly)); - } - if (isVisible != null) { - queryParams.addAll(_queryParams('', 'isVisible', isVisible)); - } - if (lensModel != null) { - queryParams.addAll(_queryParams('', 'lensModel', lensModel)); - } - if (libraryId != null) { - queryParams.addAll(_queryParams('', 'libraryId', libraryId)); - } - if (make != null) { - queryParams.addAll(_queryParams('', 'make', make)); - } - if (model != null) { - queryParams.addAll(_queryParams('', 'model', model)); - } - if (page != null) { - queryParams.addAll(_queryParams('', 'page', page)); - } - queryParams.addAll(_queryParams('', 'query', query)); - if (size != null) { - queryParams.addAll(_queryParams('', 'size', size)); - } - if (state != null) { - queryParams.addAll(_queryParams('', 'state', state)); - } - if (takenAfter != null) { - queryParams.addAll(_queryParams('', 'takenAfter', takenAfter)); - } - if (takenBefore != null) { - queryParams.addAll(_queryParams('', 'takenBefore', takenBefore)); - } - if (trashedAfter != null) { - queryParams.addAll(_queryParams('', 'trashedAfter', trashedAfter)); - } - if (trashedBefore != null) { - queryParams.addAll(_queryParams('', 'trashedBefore', trashedBefore)); - } - if (type != null) { - queryParams.addAll(_queryParams('', 'type', type)); - } - if (updatedAfter != null) { - queryParams.addAll(_queryParams('', 'updatedAfter', updatedAfter)); - } - if (updatedBefore != null) { - queryParams.addAll(_queryParams('', 'updatedBefore', updatedBefore)); - } - if (withArchived != null) { - queryParams.addAll(_queryParams('', 'withArchived', withArchived)); - } - if (withDeleted != null) { - queryParams.addAll(_queryParams('', 'withDeleted', withDeleted)); - } - if (withExif != null) { - queryParams.addAll(_queryParams('', 'withExif', withExif)); - } + queryParams.addAll(_queryParams('', 'name', name)); const contentTypes = []; @@ -827,69 +393,59 @@ class SearchApi { /// Parameters: /// - /// * [String] query (required): + /// * [String] name (required): + Future?> searchPlaces(String name,) async { + final response = await searchPlacesWithHttpInfo(name,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + final responseBody = await _decodeBodyBytes(response); + return (await apiClient.deserializeAsync(responseBody, 'List') as List) + .cast() + .toList(growable: false); + + } + return null; + } + + /// Performs an HTTP 'POST /search/smart' operation and returns the [Response]. + /// Parameters: /// - /// * [String] city: + /// * [SmartSearchDto] smartSearchDto (required): + Future searchSmartWithHttpInfo(SmartSearchDto smartSearchDto,) async { + // ignore: prefer_const_declarations + final path = r'/search/smart'; + + // ignore: prefer_final_locals + Object? postBody = smartSearchDto; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = ['application/json']; + + + return apiClient.invokeAPI( + path, + 'POST', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: /// - /// * [String] country: - /// - /// * [DateTime] createdAfter: - /// - /// * [DateTime] createdBefore: - /// - /// * [String] deviceId: - /// - /// * [bool] isArchived: - /// - /// * [bool] isEncoded: - /// - /// * [bool] isExternal: - /// - /// * [bool] isFavorite: - /// - /// * [bool] isMotion: - /// - /// * [bool] isOffline: - /// - /// * [bool] isReadOnly: - /// - /// * [bool] isVisible: - /// - /// * [String] lensModel: - /// - /// * [String] libraryId: - /// - /// * [String] make: - /// - /// * [String] model: - /// - /// * [num] page: - /// - /// * [num] size: - /// - /// * [String] state: - /// - /// * [DateTime] takenAfter: - /// - /// * [DateTime] takenBefore: - /// - /// * [DateTime] trashedAfter: - /// - /// * [DateTime] trashedBefore: - /// - /// * [AssetTypeEnum] type: - /// - /// * [DateTime] updatedAfter: - /// - /// * [DateTime] updatedBefore: - /// - /// * [bool] withArchived: - /// - /// * [bool] withDeleted: - /// - /// * [bool] withExif: - Future searchSmart(String query, { String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceId, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, num? page, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, bool? withArchived, bool? withDeleted, bool? withExif, }) async { - final response = await searchSmartWithHttpInfo(query, city: city, country: country, createdAfter: createdAfter, createdBefore: createdBefore, deviceId: deviceId, isArchived: isArchived, isEncoded: isEncoded, isExternal: isExternal, isFavorite: isFavorite, isMotion: isMotion, isOffline: isOffline, isReadOnly: isReadOnly, isVisible: isVisible, lensModel: lensModel, libraryId: libraryId, make: make, model: model, page: page, size: size, state: state, takenAfter: takenAfter, takenBefore: takenBefore, trashedAfter: trashedAfter, trashedBefore: trashedBefore, type: type, updatedAfter: updatedAfter, updatedBefore: updatedBefore, withArchived: withArchived, withDeleted: withDeleted, withExif: withExif, ); + /// * [SmartSearchDto] smartSearchDto (required): + Future searchSmart(SmartSearchDto smartSearchDto,) async { + final response = await searchSmartWithHttpInfo(smartSearchDto,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index a8cf4c34c1..24cffb7cff 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -336,6 +336,8 @@ class ApiClient { return MemoryLaneResponseDto.fromJson(value); case 'MergePersonDto': return MergePersonDto.fromJson(value); + case 'MetadataSearchDto': + return MetadataSearchDto.fromJson(value); case 'ModelType': return ModelTypeTypeTransformer().decode(value); case 'OAuthAuthorizeResponseDto': @@ -364,6 +366,8 @@ class ApiClient { return PersonUpdateDto.fromJson(value); case 'PersonWithFacesResponseDto': return PersonWithFacesResponseDto.fromJson(value); + case 'PlacesResponseDto': + return PlacesResponseDto.fromJson(value); case 'QueueStatusDto': return QueueStatusDto.fromJson(value); case 'ReactionLevel': @@ -418,6 +422,8 @@ class ApiClient { return SignUpDto.fromJson(value); case 'SmartInfoResponseDto': return SmartInfoResponseDto.fromJson(value); + case 'SmartSearchDto': + return SmartSearchDto.fromJson(value); case 'SystemConfigDto': return SystemConfigDto.fromJson(value); case 'SystemConfigFFmpegDto': @@ -496,6 +502,12 @@ class ApiClient { return UserResponseDto.fromJson(value); case 'ValidateAccessTokenResponseDto': return ValidateAccessTokenResponseDto.fromJson(value); + case 'ValidateLibraryDto': + return ValidateLibraryDto.fromJson(value); + case 'ValidateLibraryImportPathResponseDto': + return ValidateLibraryImportPathResponseDto.fromJson(value); + case 'ValidateLibraryResponseDto': + return ValidateLibraryResponseDto.fromJson(value); case 'VideoCodec': return VideoCodecTypeTransformer().decode(value); default: diff --git a/mobile/openapi/lib/model/create_library_dto.dart b/mobile/openapi/lib/model/create_library_dto.dart index ca4217dcfa..ef656ea2a3 100644 --- a/mobile/openapi/lib/model/create_library_dto.dart +++ b/mobile/openapi/lib/model/create_library_dto.dart @@ -18,6 +18,7 @@ class CreateLibraryDto { this.isVisible, this.isWatched, this.name, + this.ownerId, required this.type, }); @@ -49,6 +50,14 @@ class CreateLibraryDto { /// String? name; + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? ownerId; + LibraryType type; @override @@ -58,6 +67,7 @@ class CreateLibraryDto { other.isVisible == isVisible && other.isWatched == isWatched && other.name == name && + other.ownerId == ownerId && other.type == type; @override @@ -68,10 +78,11 @@ class CreateLibraryDto { (isVisible == null ? 0 : isVisible!.hashCode) + (isWatched == null ? 0 : isWatched!.hashCode) + (name == null ? 0 : name!.hashCode) + + (ownerId == null ? 0 : ownerId!.hashCode) + (type.hashCode); @override - String toString() => 'CreateLibraryDto[exclusionPatterns=$exclusionPatterns, importPaths=$importPaths, isVisible=$isVisible, isWatched=$isWatched, name=$name, type=$type]'; + String toString() => 'CreateLibraryDto[exclusionPatterns=$exclusionPatterns, importPaths=$importPaths, isVisible=$isVisible, isWatched=$isWatched, name=$name, ownerId=$ownerId, type=$type]'; Map toJson() { final json = {}; @@ -91,6 +102,11 @@ class CreateLibraryDto { json[r'name'] = this.name; } else { // json[r'name'] = null; + } + if (this.ownerId != null) { + json[r'ownerId'] = this.ownerId; + } else { + // json[r'ownerId'] = null; } json[r'type'] = this.type; return json; @@ -113,6 +129,7 @@ class CreateLibraryDto { isVisible: mapValueOfType(json, r'isVisible'), isWatched: mapValueOfType(json, r'isWatched'), name: mapValueOfType(json, r'name'), + ownerId: mapValueOfType(json, r'ownerId'), type: LibraryType.fromJson(json[r'type'])!, ); } diff --git a/mobile/openapi/lib/model/create_user_dto.dart b/mobile/openapi/lib/model/create_user_dto.dart index bfa26622f7..f272842cbe 100644 --- a/mobile/openapi/lib/model/create_user_dto.dart +++ b/mobile/openapi/lib/model/create_user_dto.dart @@ -14,7 +14,6 @@ class CreateUserDto { /// Returns a new [CreateUserDto] instance. CreateUserDto({ required this.email, - this.externalPath, this.memoriesEnabled, required this.name, required this.password, @@ -24,8 +23,6 @@ class CreateUserDto { String email; - String? externalPath; - /// /// Please note: This property should have been non-nullable! Since the specification file /// does not include a default value (using the "default:" property), however, the generated @@ -45,7 +42,6 @@ class CreateUserDto { @override bool operator ==(Object other) => identical(this, other) || other is CreateUserDto && other.email == email && - other.externalPath == externalPath && other.memoriesEnabled == memoriesEnabled && other.name == name && other.password == password && @@ -56,7 +52,6 @@ class CreateUserDto { int get hashCode => // ignore: unnecessary_parenthesis (email.hashCode) + - (externalPath == null ? 0 : externalPath!.hashCode) + (memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) + (name.hashCode) + (password.hashCode) + @@ -64,16 +59,11 @@ class CreateUserDto { (storageLabel == null ? 0 : storageLabel!.hashCode); @override - String toString() => 'CreateUserDto[email=$email, externalPath=$externalPath, memoriesEnabled=$memoriesEnabled, name=$name, password=$password, quotaSizeInBytes=$quotaSizeInBytes, storageLabel=$storageLabel]'; + String toString() => 'CreateUserDto[email=$email, memoriesEnabled=$memoriesEnabled, name=$name, password=$password, quotaSizeInBytes=$quotaSizeInBytes, storageLabel=$storageLabel]'; Map toJson() { final json = {}; json[r'email'] = this.email; - if (this.externalPath != null) { - json[r'externalPath'] = this.externalPath; - } else { - // json[r'externalPath'] = null; - } if (this.memoriesEnabled != null) { json[r'memoriesEnabled'] = this.memoriesEnabled; } else { @@ -103,7 +93,6 @@ class CreateUserDto { return CreateUserDto( email: mapValueOfType(json, r'email')!, - externalPath: mapValueOfType(json, r'externalPath'), memoriesEnabled: mapValueOfType(json, r'memoriesEnabled'), name: mapValueOfType(json, r'name')!, password: mapValueOfType(json, r'password')!, diff --git a/mobile/openapi/lib/model/metadata_search_dto.dart b/mobile/openapi/lib/model/metadata_search_dto.dart new file mode 100644 index 0000000000..86a2856e66 --- /dev/null +++ b/mobile/openapi/lib/model/metadata_search_dto.dart @@ -0,0 +1,803 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.12 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class MetadataSearchDto { + /// Returns a new [MetadataSearchDto] instance. + MetadataSearchDto({ + this.checksum, + this.city, + this.country, + this.createdAfter, + this.createdBefore, + this.deviceAssetId, + this.deviceId, + this.encodedVideoPath, + this.id, + this.isArchived, + this.isEncoded, + this.isExternal, + this.isFavorite, + this.isMotion, + this.isNotInAlbum, + this.isOffline, + this.isReadOnly, + this.isVisible, + this.lensModel, + this.libraryId, + this.make, + this.model, + this.order, + this.originalFileName, + this.originalPath, + this.page, + this.personIds = const [], + this.resizePath, + this.size, + this.state, + this.takenAfter, + this.takenBefore, + this.trashedAfter, + this.trashedBefore, + this.type, + this.updatedAfter, + this.updatedBefore, + this.webpPath, + this.withArchived = false, + this.withDeleted, + this.withExif, + this.withPeople, + this.withStacked, + }); + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? checksum; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? city; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? country; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + DateTime? createdAfter; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + DateTime? createdBefore; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? deviceAssetId; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? deviceId; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? encodedVideoPath; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? id; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? isArchived; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? isEncoded; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? isExternal; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? isFavorite; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? isMotion; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? isNotInAlbum; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? isOffline; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? isReadOnly; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? isVisible; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? lensModel; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? libraryId; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? make; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? model; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + AssetOrder? order; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? originalFileName; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? originalPath; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + num? page; + + List personIds; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? resizePath; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + num? size; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? state; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + DateTime? takenAfter; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + DateTime? takenBefore; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + DateTime? trashedAfter; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + DateTime? trashedBefore; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + AssetTypeEnum? type; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + DateTime? updatedAfter; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + DateTime? updatedBefore; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? webpPath; + + bool withArchived; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? withDeleted; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? withExif; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? withPeople; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? withStacked; + + @override + bool operator ==(Object other) => identical(this, other) || other is MetadataSearchDto && + other.checksum == checksum && + other.city == city && + other.country == country && + other.createdAfter == createdAfter && + other.createdBefore == createdBefore && + other.deviceAssetId == deviceAssetId && + other.deviceId == deviceId && + other.encodedVideoPath == encodedVideoPath && + other.id == id && + other.isArchived == isArchived && + other.isEncoded == isEncoded && + other.isExternal == isExternal && + other.isFavorite == isFavorite && + other.isMotion == isMotion && + other.isNotInAlbum == isNotInAlbum && + other.isOffline == isOffline && + other.isReadOnly == isReadOnly && + other.isVisible == isVisible && + other.lensModel == lensModel && + other.libraryId == libraryId && + other.make == make && + other.model == model && + other.order == order && + other.originalFileName == originalFileName && + other.originalPath == originalPath && + other.page == page && + _deepEquality.equals(other.personIds, personIds) && + other.resizePath == resizePath && + other.size == size && + other.state == state && + other.takenAfter == takenAfter && + other.takenBefore == takenBefore && + other.trashedAfter == trashedAfter && + other.trashedBefore == trashedBefore && + other.type == type && + other.updatedAfter == updatedAfter && + other.updatedBefore == updatedBefore && + other.webpPath == webpPath && + other.withArchived == withArchived && + other.withDeleted == withDeleted && + other.withExif == withExif && + other.withPeople == withPeople && + other.withStacked == withStacked; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (checksum == null ? 0 : checksum!.hashCode) + + (city == null ? 0 : city!.hashCode) + + (country == null ? 0 : country!.hashCode) + + (createdAfter == null ? 0 : createdAfter!.hashCode) + + (createdBefore == null ? 0 : createdBefore!.hashCode) + + (deviceAssetId == null ? 0 : deviceAssetId!.hashCode) + + (deviceId == null ? 0 : deviceId!.hashCode) + + (encodedVideoPath == null ? 0 : encodedVideoPath!.hashCode) + + (id == null ? 0 : id!.hashCode) + + (isArchived == null ? 0 : isArchived!.hashCode) + + (isEncoded == null ? 0 : isEncoded!.hashCode) + + (isExternal == null ? 0 : isExternal!.hashCode) + + (isFavorite == null ? 0 : isFavorite!.hashCode) + + (isMotion == null ? 0 : isMotion!.hashCode) + + (isNotInAlbum == null ? 0 : isNotInAlbum!.hashCode) + + (isOffline == null ? 0 : isOffline!.hashCode) + + (isReadOnly == null ? 0 : isReadOnly!.hashCode) + + (isVisible == null ? 0 : isVisible!.hashCode) + + (lensModel == null ? 0 : lensModel!.hashCode) + + (libraryId == null ? 0 : libraryId!.hashCode) + + (make == null ? 0 : make!.hashCode) + + (model == null ? 0 : model!.hashCode) + + (order == null ? 0 : order!.hashCode) + + (originalFileName == null ? 0 : originalFileName!.hashCode) + + (originalPath == null ? 0 : originalPath!.hashCode) + + (page == null ? 0 : page!.hashCode) + + (personIds.hashCode) + + (resizePath == null ? 0 : resizePath!.hashCode) + + (size == null ? 0 : size!.hashCode) + + (state == null ? 0 : state!.hashCode) + + (takenAfter == null ? 0 : takenAfter!.hashCode) + + (takenBefore == null ? 0 : takenBefore!.hashCode) + + (trashedAfter == null ? 0 : trashedAfter!.hashCode) + + (trashedBefore == null ? 0 : trashedBefore!.hashCode) + + (type == null ? 0 : type!.hashCode) + + (updatedAfter == null ? 0 : updatedAfter!.hashCode) + + (updatedBefore == null ? 0 : updatedBefore!.hashCode) + + (webpPath == null ? 0 : webpPath!.hashCode) + + (withArchived.hashCode) + + (withDeleted == null ? 0 : withDeleted!.hashCode) + + (withExif == null ? 0 : withExif!.hashCode) + + (withPeople == null ? 0 : withPeople!.hashCode) + + (withStacked == null ? 0 : withStacked!.hashCode); + + @override + String toString() => 'MetadataSearchDto[checksum=$checksum, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceAssetId=$deviceAssetId, deviceId=$deviceId, encodedVideoPath=$encodedVideoPath, id=$id, isArchived=$isArchived, isEncoded=$isEncoded, isExternal=$isExternal, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, isReadOnly=$isReadOnly, isVisible=$isVisible, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, order=$order, originalFileName=$originalFileName, originalPath=$originalPath, page=$page, personIds=$personIds, resizePath=$resizePath, size=$size, state=$state, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, webpPath=$webpPath, withArchived=$withArchived, withDeleted=$withDeleted, withExif=$withExif, withPeople=$withPeople, withStacked=$withStacked]'; + + Map toJson() { + final json = {}; + if (this.checksum != null) { + json[r'checksum'] = this.checksum; + } else { + // json[r'checksum'] = null; + } + if (this.city != null) { + json[r'city'] = this.city; + } else { + // json[r'city'] = null; + } + if (this.country != null) { + json[r'country'] = this.country; + } else { + // json[r'country'] = null; + } + if (this.createdAfter != null) { + json[r'createdAfter'] = this.createdAfter!.toUtc().toIso8601String(); + } else { + // json[r'createdAfter'] = null; + } + if (this.createdBefore != null) { + json[r'createdBefore'] = this.createdBefore!.toUtc().toIso8601String(); + } else { + // json[r'createdBefore'] = null; + } + if (this.deviceAssetId != null) { + json[r'deviceAssetId'] = this.deviceAssetId; + } else { + // json[r'deviceAssetId'] = null; + } + if (this.deviceId != null) { + json[r'deviceId'] = this.deviceId; + } else { + // json[r'deviceId'] = null; + } + if (this.encodedVideoPath != null) { + json[r'encodedVideoPath'] = this.encodedVideoPath; + } else { + // json[r'encodedVideoPath'] = null; + } + if (this.id != null) { + json[r'id'] = this.id; + } else { + // json[r'id'] = null; + } + if (this.isArchived != null) { + json[r'isArchived'] = this.isArchived; + } else { + // json[r'isArchived'] = null; + } + if (this.isEncoded != null) { + json[r'isEncoded'] = this.isEncoded; + } else { + // json[r'isEncoded'] = null; + } + if (this.isExternal != null) { + json[r'isExternal'] = this.isExternal; + } else { + // json[r'isExternal'] = null; + } + if (this.isFavorite != null) { + json[r'isFavorite'] = this.isFavorite; + } else { + // json[r'isFavorite'] = null; + } + if (this.isMotion != null) { + json[r'isMotion'] = this.isMotion; + } else { + // json[r'isMotion'] = null; + } + if (this.isNotInAlbum != null) { + json[r'isNotInAlbum'] = this.isNotInAlbum; + } else { + // json[r'isNotInAlbum'] = null; + } + if (this.isOffline != null) { + json[r'isOffline'] = this.isOffline; + } else { + // json[r'isOffline'] = null; + } + if (this.isReadOnly != null) { + json[r'isReadOnly'] = this.isReadOnly; + } else { + // json[r'isReadOnly'] = null; + } + if (this.isVisible != null) { + json[r'isVisible'] = this.isVisible; + } else { + // json[r'isVisible'] = null; + } + if (this.lensModel != null) { + json[r'lensModel'] = this.lensModel; + } else { + // json[r'lensModel'] = null; + } + if (this.libraryId != null) { + json[r'libraryId'] = this.libraryId; + } else { + // json[r'libraryId'] = null; + } + if (this.make != null) { + json[r'make'] = this.make; + } else { + // json[r'make'] = null; + } + if (this.model != null) { + json[r'model'] = this.model; + } else { + // json[r'model'] = null; + } + if (this.order != null) { + json[r'order'] = this.order; + } else { + // json[r'order'] = null; + } + if (this.originalFileName != null) { + json[r'originalFileName'] = this.originalFileName; + } else { + // json[r'originalFileName'] = null; + } + if (this.originalPath != null) { + json[r'originalPath'] = this.originalPath; + } else { + // json[r'originalPath'] = null; + } + if (this.page != null) { + json[r'page'] = this.page; + } else { + // json[r'page'] = null; + } + json[r'personIds'] = this.personIds; + if (this.resizePath != null) { + json[r'resizePath'] = this.resizePath; + } else { + // json[r'resizePath'] = null; + } + if (this.size != null) { + json[r'size'] = this.size; + } else { + // json[r'size'] = null; + } + if (this.state != null) { + json[r'state'] = this.state; + } else { + // json[r'state'] = null; + } + if (this.takenAfter != null) { + json[r'takenAfter'] = this.takenAfter!.toUtc().toIso8601String(); + } else { + // json[r'takenAfter'] = null; + } + if (this.takenBefore != null) { + json[r'takenBefore'] = this.takenBefore!.toUtc().toIso8601String(); + } else { + // json[r'takenBefore'] = null; + } + if (this.trashedAfter != null) { + json[r'trashedAfter'] = this.trashedAfter!.toUtc().toIso8601String(); + } else { + // json[r'trashedAfter'] = null; + } + if (this.trashedBefore != null) { + json[r'trashedBefore'] = this.trashedBefore!.toUtc().toIso8601String(); + } else { + // json[r'trashedBefore'] = null; + } + if (this.type != null) { + json[r'type'] = this.type; + } else { + // json[r'type'] = null; + } + if (this.updatedAfter != null) { + json[r'updatedAfter'] = this.updatedAfter!.toUtc().toIso8601String(); + } else { + // json[r'updatedAfter'] = null; + } + if (this.updatedBefore != null) { + json[r'updatedBefore'] = this.updatedBefore!.toUtc().toIso8601String(); + } else { + // json[r'updatedBefore'] = null; + } + if (this.webpPath != null) { + json[r'webpPath'] = this.webpPath; + } else { + // json[r'webpPath'] = null; + } + json[r'withArchived'] = this.withArchived; + if (this.withDeleted != null) { + json[r'withDeleted'] = this.withDeleted; + } else { + // json[r'withDeleted'] = null; + } + if (this.withExif != null) { + json[r'withExif'] = this.withExif; + } else { + // json[r'withExif'] = null; + } + if (this.withPeople != null) { + json[r'withPeople'] = this.withPeople; + } else { + // json[r'withPeople'] = null; + } + if (this.withStacked != null) { + json[r'withStacked'] = this.withStacked; + } else { + // json[r'withStacked'] = null; + } + return json; + } + + /// Returns a new [MetadataSearchDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static MetadataSearchDto? fromJson(dynamic value) { + if (value is Map) { + final json = value.cast(); + + return MetadataSearchDto( + checksum: mapValueOfType(json, r'checksum'), + city: mapValueOfType(json, r'city'), + country: mapValueOfType(json, r'country'), + createdAfter: mapDateTime(json, r'createdAfter', r''), + createdBefore: mapDateTime(json, r'createdBefore', r''), + deviceAssetId: mapValueOfType(json, r'deviceAssetId'), + deviceId: mapValueOfType(json, r'deviceId'), + encodedVideoPath: mapValueOfType(json, r'encodedVideoPath'), + id: mapValueOfType(json, r'id'), + isArchived: mapValueOfType(json, r'isArchived'), + isEncoded: mapValueOfType(json, r'isEncoded'), + isExternal: mapValueOfType(json, r'isExternal'), + isFavorite: mapValueOfType(json, r'isFavorite'), + isMotion: mapValueOfType(json, r'isMotion'), + isNotInAlbum: mapValueOfType(json, r'isNotInAlbum'), + isOffline: mapValueOfType(json, r'isOffline'), + isReadOnly: mapValueOfType(json, r'isReadOnly'), + isVisible: mapValueOfType(json, r'isVisible'), + lensModel: mapValueOfType(json, r'lensModel'), + libraryId: mapValueOfType(json, r'libraryId'), + make: mapValueOfType(json, r'make'), + model: mapValueOfType(json, r'model'), + order: AssetOrder.fromJson(json[r'order']), + originalFileName: mapValueOfType(json, r'originalFileName'), + originalPath: mapValueOfType(json, r'originalPath'), + page: num.parse('${json[r'page']}'), + personIds: json[r'personIds'] is Iterable + ? (json[r'personIds'] as Iterable).cast().toList(growable: false) + : const [], + resizePath: mapValueOfType(json, r'resizePath'), + size: num.parse('${json[r'size']}'), + state: mapValueOfType(json, r'state'), + takenAfter: mapDateTime(json, r'takenAfter', r''), + takenBefore: mapDateTime(json, r'takenBefore', r''), + trashedAfter: mapDateTime(json, r'trashedAfter', r''), + trashedBefore: mapDateTime(json, r'trashedBefore', r''), + type: AssetTypeEnum.fromJson(json[r'type']), + updatedAfter: mapDateTime(json, r'updatedAfter', r''), + updatedBefore: mapDateTime(json, r'updatedBefore', r''), + webpPath: mapValueOfType(json, r'webpPath'), + withArchived: mapValueOfType(json, r'withArchived') ?? false, + withDeleted: mapValueOfType(json, r'withDeleted'), + withExif: mapValueOfType(json, r'withExif'), + withPeople: mapValueOfType(json, r'withPeople'), + withStacked: mapValueOfType(json, r'withStacked'), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = MetadataSearchDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = MetadataSearchDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of MetadataSearchDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = MetadataSearchDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + }; +} + diff --git a/mobile/openapi/lib/model/partner_response_dto.dart b/mobile/openapi/lib/model/partner_response_dto.dart index aa96f764bd..008e0c4f26 100644 --- a/mobile/openapi/lib/model/partner_response_dto.dart +++ b/mobile/openapi/lib/model/partner_response_dto.dart @@ -17,7 +17,6 @@ class PartnerResponseDto { required this.createdAt, required this.deletedAt, required this.email, - required this.externalPath, required this.id, this.inTimeline, required this.isAdmin, @@ -40,8 +39,6 @@ class PartnerResponseDto { String email; - String? externalPath; - String id; /// @@ -84,7 +81,6 @@ class PartnerResponseDto { other.createdAt == createdAt && other.deletedAt == deletedAt && other.email == email && - other.externalPath == externalPath && other.id == id && other.inTimeline == inTimeline && other.isAdmin == isAdmin && @@ -105,7 +101,6 @@ class PartnerResponseDto { (createdAt.hashCode) + (deletedAt == null ? 0 : deletedAt!.hashCode) + (email.hashCode) + - (externalPath == null ? 0 : externalPath!.hashCode) + (id.hashCode) + (inTimeline == null ? 0 : inTimeline!.hashCode) + (isAdmin.hashCode) + @@ -120,7 +115,7 @@ class PartnerResponseDto { (updatedAt.hashCode); @override - String toString() => 'PartnerResponseDto[avatarColor=$avatarColor, createdAt=$createdAt, deletedAt=$deletedAt, email=$email, externalPath=$externalPath, id=$id, inTimeline=$inTimeline, isAdmin=$isAdmin, memoriesEnabled=$memoriesEnabled, name=$name, oauthId=$oauthId, profileImagePath=$profileImagePath, quotaSizeInBytes=$quotaSizeInBytes, quotaUsageInBytes=$quotaUsageInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel, updatedAt=$updatedAt]'; + String toString() => 'PartnerResponseDto[avatarColor=$avatarColor, createdAt=$createdAt, deletedAt=$deletedAt, email=$email, id=$id, inTimeline=$inTimeline, isAdmin=$isAdmin, memoriesEnabled=$memoriesEnabled, name=$name, oauthId=$oauthId, profileImagePath=$profileImagePath, quotaSizeInBytes=$quotaSizeInBytes, quotaUsageInBytes=$quotaUsageInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel, updatedAt=$updatedAt]'; Map toJson() { final json = {}; @@ -132,11 +127,6 @@ class PartnerResponseDto { // json[r'deletedAt'] = null; } json[r'email'] = this.email; - if (this.externalPath != null) { - json[r'externalPath'] = this.externalPath; - } else { - // json[r'externalPath'] = null; - } json[r'id'] = this.id; if (this.inTimeline != null) { json[r'inTimeline'] = this.inTimeline; @@ -184,7 +174,6 @@ class PartnerResponseDto { createdAt: mapDateTime(json, r'createdAt', r'')!, deletedAt: mapDateTime(json, r'deletedAt', r''), email: mapValueOfType(json, r'email')!, - externalPath: mapValueOfType(json, r'externalPath'), id: mapValueOfType(json, r'id')!, inTimeline: mapValueOfType(json, r'inTimeline'), isAdmin: mapValueOfType(json, r'isAdmin')!, @@ -248,7 +237,6 @@ class PartnerResponseDto { 'createdAt', 'deletedAt', 'email', - 'externalPath', 'id', 'isAdmin', 'name', diff --git a/mobile/openapi/lib/model/people_response_dto.dart b/mobile/openapi/lib/model/people_response_dto.dart index 80abedfc72..02a82cadf1 100644 --- a/mobile/openapi/lib/model/people_response_dto.dart +++ b/mobile/openapi/lib/model/people_response_dto.dart @@ -13,30 +13,36 @@ part of openapi.api; class PeopleResponseDto { /// Returns a new [PeopleResponseDto] instance. PeopleResponseDto({ + required this.hidden, this.people = const [], required this.total, }); + int hidden; + List people; int total; @override bool operator ==(Object other) => identical(this, other) || other is PeopleResponseDto && + other.hidden == hidden && _deepEquality.equals(other.people, people) && other.total == total; @override int get hashCode => // ignore: unnecessary_parenthesis + (hidden.hashCode) + (people.hashCode) + (total.hashCode); @override - String toString() => 'PeopleResponseDto[people=$people, total=$total]'; + String toString() => 'PeopleResponseDto[hidden=$hidden, people=$people, total=$total]'; Map toJson() { final json = {}; + json[r'hidden'] = this.hidden; json[r'people'] = this.people; json[r'total'] = this.total; return json; @@ -50,6 +56,7 @@ class PeopleResponseDto { final json = value.cast(); return PeopleResponseDto( + hidden: mapValueOfType(json, r'hidden')!, people: PersonResponseDto.listFromJson(json[r'people']), total: mapValueOfType(json, r'total')!, ); @@ -99,6 +106,7 @@ class PeopleResponseDto { /// The list of required keys that must be present in a JSON. static const requiredKeys = { + 'hidden', 'people', 'total', }; diff --git a/mobile/openapi/lib/model/places_response_dto.dart b/mobile/openapi/lib/model/places_response_dto.dart new file mode 100644 index 0000000000..a2d8378883 --- /dev/null +++ b/mobile/openapi/lib/model/places_response_dto.dart @@ -0,0 +1,148 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.12 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class PlacesResponseDto { + /// Returns a new [PlacesResponseDto] instance. + PlacesResponseDto({ + this.admin1name, + this.admin2name, + required this.latitude, + required this.longitude, + required this.name, + }); + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? admin1name; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? admin2name; + + num latitude; + + num longitude; + + String name; + + @override + bool operator ==(Object other) => identical(this, other) || other is PlacesResponseDto && + other.admin1name == admin1name && + other.admin2name == admin2name && + other.latitude == latitude && + other.longitude == longitude && + other.name == name; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (admin1name == null ? 0 : admin1name!.hashCode) + + (admin2name == null ? 0 : admin2name!.hashCode) + + (latitude.hashCode) + + (longitude.hashCode) + + (name.hashCode); + + @override + String toString() => 'PlacesResponseDto[admin1name=$admin1name, admin2name=$admin2name, latitude=$latitude, longitude=$longitude, name=$name]'; + + Map toJson() { + final json = {}; + if (this.admin1name != null) { + json[r'admin1name'] = this.admin1name; + } else { + // json[r'admin1name'] = null; + } + if (this.admin2name != null) { + json[r'admin2name'] = this.admin2name; + } else { + // json[r'admin2name'] = null; + } + json[r'latitude'] = this.latitude; + json[r'longitude'] = this.longitude; + json[r'name'] = this.name; + return json; + } + + /// Returns a new [PlacesResponseDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static PlacesResponseDto? fromJson(dynamic value) { + if (value is Map) { + final json = value.cast(); + + return PlacesResponseDto( + admin1name: mapValueOfType(json, r'admin1name'), + admin2name: mapValueOfType(json, r'admin2name'), + latitude: num.parse('${json[r'latitude']}'), + longitude: num.parse('${json[r'longitude']}'), + name: mapValueOfType(json, r'name')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = PlacesResponseDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = PlacesResponseDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of PlacesResponseDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = PlacesResponseDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'latitude', + 'longitude', + 'name', + }; +} + diff --git a/mobile/openapi/lib/model/smart_search_dto.dart b/mobile/openapi/lib/model/smart_search_dto.dart new file mode 100644 index 0000000000..0b99acdd66 --- /dev/null +++ b/mobile/openapi/lib/model/smart_search_dto.dart @@ -0,0 +1,624 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.12 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class SmartSearchDto { + /// Returns a new [SmartSearchDto] instance. + SmartSearchDto({ + this.city, + this.country, + this.createdAfter, + this.createdBefore, + this.deviceId, + this.isArchived, + this.isEncoded, + this.isExternal, + this.isFavorite, + this.isMotion, + this.isNotInAlbum, + this.isOffline, + this.isReadOnly, + this.isVisible, + this.lensModel, + this.libraryId, + this.make, + this.model, + this.page, + this.personIds = const [], + required this.query, + this.size, + this.state, + this.takenAfter, + this.takenBefore, + this.trashedAfter, + this.trashedBefore, + this.type, + this.updatedAfter, + this.updatedBefore, + this.withArchived = false, + this.withDeleted, + this.withExif, + }); + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? city; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? country; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + DateTime? createdAfter; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + DateTime? createdBefore; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? deviceId; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? isArchived; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? isEncoded; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? isExternal; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? isFavorite; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? isMotion; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? isNotInAlbum; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? isOffline; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? isReadOnly; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? isVisible; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? lensModel; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? libraryId; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? make; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? model; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + num? page; + + List personIds; + + String query; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + num? size; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? state; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + DateTime? takenAfter; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + DateTime? takenBefore; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + DateTime? trashedAfter; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + DateTime? trashedBefore; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + AssetTypeEnum? type; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + DateTime? updatedAfter; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + DateTime? updatedBefore; + + bool withArchived; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? withDeleted; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + bool? withExif; + + @override + bool operator ==(Object other) => identical(this, other) || other is SmartSearchDto && + other.city == city && + other.country == country && + other.createdAfter == createdAfter && + other.createdBefore == createdBefore && + other.deviceId == deviceId && + other.isArchived == isArchived && + other.isEncoded == isEncoded && + other.isExternal == isExternal && + other.isFavorite == isFavorite && + other.isMotion == isMotion && + other.isNotInAlbum == isNotInAlbum && + other.isOffline == isOffline && + other.isReadOnly == isReadOnly && + other.isVisible == isVisible && + other.lensModel == lensModel && + other.libraryId == libraryId && + other.make == make && + other.model == model && + other.page == page && + _deepEquality.equals(other.personIds, personIds) && + other.query == query && + other.size == size && + other.state == state && + other.takenAfter == takenAfter && + other.takenBefore == takenBefore && + other.trashedAfter == trashedAfter && + other.trashedBefore == trashedBefore && + other.type == type && + other.updatedAfter == updatedAfter && + other.updatedBefore == updatedBefore && + other.withArchived == withArchived && + other.withDeleted == withDeleted && + other.withExif == withExif; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (city == null ? 0 : city!.hashCode) + + (country == null ? 0 : country!.hashCode) + + (createdAfter == null ? 0 : createdAfter!.hashCode) + + (createdBefore == null ? 0 : createdBefore!.hashCode) + + (deviceId == null ? 0 : deviceId!.hashCode) + + (isArchived == null ? 0 : isArchived!.hashCode) + + (isEncoded == null ? 0 : isEncoded!.hashCode) + + (isExternal == null ? 0 : isExternal!.hashCode) + + (isFavorite == null ? 0 : isFavorite!.hashCode) + + (isMotion == null ? 0 : isMotion!.hashCode) + + (isNotInAlbum == null ? 0 : isNotInAlbum!.hashCode) + + (isOffline == null ? 0 : isOffline!.hashCode) + + (isReadOnly == null ? 0 : isReadOnly!.hashCode) + + (isVisible == null ? 0 : isVisible!.hashCode) + + (lensModel == null ? 0 : lensModel!.hashCode) + + (libraryId == null ? 0 : libraryId!.hashCode) + + (make == null ? 0 : make!.hashCode) + + (model == null ? 0 : model!.hashCode) + + (page == null ? 0 : page!.hashCode) + + (personIds.hashCode) + + (query.hashCode) + + (size == null ? 0 : size!.hashCode) + + (state == null ? 0 : state!.hashCode) + + (takenAfter == null ? 0 : takenAfter!.hashCode) + + (takenBefore == null ? 0 : takenBefore!.hashCode) + + (trashedAfter == null ? 0 : trashedAfter!.hashCode) + + (trashedBefore == null ? 0 : trashedBefore!.hashCode) + + (type == null ? 0 : type!.hashCode) + + (updatedAfter == null ? 0 : updatedAfter!.hashCode) + + (updatedBefore == null ? 0 : updatedBefore!.hashCode) + + (withArchived.hashCode) + + (withDeleted == null ? 0 : withDeleted!.hashCode) + + (withExif == null ? 0 : withExif!.hashCode); + + @override + String toString() => 'SmartSearchDto[city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceId=$deviceId, isArchived=$isArchived, isEncoded=$isEncoded, isExternal=$isExternal, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, isReadOnly=$isReadOnly, isVisible=$isVisible, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, page=$page, personIds=$personIds, query=$query, size=$size, state=$state, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, withArchived=$withArchived, withDeleted=$withDeleted, withExif=$withExif]'; + + Map toJson() { + final json = {}; + if (this.city != null) { + json[r'city'] = this.city; + } else { + // json[r'city'] = null; + } + if (this.country != null) { + json[r'country'] = this.country; + } else { + // json[r'country'] = null; + } + if (this.createdAfter != null) { + json[r'createdAfter'] = this.createdAfter!.toUtc().toIso8601String(); + } else { + // json[r'createdAfter'] = null; + } + if (this.createdBefore != null) { + json[r'createdBefore'] = this.createdBefore!.toUtc().toIso8601String(); + } else { + // json[r'createdBefore'] = null; + } + if (this.deviceId != null) { + json[r'deviceId'] = this.deviceId; + } else { + // json[r'deviceId'] = null; + } + if (this.isArchived != null) { + json[r'isArchived'] = this.isArchived; + } else { + // json[r'isArchived'] = null; + } + if (this.isEncoded != null) { + json[r'isEncoded'] = this.isEncoded; + } else { + // json[r'isEncoded'] = null; + } + if (this.isExternal != null) { + json[r'isExternal'] = this.isExternal; + } else { + // json[r'isExternal'] = null; + } + if (this.isFavorite != null) { + json[r'isFavorite'] = this.isFavorite; + } else { + // json[r'isFavorite'] = null; + } + if (this.isMotion != null) { + json[r'isMotion'] = this.isMotion; + } else { + // json[r'isMotion'] = null; + } + if (this.isNotInAlbum != null) { + json[r'isNotInAlbum'] = this.isNotInAlbum; + } else { + // json[r'isNotInAlbum'] = null; + } + if (this.isOffline != null) { + json[r'isOffline'] = this.isOffline; + } else { + // json[r'isOffline'] = null; + } + if (this.isReadOnly != null) { + json[r'isReadOnly'] = this.isReadOnly; + } else { + // json[r'isReadOnly'] = null; + } + if (this.isVisible != null) { + json[r'isVisible'] = this.isVisible; + } else { + // json[r'isVisible'] = null; + } + if (this.lensModel != null) { + json[r'lensModel'] = this.lensModel; + } else { + // json[r'lensModel'] = null; + } + if (this.libraryId != null) { + json[r'libraryId'] = this.libraryId; + } else { + // json[r'libraryId'] = null; + } + if (this.make != null) { + json[r'make'] = this.make; + } else { + // json[r'make'] = null; + } + if (this.model != null) { + json[r'model'] = this.model; + } else { + // json[r'model'] = null; + } + if (this.page != null) { + json[r'page'] = this.page; + } else { + // json[r'page'] = null; + } + json[r'personIds'] = this.personIds; + json[r'query'] = this.query; + if (this.size != null) { + json[r'size'] = this.size; + } else { + // json[r'size'] = null; + } + if (this.state != null) { + json[r'state'] = this.state; + } else { + // json[r'state'] = null; + } + if (this.takenAfter != null) { + json[r'takenAfter'] = this.takenAfter!.toUtc().toIso8601String(); + } else { + // json[r'takenAfter'] = null; + } + if (this.takenBefore != null) { + json[r'takenBefore'] = this.takenBefore!.toUtc().toIso8601String(); + } else { + // json[r'takenBefore'] = null; + } + if (this.trashedAfter != null) { + json[r'trashedAfter'] = this.trashedAfter!.toUtc().toIso8601String(); + } else { + // json[r'trashedAfter'] = null; + } + if (this.trashedBefore != null) { + json[r'trashedBefore'] = this.trashedBefore!.toUtc().toIso8601String(); + } else { + // json[r'trashedBefore'] = null; + } + if (this.type != null) { + json[r'type'] = this.type; + } else { + // json[r'type'] = null; + } + if (this.updatedAfter != null) { + json[r'updatedAfter'] = this.updatedAfter!.toUtc().toIso8601String(); + } else { + // json[r'updatedAfter'] = null; + } + if (this.updatedBefore != null) { + json[r'updatedBefore'] = this.updatedBefore!.toUtc().toIso8601String(); + } else { + // json[r'updatedBefore'] = null; + } + json[r'withArchived'] = this.withArchived; + if (this.withDeleted != null) { + json[r'withDeleted'] = this.withDeleted; + } else { + // json[r'withDeleted'] = null; + } + if (this.withExif != null) { + json[r'withExif'] = this.withExif; + } else { + // json[r'withExif'] = null; + } + return json; + } + + /// Returns a new [SmartSearchDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SmartSearchDto? fromJson(dynamic value) { + if (value is Map) { + final json = value.cast(); + + return SmartSearchDto( + city: mapValueOfType(json, r'city'), + country: mapValueOfType(json, r'country'), + createdAfter: mapDateTime(json, r'createdAfter', r''), + createdBefore: mapDateTime(json, r'createdBefore', r''), + deviceId: mapValueOfType(json, r'deviceId'), + isArchived: mapValueOfType(json, r'isArchived'), + isEncoded: mapValueOfType(json, r'isEncoded'), + isExternal: mapValueOfType(json, r'isExternal'), + isFavorite: mapValueOfType(json, r'isFavorite'), + isMotion: mapValueOfType(json, r'isMotion'), + isNotInAlbum: mapValueOfType(json, r'isNotInAlbum'), + isOffline: mapValueOfType(json, r'isOffline'), + isReadOnly: mapValueOfType(json, r'isReadOnly'), + isVisible: mapValueOfType(json, r'isVisible'), + lensModel: mapValueOfType(json, r'lensModel'), + libraryId: mapValueOfType(json, r'libraryId'), + make: mapValueOfType(json, r'make'), + model: mapValueOfType(json, r'model'), + page: num.parse('${json[r'page']}'), + personIds: json[r'personIds'] is Iterable + ? (json[r'personIds'] as Iterable).cast().toList(growable: false) + : const [], + query: mapValueOfType(json, r'query')!, + size: num.parse('${json[r'size']}'), + state: mapValueOfType(json, r'state'), + takenAfter: mapDateTime(json, r'takenAfter', r''), + takenBefore: mapDateTime(json, r'takenBefore', r''), + trashedAfter: mapDateTime(json, r'trashedAfter', r''), + trashedBefore: mapDateTime(json, r'trashedBefore', r''), + type: AssetTypeEnum.fromJson(json[r'type']), + updatedAfter: mapDateTime(json, r'updatedAfter', r''), + updatedBefore: mapDateTime(json, r'updatedBefore', r''), + withArchived: mapValueOfType(json, r'withArchived') ?? false, + withDeleted: mapValueOfType(json, r'withDeleted'), + withExif: mapValueOfType(json, r'withExif'), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = SmartSearchDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = SmartSearchDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SmartSearchDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = SmartSearchDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'query', + }; +} + diff --git a/mobile/openapi/lib/model/system_config_library_watch_dto.dart b/mobile/openapi/lib/model/system_config_library_watch_dto.dart index 795fd15fd3..0bcb6f1771 100644 --- a/mobile/openapi/lib/model/system_config_library_watch_dto.dart +++ b/mobile/openapi/lib/model/system_config_library_watch_dto.dart @@ -14,37 +14,25 @@ class SystemConfigLibraryWatchDto { /// Returns a new [SystemConfigLibraryWatchDto] instance. SystemConfigLibraryWatchDto({ required this.enabled, - required this.interval, - required this.usePolling, }); bool enabled; - int interval; - - bool usePolling; - @override bool operator ==(Object other) => identical(this, other) || other is SystemConfigLibraryWatchDto && - other.enabled == enabled && - other.interval == interval && - other.usePolling == usePolling; + other.enabled == enabled; @override int get hashCode => // ignore: unnecessary_parenthesis - (enabled.hashCode) + - (interval.hashCode) + - (usePolling.hashCode); + (enabled.hashCode); @override - String toString() => 'SystemConfigLibraryWatchDto[enabled=$enabled, interval=$interval, usePolling=$usePolling]'; + String toString() => 'SystemConfigLibraryWatchDto[enabled=$enabled]'; Map toJson() { final json = {}; json[r'enabled'] = this.enabled; - json[r'interval'] = this.interval; - json[r'usePolling'] = this.usePolling; return json; } @@ -57,8 +45,6 @@ class SystemConfigLibraryWatchDto { return SystemConfigLibraryWatchDto( enabled: mapValueOfType(json, r'enabled')!, - interval: mapValueOfType(json, r'interval')!, - usePolling: mapValueOfType(json, r'usePolling')!, ); } return null; @@ -107,8 +93,6 @@ class SystemConfigLibraryWatchDto { /// The list of required keys that must be present in a JSON. static const requiredKeys = { 'enabled', - 'interval', - 'usePolling', }; } diff --git a/mobile/openapi/lib/model/system_config_o_auth_dto.dart b/mobile/openapi/lib/model/system_config_o_auth_dto.dart index 603ff8a952..1e7fe7f000 100644 --- a/mobile/openapi/lib/model/system_config_o_auth_dto.dart +++ b/mobile/openapi/lib/model/system_config_o_auth_dto.dart @@ -18,6 +18,7 @@ class SystemConfigOAuthDto { required this.buttonText, required this.clientId, required this.clientSecret, + required this.defaultStorageQuota, required this.enabled, required this.issuerUrl, required this.mobileOverrideEnabled, @@ -25,6 +26,7 @@ class SystemConfigOAuthDto { required this.scope, required this.signingAlgorithm, required this.storageLabelClaim, + required this.storageQuotaClaim, }); bool autoLaunch; @@ -37,6 +39,8 @@ class SystemConfigOAuthDto { String clientSecret; + num defaultStorageQuota; + bool enabled; String issuerUrl; @@ -51,6 +55,8 @@ class SystemConfigOAuthDto { String storageLabelClaim; + String storageQuotaClaim; + @override bool operator ==(Object other) => identical(this, other) || other is SystemConfigOAuthDto && other.autoLaunch == autoLaunch && @@ -58,13 +64,15 @@ class SystemConfigOAuthDto { other.buttonText == buttonText && other.clientId == clientId && other.clientSecret == clientSecret && + other.defaultStorageQuota == defaultStorageQuota && other.enabled == enabled && other.issuerUrl == issuerUrl && other.mobileOverrideEnabled == mobileOverrideEnabled && other.mobileRedirectUri == mobileRedirectUri && other.scope == scope && other.signingAlgorithm == signingAlgorithm && - other.storageLabelClaim == storageLabelClaim; + other.storageLabelClaim == storageLabelClaim && + other.storageQuotaClaim == storageQuotaClaim; @override int get hashCode => @@ -74,16 +82,18 @@ class SystemConfigOAuthDto { (buttonText.hashCode) + (clientId.hashCode) + (clientSecret.hashCode) + + (defaultStorageQuota.hashCode) + (enabled.hashCode) + (issuerUrl.hashCode) + (mobileOverrideEnabled.hashCode) + (mobileRedirectUri.hashCode) + (scope.hashCode) + (signingAlgorithm.hashCode) + - (storageLabelClaim.hashCode); + (storageLabelClaim.hashCode) + + (storageQuotaClaim.hashCode); @override - String toString() => 'SystemConfigOAuthDto[autoLaunch=$autoLaunch, autoRegister=$autoRegister, buttonText=$buttonText, clientId=$clientId, clientSecret=$clientSecret, enabled=$enabled, issuerUrl=$issuerUrl, mobileOverrideEnabled=$mobileOverrideEnabled, mobileRedirectUri=$mobileRedirectUri, scope=$scope, signingAlgorithm=$signingAlgorithm, storageLabelClaim=$storageLabelClaim]'; + String toString() => 'SystemConfigOAuthDto[autoLaunch=$autoLaunch, autoRegister=$autoRegister, buttonText=$buttonText, clientId=$clientId, clientSecret=$clientSecret, defaultStorageQuota=$defaultStorageQuota, enabled=$enabled, issuerUrl=$issuerUrl, mobileOverrideEnabled=$mobileOverrideEnabled, mobileRedirectUri=$mobileRedirectUri, scope=$scope, signingAlgorithm=$signingAlgorithm, storageLabelClaim=$storageLabelClaim, storageQuotaClaim=$storageQuotaClaim]'; Map toJson() { final json = {}; @@ -92,6 +102,7 @@ class SystemConfigOAuthDto { json[r'buttonText'] = this.buttonText; json[r'clientId'] = this.clientId; json[r'clientSecret'] = this.clientSecret; + json[r'defaultStorageQuota'] = this.defaultStorageQuota; json[r'enabled'] = this.enabled; json[r'issuerUrl'] = this.issuerUrl; json[r'mobileOverrideEnabled'] = this.mobileOverrideEnabled; @@ -99,6 +110,7 @@ class SystemConfigOAuthDto { json[r'scope'] = this.scope; json[r'signingAlgorithm'] = this.signingAlgorithm; json[r'storageLabelClaim'] = this.storageLabelClaim; + json[r'storageQuotaClaim'] = this.storageQuotaClaim; return json; } @@ -115,6 +127,7 @@ class SystemConfigOAuthDto { buttonText: mapValueOfType(json, r'buttonText')!, clientId: mapValueOfType(json, r'clientId')!, clientSecret: mapValueOfType(json, r'clientSecret')!, + defaultStorageQuota: num.parse('${json[r'defaultStorageQuota']}'), enabled: mapValueOfType(json, r'enabled')!, issuerUrl: mapValueOfType(json, r'issuerUrl')!, mobileOverrideEnabled: mapValueOfType(json, r'mobileOverrideEnabled')!, @@ -122,6 +135,7 @@ class SystemConfigOAuthDto { scope: mapValueOfType(json, r'scope')!, signingAlgorithm: mapValueOfType(json, r'signingAlgorithm')!, storageLabelClaim: mapValueOfType(json, r'storageLabelClaim')!, + storageQuotaClaim: mapValueOfType(json, r'storageQuotaClaim')!, ); } return null; @@ -174,6 +188,7 @@ class SystemConfigOAuthDto { 'buttonText', 'clientId', 'clientSecret', + 'defaultStorageQuota', 'enabled', 'issuerUrl', 'mobileOverrideEnabled', @@ -181,6 +196,7 @@ class SystemConfigOAuthDto { 'scope', 'signingAlgorithm', 'storageLabelClaim', + 'storageQuotaClaim', }; } diff --git a/mobile/openapi/lib/model/update_user_dto.dart b/mobile/openapi/lib/model/update_user_dto.dart index 5202401787..8fc85b4868 100644 --- a/mobile/openapi/lib/model/update_user_dto.dart +++ b/mobile/openapi/lib/model/update_user_dto.dart @@ -15,7 +15,6 @@ class UpdateUserDto { UpdateUserDto({ this.avatarColor, this.email, - this.externalPath, required this.id, this.isAdmin, this.memoriesEnabled, @@ -42,14 +41,6 @@ class UpdateUserDto { /// String? email; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// - String? externalPath; - String id; /// @@ -106,7 +97,6 @@ class UpdateUserDto { bool operator ==(Object other) => identical(this, other) || other is UpdateUserDto && other.avatarColor == avatarColor && other.email == email && - other.externalPath == externalPath && other.id == id && other.isAdmin == isAdmin && other.memoriesEnabled == memoriesEnabled && @@ -121,7 +111,6 @@ class UpdateUserDto { // ignore: unnecessary_parenthesis (avatarColor == null ? 0 : avatarColor!.hashCode) + (email == null ? 0 : email!.hashCode) + - (externalPath == null ? 0 : externalPath!.hashCode) + (id.hashCode) + (isAdmin == null ? 0 : isAdmin!.hashCode) + (memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) + @@ -132,7 +121,7 @@ class UpdateUserDto { (storageLabel == null ? 0 : storageLabel!.hashCode); @override - String toString() => 'UpdateUserDto[avatarColor=$avatarColor, email=$email, externalPath=$externalPath, id=$id, isAdmin=$isAdmin, memoriesEnabled=$memoriesEnabled, name=$name, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]'; + String toString() => 'UpdateUserDto[avatarColor=$avatarColor, email=$email, id=$id, isAdmin=$isAdmin, memoriesEnabled=$memoriesEnabled, name=$name, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]'; Map toJson() { final json = {}; @@ -145,11 +134,6 @@ class UpdateUserDto { json[r'email'] = this.email; } else { // json[r'email'] = null; - } - if (this.externalPath != null) { - json[r'externalPath'] = this.externalPath; - } else { - // json[r'externalPath'] = null; } json[r'id'] = this.id; if (this.isAdmin != null) { @@ -200,7 +184,6 @@ class UpdateUserDto { return UpdateUserDto( avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), email: mapValueOfType(json, r'email'), - externalPath: mapValueOfType(json, r'externalPath'), id: mapValueOfType(json, r'id')!, isAdmin: mapValueOfType(json, r'isAdmin'), memoriesEnabled: mapValueOfType(json, r'memoriesEnabled'), diff --git a/mobile/openapi/lib/model/user_response_dto.dart b/mobile/openapi/lib/model/user_response_dto.dart index 9da67392bd..d4e0bf07dd 100644 --- a/mobile/openapi/lib/model/user_response_dto.dart +++ b/mobile/openapi/lib/model/user_response_dto.dart @@ -17,7 +17,6 @@ class UserResponseDto { required this.createdAt, required this.deletedAt, required this.email, - required this.externalPath, required this.id, required this.isAdmin, this.memoriesEnabled, @@ -39,8 +38,6 @@ class UserResponseDto { String email; - String? externalPath; - String id; bool isAdmin; @@ -75,7 +72,6 @@ class UserResponseDto { other.createdAt == createdAt && other.deletedAt == deletedAt && other.email == email && - other.externalPath == externalPath && other.id == id && other.isAdmin == isAdmin && other.memoriesEnabled == memoriesEnabled && @@ -95,7 +91,6 @@ class UserResponseDto { (createdAt.hashCode) + (deletedAt == null ? 0 : deletedAt!.hashCode) + (email.hashCode) + - (externalPath == null ? 0 : externalPath!.hashCode) + (id.hashCode) + (isAdmin.hashCode) + (memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) + @@ -109,7 +104,7 @@ class UserResponseDto { (updatedAt.hashCode); @override - String toString() => 'UserResponseDto[avatarColor=$avatarColor, createdAt=$createdAt, deletedAt=$deletedAt, email=$email, externalPath=$externalPath, id=$id, isAdmin=$isAdmin, memoriesEnabled=$memoriesEnabled, name=$name, oauthId=$oauthId, profileImagePath=$profileImagePath, quotaSizeInBytes=$quotaSizeInBytes, quotaUsageInBytes=$quotaUsageInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel, updatedAt=$updatedAt]'; + String toString() => 'UserResponseDto[avatarColor=$avatarColor, createdAt=$createdAt, deletedAt=$deletedAt, email=$email, id=$id, isAdmin=$isAdmin, memoriesEnabled=$memoriesEnabled, name=$name, oauthId=$oauthId, profileImagePath=$profileImagePath, quotaSizeInBytes=$quotaSizeInBytes, quotaUsageInBytes=$quotaUsageInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel, updatedAt=$updatedAt]'; Map toJson() { final json = {}; @@ -121,11 +116,6 @@ class UserResponseDto { // json[r'deletedAt'] = null; } json[r'email'] = this.email; - if (this.externalPath != null) { - json[r'externalPath'] = this.externalPath; - } else { - // json[r'externalPath'] = null; - } json[r'id'] = this.id; json[r'isAdmin'] = this.isAdmin; if (this.memoriesEnabled != null) { @@ -168,7 +158,6 @@ class UserResponseDto { createdAt: mapDateTime(json, r'createdAt', r'')!, deletedAt: mapDateTime(json, r'deletedAt', r''), email: mapValueOfType(json, r'email')!, - externalPath: mapValueOfType(json, r'externalPath'), id: mapValueOfType(json, r'id')!, isAdmin: mapValueOfType(json, r'isAdmin')!, memoriesEnabled: mapValueOfType(json, r'memoriesEnabled'), @@ -231,7 +220,6 @@ class UserResponseDto { 'createdAt', 'deletedAt', 'email', - 'externalPath', 'id', 'isAdmin', 'name', diff --git a/mobile/openapi/lib/model/validate_library_dto.dart b/mobile/openapi/lib/model/validate_library_dto.dart new file mode 100644 index 0000000000..a29a622079 --- /dev/null +++ b/mobile/openapi/lib/model/validate_library_dto.dart @@ -0,0 +1,108 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.12 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class ValidateLibraryDto { + /// Returns a new [ValidateLibraryDto] instance. + ValidateLibraryDto({ + this.exclusionPatterns = const [], + this.importPaths = const [], + }); + + List exclusionPatterns; + + List importPaths; + + @override + bool operator ==(Object other) => identical(this, other) || other is ValidateLibraryDto && + _deepEquality.equals(other.exclusionPatterns, exclusionPatterns) && + _deepEquality.equals(other.importPaths, importPaths); + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (exclusionPatterns.hashCode) + + (importPaths.hashCode); + + @override + String toString() => 'ValidateLibraryDto[exclusionPatterns=$exclusionPatterns, importPaths=$importPaths]'; + + Map toJson() { + final json = {}; + json[r'exclusionPatterns'] = this.exclusionPatterns; + json[r'importPaths'] = this.importPaths; + return json; + } + + /// Returns a new [ValidateLibraryDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static ValidateLibraryDto? fromJson(dynamic value) { + if (value is Map) { + final json = value.cast(); + + return ValidateLibraryDto( + exclusionPatterns: json[r'exclusionPatterns'] is Iterable + ? (json[r'exclusionPatterns'] as Iterable).cast().toList(growable: false) + : const [], + importPaths: json[r'importPaths'] is Iterable + ? (json[r'importPaths'] as Iterable).cast().toList(growable: false) + : const [], + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = ValidateLibraryDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = ValidateLibraryDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of ValidateLibraryDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = ValidateLibraryDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + }; +} + diff --git a/mobile/openapi/lib/model/validate_library_import_path_response_dto.dart b/mobile/openapi/lib/model/validate_library_import_path_response_dto.dart new file mode 100644 index 0000000000..1297c824c2 --- /dev/null +++ b/mobile/openapi/lib/model/validate_library_import_path_response_dto.dart @@ -0,0 +1,122 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.12 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class ValidateLibraryImportPathResponseDto { + /// Returns a new [ValidateLibraryImportPathResponseDto] instance. + ValidateLibraryImportPathResponseDto({ + required this.importPath, + this.isValid = false, + this.message, + }); + + String importPath; + + bool isValid; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? message; + + @override + bool operator ==(Object other) => identical(this, other) || other is ValidateLibraryImportPathResponseDto && + other.importPath == importPath && + other.isValid == isValid && + other.message == message; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (importPath.hashCode) + + (isValid.hashCode) + + (message == null ? 0 : message!.hashCode); + + @override + String toString() => 'ValidateLibraryImportPathResponseDto[importPath=$importPath, isValid=$isValid, message=$message]'; + + Map toJson() { + final json = {}; + json[r'importPath'] = this.importPath; + json[r'isValid'] = this.isValid; + if (this.message != null) { + json[r'message'] = this.message; + } else { + // json[r'message'] = null; + } + return json; + } + + /// Returns a new [ValidateLibraryImportPathResponseDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static ValidateLibraryImportPathResponseDto? fromJson(dynamic value) { + if (value is Map) { + final json = value.cast(); + + return ValidateLibraryImportPathResponseDto( + importPath: mapValueOfType(json, r'importPath')!, + isValid: mapValueOfType(json, r'isValid') ?? false, + message: mapValueOfType(json, r'message'), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = ValidateLibraryImportPathResponseDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = ValidateLibraryImportPathResponseDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of ValidateLibraryImportPathResponseDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = ValidateLibraryImportPathResponseDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'importPath', + }; +} + diff --git a/mobile/openapi/lib/model/validate_library_response_dto.dart b/mobile/openapi/lib/model/validate_library_response_dto.dart new file mode 100644 index 0000000000..bb975da23a --- /dev/null +++ b/mobile/openapi/lib/model/validate_library_response_dto.dart @@ -0,0 +1,97 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.12 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class ValidateLibraryResponseDto { + /// Returns a new [ValidateLibraryResponseDto] instance. + ValidateLibraryResponseDto({ + this.importPaths = const [], + }); + + List importPaths; + + @override + bool operator ==(Object other) => identical(this, other) || other is ValidateLibraryResponseDto && + _deepEquality.equals(other.importPaths, importPaths); + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (importPaths.hashCode); + + @override + String toString() => 'ValidateLibraryResponseDto[importPaths=$importPaths]'; + + Map toJson() { + final json = {}; + json[r'importPaths'] = this.importPaths; + return json; + } + + /// Returns a new [ValidateLibraryResponseDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static ValidateLibraryResponseDto? fromJson(dynamic value) { + if (value is Map) { + final json = value.cast(); + + return ValidateLibraryResponseDto( + importPaths: ValidateLibraryImportPathResponseDto.listFromJson(json[r'importPaths']), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = ValidateLibraryResponseDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = ValidateLibraryResponseDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of ValidateLibraryResponseDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = ValidateLibraryResponseDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + }; +} + diff --git a/mobile/openapi/test/asset_api_test.dart b/mobile/openapi/test/asset_api_test.dart index a7a63f38e6..846a5998cc 100644 --- a/mobile/openapi/test/asset_api_test.dart +++ b/mobile/openapi/test/asset_api_test.dart @@ -80,7 +80,7 @@ void main() { // TODO }); - //Future> getMapMarkers({ DateTime fileCreatedAfter, DateTime fileCreatedBefore, bool isArchived, bool isFavorite }) async + //Future> getMapMarkers({ DateTime fileCreatedAfter, DateTime fileCreatedBefore, bool isArchived, bool isFavorite, bool withPartners }) async test('test getMapMarkers', () async { // TODO }); @@ -110,7 +110,7 @@ void main() { // TODO }); - //Future> searchAssets({ String checksum, String city, String country, DateTime createdAfter, DateTime createdBefore, String deviceAssetId, String deviceId, String encodedVideoPath, String id, bool isArchived, bool isEncoded, bool isExternal, bool isFavorite, bool isMotion, bool isOffline, bool isReadOnly, bool isVisible, String lensModel, String libraryId, String make, String model, AssetOrder order, String originalFileName, String originalPath, num page, String resizePath, num size, String state, DateTime takenAfter, DateTime takenBefore, DateTime trashedAfter, DateTime trashedBefore, AssetTypeEnum type, DateTime updatedAfter, DateTime updatedBefore, String webpPath, bool withArchived, bool withDeleted, bool withExif, bool withPeople, bool withStacked }) async + //Future> searchAssets({ String checksum, String city, String country, DateTime createdAfter, DateTime createdBefore, String deviceAssetId, String deviceId, String encodedVideoPath, String id, bool isArchived, bool isEncoded, bool isExternal, bool isFavorite, bool isMotion, bool isNotInAlbum, bool isOffline, bool isReadOnly, bool isVisible, String lensModel, String libraryId, String make, String model, AssetOrder order, String originalFileName, String originalPath, num page, List personIds, String resizePath, num size, String state, DateTime takenAfter, DateTime takenBefore, DateTime trashedAfter, DateTime trashedBefore, AssetTypeEnum type, DateTime updatedAfter, DateTime updatedBefore, String webpPath, bool withArchived, bool withDeleted, bool withExif, bool withPeople, bool withStacked }) async test('test searchAssets', () async { // TODO }); @@ -135,7 +135,7 @@ void main() { // TODO }); - //Future uploadFile(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String key, String duration, bool isArchived, bool isExternal, bool isFavorite, bool isOffline, bool isReadOnly, bool isVisible, String libraryId, MultipartFile livePhotoData, MultipartFile sidecarData }) async + //Future uploadFile(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String key, String duration, bool isArchived, bool isFavorite, bool isOffline, bool isReadOnly, bool isVisible, String libraryId, MultipartFile livePhotoData, MultipartFile sidecarData }) async test('test uploadFile', () async { // TODO }); diff --git a/mobile/openapi/test/create_library_dto_test.dart b/mobile/openapi/test/create_library_dto_test.dart index dea1d4e631..88911249e7 100644 --- a/mobile/openapi/test/create_library_dto_test.dart +++ b/mobile/openapi/test/create_library_dto_test.dart @@ -41,6 +41,11 @@ void main() { // TODO }); + // String ownerId + test('to test the property `ownerId`', () async { + // TODO + }); + // LibraryType type test('to test the property `type`', () async { // TODO diff --git a/mobile/openapi/test/create_user_dto_test.dart b/mobile/openapi/test/create_user_dto_test.dart index 6614b54a62..9658c02c8a 100644 --- a/mobile/openapi/test/create_user_dto_test.dart +++ b/mobile/openapi/test/create_user_dto_test.dart @@ -21,11 +21,6 @@ void main() { // TODO }); - // String externalPath - test('to test the property `externalPath`', () async { - // TODO - }); - // bool memoriesEnabled test('to test the property `memoriesEnabled`', () async { // TODO diff --git a/mobile/openapi/test/library_api_test.dart b/mobile/openapi/test/library_api_test.dart index 85882edbd1..21afeff544 100644 --- a/mobile/openapi/test/library_api_test.dart +++ b/mobile/openapi/test/library_api_test.dart @@ -27,13 +27,13 @@ void main() { // TODO }); - //Future> getLibraries() async - test('test getLibraries', () async { + //Future> getAllLibraries({ LibraryType type }) async + test('test getAllLibraries', () async { // TODO }); - //Future getLibraryInfo(String id) async - test('test getLibraryInfo', () async { + //Future getLibrary(String id) async + test('test getLibrary', () async { // TODO }); @@ -57,5 +57,10 @@ void main() { // TODO }); + //Future validate(String id, ValidateLibraryDto validateLibraryDto) async + test('test validate', () async { + // TODO + }); + }); } diff --git a/mobile/openapi/test/metadata_search_dto_test.dart b/mobile/openapi/test/metadata_search_dto_test.dart new file mode 100644 index 0000000000..f817b7da74 --- /dev/null +++ b/mobile/openapi/test/metadata_search_dto_test.dart @@ -0,0 +1,237 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.12 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +import 'package:openapi/api.dart'; +import 'package:test/test.dart'; + +// tests for MetadataSearchDto +void main() { + // final instance = MetadataSearchDto(); + + group('test MetadataSearchDto', () { + // String checksum + test('to test the property `checksum`', () async { + // TODO + }); + + // String city + test('to test the property `city`', () async { + // TODO + }); + + // String country + test('to test the property `country`', () async { + // TODO + }); + + // DateTime createdAfter + test('to test the property `createdAfter`', () async { + // TODO + }); + + // DateTime createdBefore + test('to test the property `createdBefore`', () async { + // TODO + }); + + // String deviceAssetId + test('to test the property `deviceAssetId`', () async { + // TODO + }); + + // String deviceId + test('to test the property `deviceId`', () async { + // TODO + }); + + // String encodedVideoPath + test('to test the property `encodedVideoPath`', () async { + // TODO + }); + + // String id + test('to test the property `id`', () async { + // TODO + }); + + // bool isArchived + test('to test the property `isArchived`', () async { + // TODO + }); + + // bool isEncoded + test('to test the property `isEncoded`', () async { + // TODO + }); + + // bool isExternal + test('to test the property `isExternal`', () async { + // TODO + }); + + // bool isFavorite + test('to test the property `isFavorite`', () async { + // TODO + }); + + // bool isMotion + test('to test the property `isMotion`', () async { + // TODO + }); + + // bool isNotInAlbum + test('to test the property `isNotInAlbum`', () async { + // TODO + }); + + // bool isOffline + test('to test the property `isOffline`', () async { + // TODO + }); + + // bool isReadOnly + test('to test the property `isReadOnly`', () async { + // TODO + }); + + // bool isVisible + test('to test the property `isVisible`', () async { + // TODO + }); + + // String lensModel + test('to test the property `lensModel`', () async { + // TODO + }); + + // String libraryId + test('to test the property `libraryId`', () async { + // TODO + }); + + // String make + test('to test the property `make`', () async { + // TODO + }); + + // String model + test('to test the property `model`', () async { + // TODO + }); + + // AssetOrder order + test('to test the property `order`', () async { + // TODO + }); + + // String originalFileName + test('to test the property `originalFileName`', () async { + // TODO + }); + + // String originalPath + test('to test the property `originalPath`', () async { + // TODO + }); + + // num page + test('to test the property `page`', () async { + // TODO + }); + + // List personIds (default value: const []) + test('to test the property `personIds`', () async { + // TODO + }); + + // String resizePath + test('to test the property `resizePath`', () async { + // TODO + }); + + // num size + test('to test the property `size`', () async { + // TODO + }); + + // String state + test('to test the property `state`', () async { + // TODO + }); + + // DateTime takenAfter + test('to test the property `takenAfter`', () async { + // TODO + }); + + // DateTime takenBefore + test('to test the property `takenBefore`', () async { + // TODO + }); + + // DateTime trashedAfter + test('to test the property `trashedAfter`', () async { + // TODO + }); + + // DateTime trashedBefore + test('to test the property `trashedBefore`', () async { + // TODO + }); + + // AssetTypeEnum type + test('to test the property `type`', () async { + // TODO + }); + + // DateTime updatedAfter + test('to test the property `updatedAfter`', () async { + // TODO + }); + + // DateTime updatedBefore + test('to test the property `updatedBefore`', () async { + // TODO + }); + + // String webpPath + test('to test the property `webpPath`', () async { + // TODO + }); + + // bool withArchived (default value: false) + test('to test the property `withArchived`', () async { + // TODO + }); + + // bool withDeleted + test('to test the property `withDeleted`', () async { + // TODO + }); + + // bool withExif + test('to test the property `withExif`', () async { + // TODO + }); + + // bool withPeople + test('to test the property `withPeople`', () async { + // TODO + }); + + // bool withStacked + test('to test the property `withStacked`', () async { + // TODO + }); + + + }); + +} diff --git a/mobile/openapi/test/partner_response_dto_test.dart b/mobile/openapi/test/partner_response_dto_test.dart index d6b7769ab4..7fce31d5eb 100644 --- a/mobile/openapi/test/partner_response_dto_test.dart +++ b/mobile/openapi/test/partner_response_dto_test.dart @@ -36,11 +36,6 @@ void main() { // TODO }); - // String externalPath - test('to test the property `externalPath`', () async { - // TODO - }); - // String id test('to test the property `id`', () async { // TODO diff --git a/mobile/openapi/test/people_response_dto_test.dart b/mobile/openapi/test/people_response_dto_test.dart index ad669eeced..94db6eb86b 100644 --- a/mobile/openapi/test/people_response_dto_test.dart +++ b/mobile/openapi/test/people_response_dto_test.dart @@ -16,6 +16,11 @@ void main() { // final instance = PeopleResponseDto(); group('test PeopleResponseDto', () { + // int hidden + test('to test the property `hidden`', () async { + // TODO + }); + // List people (default value: const []) test('to test the property `people`', () async { // TODO diff --git a/mobile/openapi/test/places_response_dto_test.dart b/mobile/openapi/test/places_response_dto_test.dart new file mode 100644 index 0000000000..5a320fce64 --- /dev/null +++ b/mobile/openapi/test/places_response_dto_test.dart @@ -0,0 +1,47 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.12 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +import 'package:openapi/api.dart'; +import 'package:test/test.dart'; + +// tests for PlacesResponseDto +void main() { + // final instance = PlacesResponseDto(); + + group('test PlacesResponseDto', () { + // String admin1name + test('to test the property `admin1name`', () async { + // TODO + }); + + // String admin2name + test('to test the property `admin2name`', () async { + // TODO + }); + + // num latitude + test('to test the property `latitude`', () async { + // TODO + }); + + // num longitude + test('to test the property `longitude`', () async { + // TODO + }); + + // String name + test('to test the property `name`', () async { + // TODO + }); + + + }); + +} diff --git a/mobile/openapi/test/search_api_test.dart b/mobile/openapi/test/search_api_test.dart index d89c47e748..aa4a94847b 100644 --- a/mobile/openapi/test/search_api_test.dart +++ b/mobile/openapi/test/search_api_test.dart @@ -32,7 +32,7 @@ void main() { // TODO }); - //Future searchMetadata({ String checksum, String city, String country, DateTime createdAfter, DateTime createdBefore, String deviceAssetId, String deviceId, String encodedVideoPath, String id, bool isArchived, bool isEncoded, bool isExternal, bool isFavorite, bool isMotion, bool isOffline, bool isReadOnly, bool isVisible, String lensModel, String libraryId, String make, String model, AssetOrder order, String originalFileName, String originalPath, num page, String resizePath, num size, String state, DateTime takenAfter, DateTime takenBefore, DateTime trashedAfter, DateTime trashedBefore, AssetTypeEnum type, DateTime updatedAfter, DateTime updatedBefore, String webpPath, bool withArchived, bool withDeleted, bool withExif, bool withPeople, bool withStacked }) async + //Future searchMetadata(MetadataSearchDto metadataSearchDto) async test('test searchMetadata', () async { // TODO }); @@ -42,7 +42,12 @@ void main() { // TODO }); - //Future searchSmart(String query, { String city, String country, DateTime createdAfter, DateTime createdBefore, String deviceId, bool isArchived, bool isEncoded, bool isExternal, bool isFavorite, bool isMotion, bool isOffline, bool isReadOnly, bool isVisible, String lensModel, String libraryId, String make, String model, num page, num size, String state, DateTime takenAfter, DateTime takenBefore, DateTime trashedAfter, DateTime trashedBefore, AssetTypeEnum type, DateTime updatedAfter, DateTime updatedBefore, bool withArchived, bool withDeleted, bool withExif }) async + //Future> searchPlaces(String name) async + test('test searchPlaces', () async { + // TODO + }); + + //Future searchSmart(SmartSearchDto smartSearchDto) async test('test searchSmart', () async { // TODO }); diff --git a/mobile/openapi/test/smart_search_dto_test.dart b/mobile/openapi/test/smart_search_dto_test.dart new file mode 100644 index 0000000000..5263f7bb6a --- /dev/null +++ b/mobile/openapi/test/smart_search_dto_test.dart @@ -0,0 +1,187 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.12 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +import 'package:openapi/api.dart'; +import 'package:test/test.dart'; + +// tests for SmartSearchDto +void main() { + // final instance = SmartSearchDto(); + + group('test SmartSearchDto', () { + // String city + test('to test the property `city`', () async { + // TODO + }); + + // String country + test('to test the property `country`', () async { + // TODO + }); + + // DateTime createdAfter + test('to test the property `createdAfter`', () async { + // TODO + }); + + // DateTime createdBefore + test('to test the property `createdBefore`', () async { + // TODO + }); + + // String deviceId + test('to test the property `deviceId`', () async { + // TODO + }); + + // bool isArchived + test('to test the property `isArchived`', () async { + // TODO + }); + + // bool isEncoded + test('to test the property `isEncoded`', () async { + // TODO + }); + + // bool isExternal + test('to test the property `isExternal`', () async { + // TODO + }); + + // bool isFavorite + test('to test the property `isFavorite`', () async { + // TODO + }); + + // bool isMotion + test('to test the property `isMotion`', () async { + // TODO + }); + + // bool isNotInAlbum + test('to test the property `isNotInAlbum`', () async { + // TODO + }); + + // bool isOffline + test('to test the property `isOffline`', () async { + // TODO + }); + + // bool isReadOnly + test('to test the property `isReadOnly`', () async { + // TODO + }); + + // bool isVisible + test('to test the property `isVisible`', () async { + // TODO + }); + + // String lensModel + test('to test the property `lensModel`', () async { + // TODO + }); + + // String libraryId + test('to test the property `libraryId`', () async { + // TODO + }); + + // String make + test('to test the property `make`', () async { + // TODO + }); + + // String model + test('to test the property `model`', () async { + // TODO + }); + + // num page + test('to test the property `page`', () async { + // TODO + }); + + // List personIds (default value: const []) + test('to test the property `personIds`', () async { + // TODO + }); + + // String query + test('to test the property `query`', () async { + // TODO + }); + + // num size + test('to test the property `size`', () async { + // TODO + }); + + // String state + test('to test the property `state`', () async { + // TODO + }); + + // DateTime takenAfter + test('to test the property `takenAfter`', () async { + // TODO + }); + + // DateTime takenBefore + test('to test the property `takenBefore`', () async { + // TODO + }); + + // DateTime trashedAfter + test('to test the property `trashedAfter`', () async { + // TODO + }); + + // DateTime trashedBefore + test('to test the property `trashedBefore`', () async { + // TODO + }); + + // AssetTypeEnum type + test('to test the property `type`', () async { + // TODO + }); + + // DateTime updatedAfter + test('to test the property `updatedAfter`', () async { + // TODO + }); + + // DateTime updatedBefore + test('to test the property `updatedBefore`', () async { + // TODO + }); + + // bool withArchived (default value: false) + test('to test the property `withArchived`', () async { + // TODO + }); + + // bool withDeleted + test('to test the property `withDeleted`', () async { + // TODO + }); + + // bool withExif + test('to test the property `withExif`', () async { + // TODO + }); + + + }); + +} diff --git a/mobile/openapi/test/system_config_library_watch_dto_test.dart b/mobile/openapi/test/system_config_library_watch_dto_test.dart index 74ab53d652..19b5de05fe 100644 --- a/mobile/openapi/test/system_config_library_watch_dto_test.dart +++ b/mobile/openapi/test/system_config_library_watch_dto_test.dart @@ -21,16 +21,6 @@ void main() { // TODO }); - // int interval - test('to test the property `interval`', () async { - // TODO - }); - - // bool usePolling - test('to test the property `usePolling`', () async { - // TODO - }); - }); diff --git a/mobile/openapi/test/system_config_o_auth_dto_test.dart b/mobile/openapi/test/system_config_o_auth_dto_test.dart index 2bcbb64efd..e855ff9608 100644 --- a/mobile/openapi/test/system_config_o_auth_dto_test.dart +++ b/mobile/openapi/test/system_config_o_auth_dto_test.dart @@ -41,6 +41,11 @@ void main() { // TODO }); + // num defaultStorageQuota + test('to test the property `defaultStorageQuota`', () async { + // TODO + }); + // bool enabled test('to test the property `enabled`', () async { // TODO @@ -76,6 +81,11 @@ void main() { // TODO }); + // String storageQuotaClaim + test('to test the property `storageQuotaClaim`', () async { + // TODO + }); + }); diff --git a/mobile/openapi/test/update_user_dto_test.dart b/mobile/openapi/test/update_user_dto_test.dart index aaacfa7cbd..10c506666d 100644 --- a/mobile/openapi/test/update_user_dto_test.dart +++ b/mobile/openapi/test/update_user_dto_test.dart @@ -26,11 +26,6 @@ void main() { // TODO }); - // String externalPath - test('to test the property `externalPath`', () async { - // TODO - }); - // String id test('to test the property `id`', () async { // TODO diff --git a/mobile/openapi/test/user_response_dto_test.dart b/mobile/openapi/test/user_response_dto_test.dart index bef6c812e9..d0fdf97e12 100644 --- a/mobile/openapi/test/user_response_dto_test.dart +++ b/mobile/openapi/test/user_response_dto_test.dart @@ -36,11 +36,6 @@ void main() { // TODO }); - // String externalPath - test('to test the property `externalPath`', () async { - // TODO - }); - // String id test('to test the property `id`', () async { // TODO diff --git a/mobile/openapi/test/validate_library_dto_test.dart b/mobile/openapi/test/validate_library_dto_test.dart new file mode 100644 index 0000000000..8d4922ee4a --- /dev/null +++ b/mobile/openapi/test/validate_library_dto_test.dart @@ -0,0 +1,32 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.12 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +import 'package:openapi/api.dart'; +import 'package:test/test.dart'; + +// tests for ValidateLibraryDto +void main() { + // final instance = ValidateLibraryDto(); + + group('test ValidateLibraryDto', () { + // List exclusionPatterns (default value: const []) + test('to test the property `exclusionPatterns`', () async { + // TODO + }); + + // List importPaths (default value: const []) + test('to test the property `importPaths`', () async { + // TODO + }); + + + }); + +} diff --git a/mobile/openapi/test/validate_library_import_path_response_dto_test.dart b/mobile/openapi/test/validate_library_import_path_response_dto_test.dart new file mode 100644 index 0000000000..a118698a13 --- /dev/null +++ b/mobile/openapi/test/validate_library_import_path_response_dto_test.dart @@ -0,0 +1,37 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.12 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +import 'package:openapi/api.dart'; +import 'package:test/test.dart'; + +// tests for ValidateLibraryImportPathResponseDto +void main() { + // final instance = ValidateLibraryImportPathResponseDto(); + + group('test ValidateLibraryImportPathResponseDto', () { + // String importPath + test('to test the property `importPath`', () async { + // TODO + }); + + // bool isValid (default value: false) + test('to test the property `isValid`', () async { + // TODO + }); + + // String message + test('to test the property `message`', () async { + // TODO + }); + + + }); + +} diff --git a/mobile/openapi/test/validate_library_response_dto_test.dart b/mobile/openapi/test/validate_library_response_dto_test.dart new file mode 100644 index 0000000000..6750809066 --- /dev/null +++ b/mobile/openapi/test/validate_library_response_dto_test.dart @@ -0,0 +1,27 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.12 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +import 'package:openapi/api.dart'; +import 'package:test/test.dart'; + +// tests for ValidateLibraryResponseDto +void main() { + // final instance = ValidateLibraryResponseDto(); + + group('test ValidateLibraryResponseDto', () { + // List importPaths (default value: const []) + test('to test the property `importPaths`', () async { + // TODO + }); + + + }); + +} diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index ffa57f826b..f27351898d 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -569,10 +569,10 @@ packages: dependency: "direct main" description: name: flutter_udid - sha256: "666412097b86d9a6f9803073d0f0ba70de9b198fe6493d89d352a1f8cd6c5c84" + sha256: "63384bd96203aaefccfd7137fab642edda18afede12b0e9e1a2c96fe2589fd07" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" flutter_web_auth: dependency: "direct main" description: @@ -619,10 +619,10 @@ packages: dependency: "direct main" description: name: geolocator - sha256: e946395fc608842bb2f6c914807e9183f86f3cb787f6b8f832753e5251036f02 + sha256: "694ec58afe97787b5b72b8a0ab78c1a9244811c3c10e72c4362ef3c0ceb005cd" url: "https://pub.dev" source: hosted - version: "10.1.0" + version: "11.0.0" geolocator_android: dependency: transitive description: @@ -651,10 +651,10 @@ packages: dependency: transitive description: name: geolocator_web - sha256: "59083f7e0871b78299918d92bf930a14377f711d2d1156c558cd5ebae6c20d58" + sha256: "49d8f846ebeb5e2b6641fe477a7e97e5dd73f03cbfef3fd5c42177b7300fb0ed" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "3.0.0" geolocator_windows: dependency: transitive description: @@ -1298,10 +1298,10 @@ packages: dependency: transitive description: name: shared_preferences_linux - sha256: "71d6806d1449b0a9d4e85e0c7a917771e672a3d5dc61149cc9fac871115018e1" + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.3.2" shared_preferences_platform_interface: dependency: transitive description: @@ -1322,10 +1322,10 @@ packages: dependency: transitive description: name: shared_preferences_windows - sha256: f95e6a43162bce43c9c3405f3eb6f39e5b5d11f65fab19196cf8225e2777624d + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.3.2" shelf: dependency: transitive description: @@ -1467,6 +1467,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.1" + thumbhash: + dependency: "direct main" + description: + name: thumbhash + sha256: "5f6d31c5279ca0b5caa81ec10aae8dcaab098d82cb699ea66ada4ed09c794a37" + url: "https://pub.dev" + source: hosted + version: "0.1.0+1" time: dependency: transitive description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index ddfed62dad..04056977a4 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -2,7 +2,7 @@ name: immich_mobile description: Immich - selfhosted backup media file on mobile phone publish_to: "none" -version: 1.94.1+121 +version: 1.97.0+125 isar_version: &isar_version 3.1.0+1 environment: @@ -32,8 +32,8 @@ dependencies: git: url: https://github.com/maplibre/flutter-maplibre-gl.git ref: acb428a005efd9832a0a8e7ef0945f899dfb3ca5 - geolocator: ^10.1.0 # used to move to current location in map view - flutter_udid: ^2.1.1 + geolocator: ^11.0.0 # used to move to current location in map view + flutter_udid: ^3.0.0 package_info_plus: ^5.0.1 url_launcher: ^6.2.4 http: 0.13.5 @@ -57,6 +57,7 @@ dependencies: flutter_local_notifications: ^16.3.2 timezone: ^0.9.2 octo_image: ^2.0.0 + thumbhash: 0.1.0+1 openapi: path: openapi diff --git a/mobile/scripts/fdroid_build_isar.sh b/mobile/scripts/fdroid_build_isar.sh index f3fe423cb4..44f59c69ae 100644 --- a/mobile/scripts/fdroid_build_isar.sh +++ b/mobile/scripts/fdroid_build_isar.sh @@ -1,4 +1,6 @@ -cd .isar +#!/usr/bin/env sh + +cd .isar || exit bash tool/build_android.sh x86 bash tool/build_android.sh x64 bash tool/build_android.sh armv7 diff --git a/open-api/bin/generate-open-api.sh b/open-api/bin/generate-open-api.sh index 76012383cf..ba62bfd9ee 100755 --- a/open-api/bin/generate-open-api.sh +++ b/open-api/bin/generate-open-api.sh @@ -17,9 +17,7 @@ function dart { } function typescript { - rm -rf ./typescript-sdk/client - npx --yes @openapitools/openapi-generator-cli generate -g typescript-axios -i ./immich-openapi-specs.json -o ./typescript-sdk/axios-client --additional-properties=useSingleRequestParameter=true,supportsES6=true - npx --yes oazapfts --optimistic --argumentStyle=object immich-openapi-specs.json typescript-sdk/fetch-client.ts + npx --yes oazapfts --optimistic --argumentStyle=object --useEnumType immich-openapi-specs.json typescript-sdk/src/fetch-client.ts npm --prefix typescript-sdk ci && npm --prefix typescript-sdk run build } diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 67f6e45c2c..4010336fdb 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -1397,6 +1397,14 @@ "schema": { "type": "boolean" } + }, + { + "name": "withPartners", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } } ], "responses": { @@ -2248,6 +2256,14 @@ "type": "boolean" } }, + { + "name": "isNotInAlbum", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, { "name": "isOffline", "required": false, @@ -2337,6 +2353,17 @@ "type": "number" } }, + { + "name": "personIds", + "required": false, + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, { "name": "resizePath", "required": false, @@ -2436,6 +2463,7 @@ "required": false, "in": "query", "schema": { + "default": false, "type": "boolean" } }, @@ -3271,8 +3299,17 @@ }, "/library": { "get": { - "operationId": "getLibraries", - "parameters": [], + "operationId": "getAllLibraries", + "parameters": [ + { + "name": "type", + "required": false, + "in": "query", + "schema": { + "$ref": "#/components/schemas/LibraryType" + } + } + ], "responses": { "200": { "content": { @@ -3359,7 +3396,7 @@ } ], "responses": { - "200": { + "204": { "description": "" } }, @@ -3379,7 +3416,7 @@ ] }, "get": { - "operationId": "getLibraryInfo", + "operationId": "getLibrary", "parameters": [ { "name": "id", @@ -3484,7 +3521,7 @@ } ], "responses": { - "201": { + "204": { "description": "" } }, @@ -3529,7 +3566,7 @@ "required": true }, "responses": { - "201": { + "204": { "description": "" } }, @@ -3591,6 +3628,58 @@ ] } }, + "/library/{id}/validate": { + "post": { + "operationId": "validate", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ValidateLibraryDto" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ValidateLibraryResponseDto" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Library" + ] + } + }, "/oauth/authorize": { "post": { "operationId": "startOAuth", @@ -4518,350 +4607,21 @@ } }, "/search/metadata": { - "get": { + "post": { "operationId": "searchMetadata", - "parameters": [ - { - "name": "checksum", - "required": false, - "in": "query", - "schema": { - "type": "string" + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataSearchDto" + } } }, - { - "name": "city", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "country", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "createdAfter", - "required": false, - "in": "query", - "schema": { - "format": "date-time", - "type": "string" - } - }, - { - "name": "createdBefore", - "required": false, - "in": "query", - "schema": { - "format": "date-time", - "type": "string" - } - }, - { - "name": "deviceAssetId", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "deviceId", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "encodedVideoPath", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "id", - "required": false, - "in": "query", - "schema": { - "format": "uuid", - "type": "string" - } - }, - { - "name": "isArchived", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "isEncoded", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "isExternal", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "isFavorite", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "isMotion", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "isOffline", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "isReadOnly", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "isVisible", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "lensModel", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "libraryId", - "required": false, - "in": "query", - "schema": { - "format": "uuid", - "type": "string" - } - }, - { - "name": "make", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "model", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "order", - "required": false, - "in": "query", - "schema": { - "$ref": "#/components/schemas/AssetOrder" - } - }, - { - "name": "originalFileName", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "originalPath", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "page", - "required": false, - "in": "query", - "schema": { - "type": "number" - } - }, - { - "name": "resizePath", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "size", - "required": false, - "in": "query", - "schema": { - "type": "number" - } - }, - { - "name": "state", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "takenAfter", - "required": false, - "in": "query", - "schema": { - "format": "date-time", - "type": "string" - } - }, - { - "name": "takenBefore", - "required": false, - "in": "query", - "schema": { - "format": "date-time", - "type": "string" - } - }, - { - "name": "trashedAfter", - "required": false, - "in": "query", - "schema": { - "format": "date-time", - "type": "string" - } - }, - { - "name": "trashedBefore", - "required": false, - "in": "query", - "schema": { - "format": "date-time", - "type": "string" - } - }, - { - "name": "type", - "required": false, - "in": "query", - "schema": { - "$ref": "#/components/schemas/AssetTypeEnum" - } - }, - { - "name": "updatedAfter", - "required": false, - "in": "query", - "schema": { - "format": "date-time", - "type": "string" - } - }, - { - "name": "updatedBefore", - "required": false, - "in": "query", - "schema": { - "format": "date-time", - "type": "string" - } - }, - { - "name": "webpPath", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "withArchived", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "withDeleted", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "withExif", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "withPeople", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "withStacked", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - } - ], + "required": true + }, "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { @@ -4940,270 +4700,66 @@ ] } }, - "/search/smart": { + "/search/places": { "get": { - "operationId": "searchSmart", + "operationId": "searchPlaces", "parameters": [ { - "name": "city", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "country", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "createdAfter", - "required": false, - "in": "query", - "schema": { - "format": "date-time", - "type": "string" - } - }, - { - "name": "createdBefore", - "required": false, - "in": "query", - "schema": { - "format": "date-time", - "type": "string" - } - }, - { - "name": "deviceId", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "isArchived", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "isEncoded", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "isExternal", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "isFavorite", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "isMotion", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "isOffline", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "isReadOnly", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "isVisible", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "lensModel", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "libraryId", - "required": false, - "in": "query", - "schema": { - "format": "uuid", - "type": "string" - } - }, - { - "name": "make", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "model", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "page", - "required": false, - "in": "query", - "schema": { - "type": "number" - } - }, - { - "name": "query", + "name": "name", "required": true, "in": "query", "schema": { "type": "string" } - }, - { - "name": "size", - "required": false, - "in": "query", - "schema": { - "type": "number" - } - }, - { - "name": "state", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "takenAfter", - "required": false, - "in": "query", - "schema": { - "format": "date-time", - "type": "string" - } - }, - { - "name": "takenBefore", - "required": false, - "in": "query", - "schema": { - "format": "date-time", - "type": "string" - } - }, - { - "name": "trashedAfter", - "required": false, - "in": "query", - "schema": { - "format": "date-time", - "type": "string" - } - }, - { - "name": "trashedBefore", - "required": false, - "in": "query", - "schema": { - "format": "date-time", - "type": "string" - } - }, - { - "name": "type", - "required": false, - "in": "query", - "schema": { - "$ref": "#/components/schemas/AssetTypeEnum" - } - }, - { - "name": "updatedAfter", - "required": false, - "in": "query", - "schema": { - "format": "date-time", - "type": "string" - } - }, - { - "name": "updatedBefore", - "required": false, - "in": "query", - "schema": { - "format": "date-time", - "type": "string" - } - }, - { - "name": "withArchived", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "withDeleted", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "withExif", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } } ], "responses": { "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/PlacesResponseDto" + }, + "type": "array" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Search" + ] + } + }, + "/search/smart": { + "post": { + "operationId": "searchSmart", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SmartSearchDto" + } + } + }, + "required": true + }, + "responses": { + "201": { "content": { "application/json": { "schema": { @@ -6911,7 +6467,7 @@ "info": { "title": "Immich", "description": "Immich API", - "version": "1.94.1", + "version": "1.97.0", "contact": {} }, "tags": [], @@ -7985,9 +7541,6 @@ "isArchived": { "type": "boolean" }, - "isExternal": { - "type": "boolean" - }, "isFavorite": { "type": "boolean" }, @@ -8045,6 +7598,10 @@ "name": { "type": "string" }, + "ownerId": { + "format": "uuid", + "type": "string" + }, "type": { "$ref": "#/components/schemas/LibraryType" } @@ -8101,10 +7658,6 @@ "email": { "type": "string" }, - "externalPath": { - "nullable": true, - "type": "string" - }, "memoriesEnabled": { "type": "boolean" }, @@ -8797,6 +8350,154 @@ ], "type": "object" }, + "MetadataSearchDto": { + "properties": { + "checksum": { + "type": "string" + }, + "city": { + "type": "string" + }, + "country": { + "type": "string" + }, + "createdAfter": { + "format": "date-time", + "type": "string" + }, + "createdBefore": { + "format": "date-time", + "type": "string" + }, + "deviceAssetId": { + "type": "string" + }, + "deviceId": { + "type": "string" + }, + "encodedVideoPath": { + "type": "string" + }, + "id": { + "format": "uuid", + "type": "string" + }, + "isArchived": { + "type": "boolean" + }, + "isEncoded": { + "type": "boolean" + }, + "isExternal": { + "type": "boolean" + }, + "isFavorite": { + "type": "boolean" + }, + "isMotion": { + "type": "boolean" + }, + "isNotInAlbum": { + "type": "boolean" + }, + "isOffline": { + "type": "boolean" + }, + "isReadOnly": { + "type": "boolean" + }, + "isVisible": { + "type": "boolean" + }, + "lensModel": { + "type": "string" + }, + "libraryId": { + "format": "uuid", + "type": "string" + }, + "make": { + "type": "string" + }, + "model": { + "type": "string" + }, + "order": { + "$ref": "#/components/schemas/AssetOrder" + }, + "originalFileName": { + "type": "string" + }, + "originalPath": { + "type": "string" + }, + "page": { + "type": "number" + }, + "personIds": { + "items": { + "type": "string" + }, + "type": "array" + }, + "resizePath": { + "type": "string" + }, + "size": { + "type": "number" + }, + "state": { + "type": "string" + }, + "takenAfter": { + "format": "date-time", + "type": "string" + }, + "takenBefore": { + "format": "date-time", + "type": "string" + }, + "trashedAfter": { + "format": "date-time", + "type": "string" + }, + "trashedBefore": { + "format": "date-time", + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/AssetTypeEnum" + }, + "updatedAfter": { + "format": "date-time", + "type": "string" + }, + "updatedBefore": { + "format": "date-time", + "type": "string" + }, + "webpPath": { + "type": "string" + }, + "withArchived": { + "default": false, + "type": "boolean" + }, + "withDeleted": { + "type": "boolean" + }, + "withExif": { + "type": "boolean" + }, + "withPeople": { + "type": "boolean" + }, + "withStacked": { + "type": "boolean" + } + }, + "type": "object" + }, "ModelType": { "enum": [ "facial-recognition", @@ -8854,10 +8555,6 @@ "email": { "type": "string" }, - "externalPath": { - "nullable": true, - "type": "string" - }, "id": { "type": "string" }, @@ -8906,7 +8603,6 @@ "createdAt", "deletedAt", "email", - "externalPath", "id", "isAdmin", "name", @@ -8942,6 +8638,9 @@ }, "PeopleResponseDto": { "properties": { + "hidden": { + "type": "integer" + }, "people": { "items": { "$ref": "#/components/schemas/PersonResponseDto" @@ -8953,6 +8652,7 @@ } }, "required": [ + "hidden", "people", "total" ], @@ -9101,6 +8801,31 @@ ], "type": "object" }, + "PlacesResponseDto": { + "properties": { + "admin1name": { + "type": "string" + }, + "admin2name": { + "type": "string" + }, + "latitude": { + "type": "number" + }, + "longitude": { + "type": "number" + }, + "name": { + "type": "string" + } + }, + "required": [ + "latitude", + "longitude", + "name" + ], + "type": "object" + }, "QueueStatusDto": { "properties": { "isActive": { @@ -9752,6 +9477,126 @@ }, "type": "object" }, + "SmartSearchDto": { + "properties": { + "city": { + "type": "string" + }, + "country": { + "type": "string" + }, + "createdAfter": { + "format": "date-time", + "type": "string" + }, + "createdBefore": { + "format": "date-time", + "type": "string" + }, + "deviceId": { + "type": "string" + }, + "isArchived": { + "type": "boolean" + }, + "isEncoded": { + "type": "boolean" + }, + "isExternal": { + "type": "boolean" + }, + "isFavorite": { + "type": "boolean" + }, + "isMotion": { + "type": "boolean" + }, + "isNotInAlbum": { + "type": "boolean" + }, + "isOffline": { + "type": "boolean" + }, + "isReadOnly": { + "type": "boolean" + }, + "isVisible": { + "type": "boolean" + }, + "lensModel": { + "type": "string" + }, + "libraryId": { + "format": "uuid", + "type": "string" + }, + "make": { + "type": "string" + }, + "model": { + "type": "string" + }, + "page": { + "type": "number" + }, + "personIds": { + "items": { + "type": "string" + }, + "type": "array" + }, + "query": { + "type": "string" + }, + "size": { + "type": "number" + }, + "state": { + "type": "string" + }, + "takenAfter": { + "format": "date-time", + "type": "string" + }, + "takenBefore": { + "format": "date-time", + "type": "string" + }, + "trashedAfter": { + "format": "date-time", + "type": "string" + }, + "trashedBefore": { + "format": "date-time", + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/AssetTypeEnum" + }, + "updatedAfter": { + "format": "date-time", + "type": "string" + }, + "updatedBefore": { + "format": "date-time", + "type": "string" + }, + "withArchived": { + "default": false, + "type": "boolean" + }, + "withDeleted": { + "type": "boolean" + }, + "withExif": { + "type": "boolean" + } + }, + "required": [ + "query" + ], + "type": "object" + }, "SystemConfigDto": { "properties": { "ffmpeg": { @@ -9993,18 +9838,10 @@ "properties": { "enabled": { "type": "boolean" - }, - "interval": { - "type": "integer" - }, - "usePolling": { - "type": "boolean" } }, "required": [ - "enabled", - "interval", - "usePolling" + "enabled" ], "type": "object" }, @@ -10093,6 +9930,9 @@ "clientSecret": { "type": "string" }, + "defaultStorageQuota": { + "type": "number" + }, "enabled": { "type": "boolean" }, @@ -10113,6 +9953,9 @@ }, "storageLabelClaim": { "type": "string" + }, + "storageQuotaClaim": { + "type": "string" } }, "required": [ @@ -10121,13 +9964,15 @@ "buttonText", "clientId", "clientSecret", + "defaultStorageQuota", "enabled", "issuerUrl", "mobileOverrideEnabled", "mobileRedirectUri", "scope", "signingAlgorithm", - "storageLabelClaim" + "storageLabelClaim", + "storageQuotaClaim" ], "type": "object" }, @@ -10496,9 +10341,6 @@ "email": { "type": "string" }, - "externalPath": { - "type": "string" - }, "id": { "format": "uuid", "type": "string" @@ -10625,10 +10467,6 @@ "email": { "type": "string" }, - "externalPath": { - "nullable": true, - "type": "string" - }, "id": { "type": "string" }, @@ -10674,7 +10512,6 @@ "createdAt", "deletedAt", "email", - "externalPath", "id", "isAdmin", "name", @@ -10699,6 +10536,52 @@ ], "type": "object" }, + "ValidateLibraryDto": { + "properties": { + "exclusionPatterns": { + "items": { + "type": "string" + }, + "type": "array" + }, + "importPaths": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "ValidateLibraryImportPathResponseDto": { + "properties": { + "importPath": { + "type": "string" + }, + "isValid": { + "default": false, + "type": "boolean" + }, + "message": { + "type": "string" + } + }, + "required": [ + "importPath" + ], + "type": "object" + }, + "ValidateLibraryResponseDto": { + "properties": { + "importPaths": { + "items": { + "$ref": "#/components/schemas/ValidateLibraryImportPathResponseDto" + }, + "type": "array" + } + }, + "type": "object" + }, "VideoCodec": { "enum": [ "h264", diff --git a/open-api/typescript-sdk/axios-client/.gitignore b/open-api/typescript-sdk/axios-client/.gitignore deleted file mode 100644 index 149b576547..0000000000 --- a/open-api/typescript-sdk/axios-client/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -wwwroot/*.js -node_modules -typings -dist diff --git a/open-api/typescript-sdk/axios-client/.npmignore b/open-api/typescript-sdk/axios-client/.npmignore deleted file mode 100644 index 999d88df69..0000000000 --- a/open-api/typescript-sdk/axios-client/.npmignore +++ /dev/null @@ -1 +0,0 @@ -# empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm \ No newline at end of file diff --git a/open-api/typescript-sdk/axios-client/.openapi-generator-ignore b/open-api/typescript-sdk/axios-client/.openapi-generator-ignore deleted file mode 100644 index 7484ee590a..0000000000 --- a/open-api/typescript-sdk/axios-client/.openapi-generator-ignore +++ /dev/null @@ -1,23 +0,0 @@ -# OpenAPI Generator Ignore -# Generated by openapi-generator https://github.com/openapitools/openapi-generator - -# Use this file to prevent files from being overwritten by the generator. -# The patterns follow closely to .gitignore or .dockerignore. - -# As an example, the C# client generator defines ApiClient.cs. -# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: -#ApiClient.cs - -# You can match any string of characters against a directory, file or extension with a single asterisk (*): -#foo/*/qux -# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux - -# You can recursively match patterns against a directory, file or extension with a double asterisk (**): -#foo/**/qux -# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux - -# You can also negate patterns with an exclamation (!). -# For example, you can ignore all files in a docs folder with the file extension .md: -#docs/*.md -# Then explicitly reverse the ignore rule for a single file: -#!docs/README.md diff --git a/open-api/typescript-sdk/axios-client/.openapi-generator/FILES b/open-api/typescript-sdk/axios-client/.openapi-generator/FILES deleted file mode 100644 index a80cd4f07b..0000000000 --- a/open-api/typescript-sdk/axios-client/.openapi-generator/FILES +++ /dev/null @@ -1,8 +0,0 @@ -.gitignore -.npmignore -api.ts -base.ts -common.ts -configuration.ts -git_push.sh -index.ts diff --git a/open-api/typescript-sdk/axios-client/.openapi-generator/VERSION b/open-api/typescript-sdk/axios-client/.openapi-generator/VERSION deleted file mode 100644 index 4b49d9bb63..0000000000 --- a/open-api/typescript-sdk/axios-client/.openapi-generator/VERSION +++ /dev/null @@ -1 +0,0 @@ -7.2.0 \ No newline at end of file diff --git a/open-api/typescript-sdk/axios-client/api.ts b/open-api/typescript-sdk/axios-client/api.ts deleted file mode 100644 index 80e9588c12..0000000000 --- a/open-api/typescript-sdk/axios-client/api.ts +++ /dev/null @@ -1,19937 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -/** - * Immich - * Immich API - * - * The version of the OpenAPI document: 1.94.1 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -import type { Configuration } from './configuration'; -import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios'; -import globalAxios from 'axios'; -// Some imports not used depending on template conditions -// @ts-ignore -import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from './common'; -import type { RequestArgs } from './base'; -// @ts-ignore -import { BASE_PATH, COLLECTION_FORMATS, BaseAPI, RequiredError, operationServerMap } from './base'; - -/** - * - * @export - * @interface APIKeyCreateDto - */ -export interface APIKeyCreateDto { - /** - * - * @type {string} - * @memberof APIKeyCreateDto - */ - 'name'?: string; -} -/** - * - * @export - * @interface APIKeyCreateResponseDto - */ -export interface APIKeyCreateResponseDto { - /** - * - * @type {APIKeyResponseDto} - * @memberof APIKeyCreateResponseDto - */ - 'apiKey': APIKeyResponseDto; - /** - * - * @type {string} - * @memberof APIKeyCreateResponseDto - */ - 'secret': string; -} -/** - * - * @export - * @interface APIKeyResponseDto - */ -export interface APIKeyResponseDto { - /** - * - * @type {string} - * @memberof APIKeyResponseDto - */ - 'createdAt': string; - /** - * - * @type {string} - * @memberof APIKeyResponseDto - */ - 'id': string; - /** - * - * @type {string} - * @memberof APIKeyResponseDto - */ - 'name': string; - /** - * - * @type {string} - * @memberof APIKeyResponseDto - */ - 'updatedAt': string; -} -/** - * - * @export - * @interface APIKeyUpdateDto - */ -export interface APIKeyUpdateDto { - /** - * - * @type {string} - * @memberof APIKeyUpdateDto - */ - 'name': string; -} -/** - * - * @export - * @interface ActivityCreateDto - */ -export interface ActivityCreateDto { - /** - * - * @type {string} - * @memberof ActivityCreateDto - */ - 'albumId': string; - /** - * - * @type {string} - * @memberof ActivityCreateDto - */ - 'assetId'?: string; - /** - * - * @type {string} - * @memberof ActivityCreateDto - */ - 'comment'?: string; - /** - * - * @type {ReactionType} - * @memberof ActivityCreateDto - */ - 'type': ReactionType; -} - - -/** - * - * @export - * @interface ActivityResponseDto - */ -export interface ActivityResponseDto { - /** - * - * @type {string} - * @memberof ActivityResponseDto - */ - 'assetId': string | null; - /** - * - * @type {string} - * @memberof ActivityResponseDto - */ - 'comment'?: string | null; - /** - * - * @type {string} - * @memberof ActivityResponseDto - */ - 'createdAt': string; - /** - * - * @type {string} - * @memberof ActivityResponseDto - */ - 'id': string; - /** - * - * @type {string} - * @memberof ActivityResponseDto - */ - 'type': ActivityResponseDtoTypeEnum; - /** - * - * @type {UserDto} - * @memberof ActivityResponseDto - */ - 'user': UserDto; -} - -export const ActivityResponseDtoTypeEnum = { - Comment: 'comment', - Like: 'like' -} as const; - -export type ActivityResponseDtoTypeEnum = typeof ActivityResponseDtoTypeEnum[keyof typeof ActivityResponseDtoTypeEnum]; - -/** - * - * @export - * @interface ActivityStatisticsResponseDto - */ -export interface ActivityStatisticsResponseDto { - /** - * - * @type {number} - * @memberof ActivityStatisticsResponseDto - */ - 'comments': number; -} -/** - * - * @export - * @interface AddUsersDto - */ -export interface AddUsersDto { - /** - * - * @type {Array} - * @memberof AddUsersDto - */ - 'sharedUserIds': Array; -} -/** - * - * @export - * @interface AlbumCountResponseDto - */ -export interface AlbumCountResponseDto { - /** - * - * @type {number} - * @memberof AlbumCountResponseDto - */ - 'notShared': number; - /** - * - * @type {number} - * @memberof AlbumCountResponseDto - */ - 'owned': number; - /** - * - * @type {number} - * @memberof AlbumCountResponseDto - */ - 'shared': number; -} -/** - * - * @export - * @interface AlbumResponseDto - */ -export interface AlbumResponseDto { - /** - * - * @type {string} - * @memberof AlbumResponseDto - */ - 'albumName': string; - /** - * - * @type {string} - * @memberof AlbumResponseDto - */ - 'albumThumbnailAssetId': string | null; - /** - * - * @type {number} - * @memberof AlbumResponseDto - */ - 'assetCount': number; - /** - * - * @type {Array} - * @memberof AlbumResponseDto - */ - 'assets': Array; - /** - * - * @type {string} - * @memberof AlbumResponseDto - */ - 'createdAt': string; - /** - * - * @type {string} - * @memberof AlbumResponseDto - */ - 'description': string; - /** - * - * @type {string} - * @memberof AlbumResponseDto - */ - 'endDate'?: string; - /** - * - * @type {boolean} - * @memberof AlbumResponseDto - */ - 'hasSharedLink': boolean; - /** - * - * @type {string} - * @memberof AlbumResponseDto - */ - 'id': string; - /** - * - * @type {boolean} - * @memberof AlbumResponseDto - */ - 'isActivityEnabled': boolean; - /** - * - * @type {string} - * @memberof AlbumResponseDto - */ - 'lastModifiedAssetTimestamp'?: string; - /** - * - * @type {UserResponseDto} - * @memberof AlbumResponseDto - */ - 'owner': UserResponseDto; - /** - * - * @type {string} - * @memberof AlbumResponseDto - */ - 'ownerId': string; - /** - * - * @type {boolean} - * @memberof AlbumResponseDto - */ - 'shared': boolean; - /** - * - * @type {Array} - * @memberof AlbumResponseDto - */ - 'sharedUsers': Array; - /** - * - * @type {string} - * @memberof AlbumResponseDto - */ - 'startDate'?: string; - /** - * - * @type {string} - * @memberof AlbumResponseDto - */ - 'updatedAt': string; -} -/** - * - * @export - * @interface AllJobStatusResponseDto - */ -export interface AllJobStatusResponseDto { - /** - * - * @type {JobStatusDto} - * @memberof AllJobStatusResponseDto - */ - 'backgroundTask': JobStatusDto; - /** - * - * @type {JobStatusDto} - * @memberof AllJobStatusResponseDto - */ - 'faceDetection': JobStatusDto; - /** - * - * @type {JobStatusDto} - * @memberof AllJobStatusResponseDto - */ - 'facialRecognition': JobStatusDto; - /** - * - * @type {JobStatusDto} - * @memberof AllJobStatusResponseDto - */ - 'library': JobStatusDto; - /** - * - * @type {JobStatusDto} - * @memberof AllJobStatusResponseDto - */ - 'metadataExtraction': JobStatusDto; - /** - * - * @type {JobStatusDto} - * @memberof AllJobStatusResponseDto - */ - 'migration': JobStatusDto; - /** - * - * @type {JobStatusDto} - * @memberof AllJobStatusResponseDto - */ - 'search': JobStatusDto; - /** - * - * @type {JobStatusDto} - * @memberof AllJobStatusResponseDto - */ - 'sidecar': JobStatusDto; - /** - * - * @type {JobStatusDto} - * @memberof AllJobStatusResponseDto - */ - 'smartSearch': JobStatusDto; - /** - * - * @type {JobStatusDto} - * @memberof AllJobStatusResponseDto - */ - 'storageTemplateMigration': JobStatusDto; - /** - * - * @type {JobStatusDto} - * @memberof AllJobStatusResponseDto - */ - 'thumbnailGeneration': JobStatusDto; - /** - * - * @type {JobStatusDto} - * @memberof AllJobStatusResponseDto - */ - 'videoConversion': JobStatusDto; -} -/** - * - * @export - * @interface AssetBulkDeleteDto - */ -export interface AssetBulkDeleteDto { - /** - * - * @type {boolean} - * @memberof AssetBulkDeleteDto - */ - 'force'?: boolean; - /** - * - * @type {Array} - * @memberof AssetBulkDeleteDto - */ - 'ids': Array; -} -/** - * - * @export - * @interface AssetBulkUpdateDto - */ -export interface AssetBulkUpdateDto { - /** - * - * @type {string} - * @memberof AssetBulkUpdateDto - */ - 'dateTimeOriginal'?: string; - /** - * - * @type {Array} - * @memberof AssetBulkUpdateDto - */ - 'ids': Array; - /** - * - * @type {boolean} - * @memberof AssetBulkUpdateDto - */ - 'isArchived'?: boolean; - /** - * - * @type {boolean} - * @memberof AssetBulkUpdateDto - */ - 'isFavorite'?: boolean; - /** - * - * @type {number} - * @memberof AssetBulkUpdateDto - */ - 'latitude'?: number; - /** - * - * @type {number} - * @memberof AssetBulkUpdateDto - */ - 'longitude'?: number; - /** - * - * @type {boolean} - * @memberof AssetBulkUpdateDto - */ - 'removeParent'?: boolean; - /** - * - * @type {string} - * @memberof AssetBulkUpdateDto - */ - 'stackParentId'?: string; -} -/** - * - * @export - * @interface AssetBulkUploadCheckDto - */ -export interface AssetBulkUploadCheckDto { - /** - * - * @type {Array} - * @memberof AssetBulkUploadCheckDto - */ - 'assets': Array; -} -/** - * - * @export - * @interface AssetBulkUploadCheckItem - */ -export interface AssetBulkUploadCheckItem { - /** - * base64 or hex encoded sha1 hash - * @type {string} - * @memberof AssetBulkUploadCheckItem - */ - 'checksum': string; - /** - * - * @type {string} - * @memberof AssetBulkUploadCheckItem - */ - 'id': string; -} -/** - * - * @export - * @interface AssetBulkUploadCheckResponseDto - */ -export interface AssetBulkUploadCheckResponseDto { - /** - * - * @type {Array} - * @memberof AssetBulkUploadCheckResponseDto - */ - 'results': Array; -} -/** - * - * @export - * @interface AssetBulkUploadCheckResult - */ -export interface AssetBulkUploadCheckResult { - /** - * - * @type {string} - * @memberof AssetBulkUploadCheckResult - */ - 'action': AssetBulkUploadCheckResultActionEnum; - /** - * - * @type {string} - * @memberof AssetBulkUploadCheckResult - */ - 'assetId'?: string; - /** - * - * @type {string} - * @memberof AssetBulkUploadCheckResult - */ - 'id': string; - /** - * - * @type {string} - * @memberof AssetBulkUploadCheckResult - */ - 'reason'?: AssetBulkUploadCheckResultReasonEnum; -} - -export const AssetBulkUploadCheckResultActionEnum = { - Accept: 'accept', - Reject: 'reject' -} as const; - -export type AssetBulkUploadCheckResultActionEnum = typeof AssetBulkUploadCheckResultActionEnum[keyof typeof AssetBulkUploadCheckResultActionEnum]; -export const AssetBulkUploadCheckResultReasonEnum = { - Duplicate: 'duplicate', - UnsupportedFormat: 'unsupported-format' -} as const; - -export type AssetBulkUploadCheckResultReasonEnum = typeof AssetBulkUploadCheckResultReasonEnum[keyof typeof AssetBulkUploadCheckResultReasonEnum]; - -/** - * - * @export - * @interface AssetFaceResponseDto - */ -export interface AssetFaceResponseDto { - /** - * - * @type {number} - * @memberof AssetFaceResponseDto - */ - 'boundingBoxX1': number; - /** - * - * @type {number} - * @memberof AssetFaceResponseDto - */ - 'boundingBoxX2': number; - /** - * - * @type {number} - * @memberof AssetFaceResponseDto - */ - 'boundingBoxY1': number; - /** - * - * @type {number} - * @memberof AssetFaceResponseDto - */ - 'boundingBoxY2': number; - /** - * - * @type {string} - * @memberof AssetFaceResponseDto - */ - 'id': string; - /** - * - * @type {number} - * @memberof AssetFaceResponseDto - */ - 'imageHeight': number; - /** - * - * @type {number} - * @memberof AssetFaceResponseDto - */ - 'imageWidth': number; - /** - * - * @type {PersonResponseDto} - * @memberof AssetFaceResponseDto - */ - 'person': PersonResponseDto | null; -} -/** - * - * @export - * @interface AssetFaceUpdateDto - */ -export interface AssetFaceUpdateDto { - /** - * - * @type {Array} - * @memberof AssetFaceUpdateDto - */ - 'data': Array; -} -/** - * - * @export - * @interface AssetFaceUpdateItem - */ -export interface AssetFaceUpdateItem { - /** - * - * @type {string} - * @memberof AssetFaceUpdateItem - */ - 'assetId': string; - /** - * - * @type {string} - * @memberof AssetFaceUpdateItem - */ - 'personId': string; -} -/** - * - * @export - * @interface AssetFaceWithoutPersonResponseDto - */ -export interface AssetFaceWithoutPersonResponseDto { - /** - * - * @type {number} - * @memberof AssetFaceWithoutPersonResponseDto - */ - 'boundingBoxX1': number; - /** - * - * @type {number} - * @memberof AssetFaceWithoutPersonResponseDto - */ - 'boundingBoxX2': number; - /** - * - * @type {number} - * @memberof AssetFaceWithoutPersonResponseDto - */ - 'boundingBoxY1': number; - /** - * - * @type {number} - * @memberof AssetFaceWithoutPersonResponseDto - */ - 'boundingBoxY2': number; - /** - * - * @type {string} - * @memberof AssetFaceWithoutPersonResponseDto - */ - 'id': string; - /** - * - * @type {number} - * @memberof AssetFaceWithoutPersonResponseDto - */ - 'imageHeight': number; - /** - * - * @type {number} - * @memberof AssetFaceWithoutPersonResponseDto - */ - 'imageWidth': number; -} -/** - * - * @export - * @interface AssetFileUploadResponseDto - */ -export interface AssetFileUploadResponseDto { - /** - * - * @type {boolean} - * @memberof AssetFileUploadResponseDto - */ - 'duplicate': boolean; - /** - * - * @type {string} - * @memberof AssetFileUploadResponseDto - */ - 'id': string; -} -/** - * - * @export - * @interface AssetIdsDto - */ -export interface AssetIdsDto { - /** - * - * @type {Array} - * @memberof AssetIdsDto - */ - 'assetIds': Array; -} -/** - * - * @export - * @interface AssetIdsResponseDto - */ -export interface AssetIdsResponseDto { - /** - * - * @type {string} - * @memberof AssetIdsResponseDto - */ - 'assetId': string; - /** - * - * @type {string} - * @memberof AssetIdsResponseDto - */ - 'error'?: AssetIdsResponseDtoErrorEnum; - /** - * - * @type {boolean} - * @memberof AssetIdsResponseDto - */ - 'success': boolean; -} - -export const AssetIdsResponseDtoErrorEnum = { - Duplicate: 'duplicate', - NoPermission: 'no_permission', - NotFound: 'not_found' -} as const; - -export type AssetIdsResponseDtoErrorEnum = typeof AssetIdsResponseDtoErrorEnum[keyof typeof AssetIdsResponseDtoErrorEnum]; - -/** - * - * @export - * @enum {string} - */ - -export const AssetJobName = { - RegenerateThumbnail: 'regenerate-thumbnail', - RefreshMetadata: 'refresh-metadata', - TranscodeVideo: 'transcode-video' -} as const; - -export type AssetJobName = typeof AssetJobName[keyof typeof AssetJobName]; - - -/** - * - * @export - * @interface AssetJobsDto - */ -export interface AssetJobsDto { - /** - * - * @type {Array} - * @memberof AssetJobsDto - */ - 'assetIds': Array; - /** - * - * @type {AssetJobName} - * @memberof AssetJobsDto - */ - 'name': AssetJobName; -} - - -/** - * - * @export - * @enum {string} - */ - -export const AssetOrder = { - Asc: 'asc', - Desc: 'desc' -} as const; - -export type AssetOrder = typeof AssetOrder[keyof typeof AssetOrder]; - - -/** - * - * @export - * @interface AssetResponseDto - */ -export interface AssetResponseDto { - /** - * base64 encoded sha1 hash - * @type {string} - * @memberof AssetResponseDto - */ - 'checksum': string; - /** - * - * @type {string} - * @memberof AssetResponseDto - */ - 'deviceAssetId': string; - /** - * - * @type {string} - * @memberof AssetResponseDto - */ - 'deviceId': string; - /** - * - * @type {string} - * @memberof AssetResponseDto - */ - 'duration': string; - /** - * - * @type {ExifResponseDto} - * @memberof AssetResponseDto - */ - 'exifInfo'?: ExifResponseDto; - /** - * - * @type {string} - * @memberof AssetResponseDto - */ - 'fileCreatedAt': string; - /** - * - * @type {string} - * @memberof AssetResponseDto - */ - 'fileModifiedAt': string; - /** - * - * @type {boolean} - * @memberof AssetResponseDto - */ - 'hasMetadata': boolean; - /** - * - * @type {string} - * @memberof AssetResponseDto - */ - 'id': string; - /** - * - * @type {boolean} - * @memberof AssetResponseDto - */ - 'isArchived': boolean; - /** - * - * @type {boolean} - * @memberof AssetResponseDto - */ - 'isExternal': boolean; - /** - * - * @type {boolean} - * @memberof AssetResponseDto - */ - 'isFavorite': boolean; - /** - * - * @type {boolean} - * @memberof AssetResponseDto - */ - 'isOffline': boolean; - /** - * - * @type {boolean} - * @memberof AssetResponseDto - */ - 'isReadOnly': boolean; - /** - * - * @type {boolean} - * @memberof AssetResponseDto - */ - 'isTrashed': boolean; - /** - * - * @type {string} - * @memberof AssetResponseDto - */ - 'libraryId': string; - /** - * - * @type {string} - * @memberof AssetResponseDto - */ - 'livePhotoVideoId'?: string | null; - /** - * - * @type {string} - * @memberof AssetResponseDto - */ - 'localDateTime': string; - /** - * - * @type {string} - * @memberof AssetResponseDto - */ - 'originalFileName': string; - /** - * - * @type {string} - * @memberof AssetResponseDto - */ - 'originalPath': string; - /** - * - * @type {UserResponseDto} - * @memberof AssetResponseDto - */ - 'owner'?: UserResponseDto; - /** - * - * @type {string} - * @memberof AssetResponseDto - */ - 'ownerId': string; - /** - * - * @type {Array} - * @memberof AssetResponseDto - */ - 'people'?: Array; - /** - * - * @type {boolean} - * @memberof AssetResponseDto - */ - 'resized': boolean; - /** - * - * @type {SmartInfoResponseDto} - * @memberof AssetResponseDto - */ - 'smartInfo'?: SmartInfoResponseDto; - /** - * - * @type {Array} - * @memberof AssetResponseDto - */ - 'stack'?: Array; - /** - * - * @type {number} - * @memberof AssetResponseDto - */ - 'stackCount': number | null; - /** - * - * @type {string} - * @memberof AssetResponseDto - */ - 'stackParentId'?: string | null; - /** - * - * @type {Array} - * @memberof AssetResponseDto - */ - 'tags'?: Array; - /** - * - * @type {string} - * @memberof AssetResponseDto - */ - 'thumbhash': string | null; - /** - * - * @type {AssetTypeEnum} - * @memberof AssetResponseDto - */ - 'type': AssetTypeEnum; - /** - * - * @type {string} - * @memberof AssetResponseDto - */ - 'updatedAt': string; -} - - -/** - * - * @export - * @interface AssetStatsResponseDto - */ -export interface AssetStatsResponseDto { - /** - * - * @type {number} - * @memberof AssetStatsResponseDto - */ - 'images': number; - /** - * - * @type {number} - * @memberof AssetStatsResponseDto - */ - 'total': number; - /** - * - * @type {number} - * @memberof AssetStatsResponseDto - */ - 'videos': number; -} -/** - * - * @export - * @enum {string} - */ - -export const AssetTypeEnum = { - Image: 'IMAGE', - Video: 'VIDEO', - Audio: 'AUDIO', - Other: 'OTHER' -} as const; - -export type AssetTypeEnum = typeof AssetTypeEnum[keyof typeof AssetTypeEnum]; - - -/** - * - * @export - * @enum {string} - */ - -export const AudioCodec = { - Mp3: 'mp3', - Aac: 'aac', - Libopus: 'libopus' -} as const; - -export type AudioCodec = typeof AudioCodec[keyof typeof AudioCodec]; - - -/** - * - * @export - * @interface AuditDeletesResponseDto - */ -export interface AuditDeletesResponseDto { - /** - * - * @type {Array} - * @memberof AuditDeletesResponseDto - */ - 'ids': Array; - /** - * - * @type {boolean} - * @memberof AuditDeletesResponseDto - */ - 'needsFullSync': boolean; -} -/** - * - * @export - * @interface AuthDeviceResponseDto - */ -export interface AuthDeviceResponseDto { - /** - * - * @type {string} - * @memberof AuthDeviceResponseDto - */ - 'createdAt': string; - /** - * - * @type {boolean} - * @memberof AuthDeviceResponseDto - */ - 'current': boolean; - /** - * - * @type {string} - * @memberof AuthDeviceResponseDto - */ - 'deviceOS': string; - /** - * - * @type {string} - * @memberof AuthDeviceResponseDto - */ - 'deviceType': string; - /** - * - * @type {string} - * @memberof AuthDeviceResponseDto - */ - 'id': string; - /** - * - * @type {string} - * @memberof AuthDeviceResponseDto - */ - 'updatedAt': string; -} -/** - * - * @export - * @interface BulkIdResponseDto - */ -export interface BulkIdResponseDto { - /** - * - * @type {string} - * @memberof BulkIdResponseDto - */ - 'error'?: BulkIdResponseDtoErrorEnum; - /** - * - * @type {string} - * @memberof BulkIdResponseDto - */ - 'id': string; - /** - * - * @type {boolean} - * @memberof BulkIdResponseDto - */ - 'success': boolean; -} - -export const BulkIdResponseDtoErrorEnum = { - Duplicate: 'duplicate', - NoPermission: 'no_permission', - NotFound: 'not_found', - Unknown: 'unknown' -} as const; - -export type BulkIdResponseDtoErrorEnum = typeof BulkIdResponseDtoErrorEnum[keyof typeof BulkIdResponseDtoErrorEnum]; - -/** - * - * @export - * @interface BulkIdsDto - */ -export interface BulkIdsDto { - /** - * - * @type {Array} - * @memberof BulkIdsDto - */ - 'ids': Array; -} -/** - * - * @export - * @interface CLIPConfig - */ -export interface CLIPConfig { - /** - * - * @type {boolean} - * @memberof CLIPConfig - */ - 'enabled': boolean; - /** - * - * @type {CLIPMode} - * @memberof CLIPConfig - */ - 'mode'?: CLIPMode; - /** - * - * @type {string} - * @memberof CLIPConfig - */ - 'modelName': string; - /** - * - * @type {ModelType} - * @memberof CLIPConfig - */ - 'modelType'?: ModelType; -} - - -/** - * - * @export - * @enum {string} - */ - -export const CLIPMode = { - Vision: 'vision', - Text: 'text' -} as const; - -export type CLIPMode = typeof CLIPMode[keyof typeof CLIPMode]; - - -/** - * - * @export - * @enum {string} - */ - -export const CQMode = { - Auto: 'auto', - Cqp: 'cqp', - Icq: 'icq' -} as const; - -export type CQMode = typeof CQMode[keyof typeof CQMode]; - - -/** - * - * @export - * @interface ChangePasswordDto - */ -export interface ChangePasswordDto { - /** - * - * @type {string} - * @memberof ChangePasswordDto - */ - 'newPassword': string; - /** - * - * @type {string} - * @memberof ChangePasswordDto - */ - 'password': string; -} -/** - * - * @export - * @interface CheckExistingAssetsDto - */ -export interface CheckExistingAssetsDto { - /** - * - * @type {Array} - * @memberof CheckExistingAssetsDto - */ - 'deviceAssetIds': Array; - /** - * - * @type {string} - * @memberof CheckExistingAssetsDto - */ - 'deviceId': string; -} -/** - * - * @export - * @interface CheckExistingAssetsResponseDto - */ -export interface CheckExistingAssetsResponseDto { - /** - * - * @type {Array} - * @memberof CheckExistingAssetsResponseDto - */ - 'existingIds': Array; -} -/** - * - * @export - * @enum {string} - */ - -export const Colorspace = { - Srgb: 'srgb', - P3: 'p3' -} as const; - -export type Colorspace = typeof Colorspace[keyof typeof Colorspace]; - - -/** - * - * @export - * @interface CreateAlbumDto - */ -export interface CreateAlbumDto { - /** - * - * @type {string} - * @memberof CreateAlbumDto - */ - 'albumName': string; - /** - * - * @type {Array} - * @memberof CreateAlbumDto - */ - 'assetIds'?: Array; - /** - * - * @type {string} - * @memberof CreateAlbumDto - */ - 'description'?: string; - /** - * - * @type {Array} - * @memberof CreateAlbumDto - */ - 'sharedWithUserIds'?: Array; -} -/** - * - * @export - * @interface CreateLibraryDto - */ -export interface CreateLibraryDto { - /** - * - * @type {Array} - * @memberof CreateLibraryDto - */ - 'exclusionPatterns'?: Array; - /** - * - * @type {Array} - * @memberof CreateLibraryDto - */ - 'importPaths'?: Array; - /** - * - * @type {boolean} - * @memberof CreateLibraryDto - */ - 'isVisible'?: boolean; - /** - * - * @type {boolean} - * @memberof CreateLibraryDto - */ - 'isWatched'?: boolean; - /** - * - * @type {string} - * @memberof CreateLibraryDto - */ - 'name'?: string; - /** - * - * @type {LibraryType} - * @memberof CreateLibraryDto - */ - 'type': LibraryType; -} - - -/** - * - * @export - * @interface CreateProfileImageResponseDto - */ -export interface CreateProfileImageResponseDto { - /** - * - * @type {string} - * @memberof CreateProfileImageResponseDto - */ - 'profileImagePath': string; - /** - * - * @type {string} - * @memberof CreateProfileImageResponseDto - */ - 'userId': string; -} -/** - * - * @export - * @interface CreateTagDto - */ -export interface CreateTagDto { - /** - * - * @type {string} - * @memberof CreateTagDto - */ - 'name': string; - /** - * - * @type {TagTypeEnum} - * @memberof CreateTagDto - */ - 'type': TagTypeEnum; -} - - -/** - * - * @export - * @interface CreateUserDto - */ -export interface CreateUserDto { - /** - * - * @type {string} - * @memberof CreateUserDto - */ - 'email': string; - /** - * - * @type {string} - * @memberof CreateUserDto - */ - 'externalPath'?: string | null; - /** - * - * @type {boolean} - * @memberof CreateUserDto - */ - 'memoriesEnabled'?: boolean; - /** - * - * @type {string} - * @memberof CreateUserDto - */ - 'name': string; - /** - * - * @type {string} - * @memberof CreateUserDto - */ - 'password': string; - /** - * - * @type {number} - * @memberof CreateUserDto - */ - 'quotaSizeInBytes'?: number | null; - /** - * - * @type {string} - * @memberof CreateUserDto - */ - 'storageLabel'?: string | null; -} -/** - * - * @export - * @interface CuratedLocationsResponseDto - */ -export interface CuratedLocationsResponseDto { - /** - * - * @type {string} - * @memberof CuratedLocationsResponseDto - */ - 'city': string; - /** - * - * @type {string} - * @memberof CuratedLocationsResponseDto - */ - 'deviceAssetId': string; - /** - * - * @type {string} - * @memberof CuratedLocationsResponseDto - */ - 'deviceId': string; - /** - * - * @type {string} - * @memberof CuratedLocationsResponseDto - */ - 'id': string; - /** - * - * @type {string} - * @memberof CuratedLocationsResponseDto - */ - 'resizePath': string; -} -/** - * - * @export - * @interface CuratedObjectsResponseDto - */ -export interface CuratedObjectsResponseDto { - /** - * - * @type {string} - * @memberof CuratedObjectsResponseDto - */ - 'deviceAssetId': string; - /** - * - * @type {string} - * @memberof CuratedObjectsResponseDto - */ - 'deviceId': string; - /** - * - * @type {string} - * @memberof CuratedObjectsResponseDto - */ - 'id': string; - /** - * - * @type {string} - * @memberof CuratedObjectsResponseDto - */ - 'object': string; - /** - * - * @type {string} - * @memberof CuratedObjectsResponseDto - */ - 'resizePath': string; -} -/** - * - * @export - * @interface DownloadArchiveInfo - */ -export interface DownloadArchiveInfo { - /** - * - * @type {Array} - * @memberof DownloadArchiveInfo - */ - 'assetIds': Array; - /** - * - * @type {number} - * @memberof DownloadArchiveInfo - */ - 'size': number; -} -/** - * - * @export - * @interface DownloadInfoDto - */ -export interface DownloadInfoDto { - /** - * - * @type {string} - * @memberof DownloadInfoDto - */ - 'albumId'?: string; - /** - * - * @type {number} - * @memberof DownloadInfoDto - */ - 'archiveSize'?: number; - /** - * - * @type {Array} - * @memberof DownloadInfoDto - */ - 'assetIds'?: Array; - /** - * - * @type {string} - * @memberof DownloadInfoDto - */ - 'userId'?: string; -} -/** - * - * @export - * @interface DownloadResponseDto - */ -export interface DownloadResponseDto { - /** - * - * @type {Array} - * @memberof DownloadResponseDto - */ - 'archives': Array; - /** - * - * @type {number} - * @memberof DownloadResponseDto - */ - 'totalSize': number; -} -/** - * - * @export - * @enum {string} - */ - -export const EntityType = { - Asset: 'ASSET', - Album: 'ALBUM' -} as const; - -export type EntityType = typeof EntityType[keyof typeof EntityType]; - - -/** - * - * @export - * @interface ExifResponseDto - */ -export interface ExifResponseDto { - /** - * - * @type {string} - * @memberof ExifResponseDto - */ - 'city'?: string | null; - /** - * - * @type {string} - * @memberof ExifResponseDto - */ - 'country'?: string | null; - /** - * - * @type {string} - * @memberof ExifResponseDto - */ - 'dateTimeOriginal'?: string | null; - /** - * - * @type {string} - * @memberof ExifResponseDto - */ - 'description'?: string | null; - /** - * - * @type {number} - * @memberof ExifResponseDto - */ - 'exifImageHeight'?: number | null; - /** - * - * @type {number} - * @memberof ExifResponseDto - */ - 'exifImageWidth'?: number | null; - /** - * - * @type {string} - * @memberof ExifResponseDto - */ - 'exposureTime'?: string | null; - /** - * - * @type {number} - * @memberof ExifResponseDto - */ - 'fNumber'?: number | null; - /** - * - * @type {number} - * @memberof ExifResponseDto - */ - 'fileSizeInByte'?: number | null; - /** - * - * @type {number} - * @memberof ExifResponseDto - */ - 'focalLength'?: number | null; - /** - * - * @type {number} - * @memberof ExifResponseDto - */ - 'iso'?: number | null; - /** - * - * @type {number} - * @memberof ExifResponseDto - */ - 'latitude'?: number | null; - /** - * - * @type {string} - * @memberof ExifResponseDto - */ - 'lensModel'?: string | null; - /** - * - * @type {number} - * @memberof ExifResponseDto - */ - 'longitude'?: number | null; - /** - * - * @type {string} - * @memberof ExifResponseDto - */ - 'make'?: string | null; - /** - * - * @type {string} - * @memberof ExifResponseDto - */ - 'model'?: string | null; - /** - * - * @type {string} - * @memberof ExifResponseDto - */ - 'modifyDate'?: string | null; - /** - * - * @type {string} - * @memberof ExifResponseDto - */ - 'orientation'?: string | null; - /** - * - * @type {string} - * @memberof ExifResponseDto - */ - 'projectionType'?: string | null; - /** - * - * @type {string} - * @memberof ExifResponseDto - */ - 'state'?: string | null; - /** - * - * @type {string} - * @memberof ExifResponseDto - */ - 'timeZone'?: string | null; -} -/** - * - * @export - * @interface FaceDto - */ -export interface FaceDto { - /** - * - * @type {string} - * @memberof FaceDto - */ - 'id': string; -} -/** - * - * @export - * @interface FileChecksumDto - */ -export interface FileChecksumDto { - /** - * - * @type {Array} - * @memberof FileChecksumDto - */ - 'filenames': Array; -} -/** - * - * @export - * @interface FileChecksumResponseDto - */ -export interface FileChecksumResponseDto { - /** - * - * @type {string} - * @memberof FileChecksumResponseDto - */ - 'checksum': string; - /** - * - * @type {string} - * @memberof FileChecksumResponseDto - */ - 'filename': string; -} -/** - * - * @export - * @interface FileReportDto - */ -export interface FileReportDto { - /** - * - * @type {Array} - * @memberof FileReportDto - */ - 'extras': Array; - /** - * - * @type {Array} - * @memberof FileReportDto - */ - 'orphans': Array; -} -/** - * - * @export - * @interface FileReportFixDto - */ -export interface FileReportFixDto { - /** - * - * @type {Array} - * @memberof FileReportFixDto - */ - 'items': Array; -} -/** - * - * @export - * @interface FileReportItemDto - */ -export interface FileReportItemDto { - /** - * - * @type {string} - * @memberof FileReportItemDto - */ - 'checksum'?: string; - /** - * - * @type {string} - * @memberof FileReportItemDto - */ - 'entityId': string; - /** - * - * @type {PathEntityType} - * @memberof FileReportItemDto - */ - 'entityType': PathEntityType; - /** - * - * @type {PathType} - * @memberof FileReportItemDto - */ - 'pathType': PathType; - /** - * - * @type {string} - * @memberof FileReportItemDto - */ - 'pathValue': string; -} - - -/** - * - * @export - * @enum {string} - */ - -export const JobCommand = { - Start: 'start', - Pause: 'pause', - Resume: 'resume', - Empty: 'empty', - ClearFailed: 'clear-failed' -} as const; - -export type JobCommand = typeof JobCommand[keyof typeof JobCommand]; - - -/** - * - * @export - * @interface JobCommandDto - */ -export interface JobCommandDto { - /** - * - * @type {JobCommand} - * @memberof JobCommandDto - */ - 'command': JobCommand; - /** - * - * @type {boolean} - * @memberof JobCommandDto - */ - 'force': boolean; -} - - -/** - * - * @export - * @interface JobCountsDto - */ -export interface JobCountsDto { - /** - * - * @type {number} - * @memberof JobCountsDto - */ - 'active': number; - /** - * - * @type {number} - * @memberof JobCountsDto - */ - 'completed': number; - /** - * - * @type {number} - * @memberof JobCountsDto - */ - 'delayed': number; - /** - * - * @type {number} - * @memberof JobCountsDto - */ - 'failed': number; - /** - * - * @type {number} - * @memberof JobCountsDto - */ - 'paused': number; - /** - * - * @type {number} - * @memberof JobCountsDto - */ - 'waiting': number; -} -/** - * - * @export - * @enum {string} - */ - -export const JobName = { - ThumbnailGeneration: 'thumbnailGeneration', - MetadataExtraction: 'metadataExtraction', - VideoConversion: 'videoConversion', - FaceDetection: 'faceDetection', - FacialRecognition: 'facialRecognition', - SmartSearch: 'smartSearch', - BackgroundTask: 'backgroundTask', - StorageTemplateMigration: 'storageTemplateMigration', - Migration: 'migration', - Search: 'search', - Sidecar: 'sidecar', - Library: 'library' -} as const; - -export type JobName = typeof JobName[keyof typeof JobName]; - - -/** - * - * @export - * @interface JobSettingsDto - */ -export interface JobSettingsDto { - /** - * - * @type {number} - * @memberof JobSettingsDto - */ - 'concurrency': number; -} -/** - * - * @export - * @interface JobStatusDto - */ -export interface JobStatusDto { - /** - * - * @type {JobCountsDto} - * @memberof JobStatusDto - */ - 'jobCounts': JobCountsDto; - /** - * - * @type {QueueStatusDto} - * @memberof JobStatusDto - */ - 'queueStatus': QueueStatusDto; -} -/** - * - * @export - * @interface LibraryResponseDto - */ -export interface LibraryResponseDto { - /** - * - * @type {number} - * @memberof LibraryResponseDto - */ - 'assetCount': number; - /** - * - * @type {string} - * @memberof LibraryResponseDto - */ - 'createdAt': string; - /** - * - * @type {Array} - * @memberof LibraryResponseDto - */ - 'exclusionPatterns': Array; - /** - * - * @type {string} - * @memberof LibraryResponseDto - */ - 'id': string; - /** - * - * @type {Array} - * @memberof LibraryResponseDto - */ - 'importPaths': Array; - /** - * - * @type {string} - * @memberof LibraryResponseDto - */ - 'name': string; - /** - * - * @type {string} - * @memberof LibraryResponseDto - */ - 'ownerId': string; - /** - * - * @type {string} - * @memberof LibraryResponseDto - */ - 'refreshedAt': string | null; - /** - * - * @type {LibraryType} - * @memberof LibraryResponseDto - */ - 'type': LibraryType; - /** - * - * @type {string} - * @memberof LibraryResponseDto - */ - 'updatedAt': string; -} - - -/** - * - * @export - * @interface LibraryStatsResponseDto - */ -export interface LibraryStatsResponseDto { - /** - * - * @type {number} - * @memberof LibraryStatsResponseDto - */ - 'photos': number; - /** - * - * @type {number} - * @memberof LibraryStatsResponseDto - */ - 'total': number; - /** - * - * @type {number} - * @memberof LibraryStatsResponseDto - */ - 'usage': number; - /** - * - * @type {number} - * @memberof LibraryStatsResponseDto - */ - 'videos': number; -} -/** - * - * @export - * @enum {string} - */ - -export const LibraryType = { - Upload: 'UPLOAD', - External: 'EXTERNAL' -} as const; - -export type LibraryType = typeof LibraryType[keyof typeof LibraryType]; - - -/** - * - * @export - * @enum {string} - */ - -export const LogLevel = { - Verbose: 'verbose', - Debug: 'debug', - Log: 'log', - Warn: 'warn', - Error: 'error', - Fatal: 'fatal' -} as const; - -export type LogLevel = typeof LogLevel[keyof typeof LogLevel]; - - -/** - * - * @export - * @interface LoginCredentialDto - */ -export interface LoginCredentialDto { - /** - * - * @type {string} - * @memberof LoginCredentialDto - */ - 'email': string; - /** - * - * @type {string} - * @memberof LoginCredentialDto - */ - 'password': string; -} -/** - * - * @export - * @interface LoginResponseDto - */ -export interface LoginResponseDto { - /** - * - * @type {string} - * @memberof LoginResponseDto - */ - 'accessToken': string; - /** - * - * @type {boolean} - * @memberof LoginResponseDto - */ - 'isAdmin': boolean; - /** - * - * @type {string} - * @memberof LoginResponseDto - */ - 'name': string; - /** - * - * @type {string} - * @memberof LoginResponseDto - */ - 'profileImagePath': string; - /** - * - * @type {boolean} - * @memberof LoginResponseDto - */ - 'shouldChangePassword': boolean; - /** - * - * @type {string} - * @memberof LoginResponseDto - */ - 'userEmail': string; - /** - * - * @type {string} - * @memberof LoginResponseDto - */ - 'userId': string; -} -/** - * - * @export - * @interface LogoutResponseDto - */ -export interface LogoutResponseDto { - /** - * - * @type {string} - * @memberof LogoutResponseDto - */ - 'redirectUri': string; - /** - * - * @type {boolean} - * @memberof LogoutResponseDto - */ - 'successful': boolean; -} -/** - * - * @export - * @interface MapMarkerResponseDto - */ -export interface MapMarkerResponseDto { - /** - * - * @type {string} - * @memberof MapMarkerResponseDto - */ - 'id': string; - /** - * - * @type {number} - * @memberof MapMarkerResponseDto - */ - 'lat': number; - /** - * - * @type {number} - * @memberof MapMarkerResponseDto - */ - 'lon': number; -} -/** - * - * @export - * @enum {string} - */ - -export const MapTheme = { - Light: 'light', - Dark: 'dark' -} as const; - -export type MapTheme = typeof MapTheme[keyof typeof MapTheme]; - - -/** - * - * @export - * @interface MemoryLaneResponseDto - */ -export interface MemoryLaneResponseDto { - /** - * - * @type {Array} - * @memberof MemoryLaneResponseDto - */ - 'assets': Array; - /** - * - * @type {string} - * @memberof MemoryLaneResponseDto - */ - 'title': string; -} -/** - * - * @export - * @interface MergePersonDto - */ -export interface MergePersonDto { - /** - * - * @type {Array} - * @memberof MergePersonDto - */ - 'ids': Array; -} -/** - * - * @export - * @enum {string} - */ - -export const ModelType = { - FacialRecognition: 'facial-recognition', - Clip: 'clip' -} as const; - -export type ModelType = typeof ModelType[keyof typeof ModelType]; - - -/** - * - * @export - * @interface OAuthAuthorizeResponseDto - */ -export interface OAuthAuthorizeResponseDto { - /** - * - * @type {string} - * @memberof OAuthAuthorizeResponseDto - */ - 'url': string; -} -/** - * - * @export - * @interface OAuthCallbackDto - */ -export interface OAuthCallbackDto { - /** - * - * @type {string} - * @memberof OAuthCallbackDto - */ - 'url': string; -} -/** - * - * @export - * @interface OAuthConfigDto - */ -export interface OAuthConfigDto { - /** - * - * @type {string} - * @memberof OAuthConfigDto - */ - 'redirectUri': string; -} -/** - * - * @export - * @interface PartnerResponseDto - */ -export interface PartnerResponseDto { - /** - * - * @type {UserAvatarColor} - * @memberof PartnerResponseDto - */ - 'avatarColor': UserAvatarColor; - /** - * - * @type {string} - * @memberof PartnerResponseDto - */ - 'createdAt': string; - /** - * - * @type {string} - * @memberof PartnerResponseDto - */ - 'deletedAt': string | null; - /** - * - * @type {string} - * @memberof PartnerResponseDto - */ - 'email': string; - /** - * - * @type {string} - * @memberof PartnerResponseDto - */ - 'externalPath': string | null; - /** - * - * @type {string} - * @memberof PartnerResponseDto - */ - 'id': string; - /** - * - * @type {boolean} - * @memberof PartnerResponseDto - */ - 'inTimeline'?: boolean; - /** - * - * @type {boolean} - * @memberof PartnerResponseDto - */ - 'isAdmin': boolean; - /** - * - * @type {boolean} - * @memberof PartnerResponseDto - */ - 'memoriesEnabled'?: boolean; - /** - * - * @type {string} - * @memberof PartnerResponseDto - */ - 'name': string; - /** - * - * @type {string} - * @memberof PartnerResponseDto - */ - 'oauthId': string; - /** - * - * @type {string} - * @memberof PartnerResponseDto - */ - 'profileImagePath': string; - /** - * - * @type {number} - * @memberof PartnerResponseDto - */ - 'quotaSizeInBytes': number | null; - /** - * - * @type {number} - * @memberof PartnerResponseDto - */ - 'quotaUsageInBytes': number | null; - /** - * - * @type {boolean} - * @memberof PartnerResponseDto - */ - 'shouldChangePassword': boolean; - /** - * - * @type {string} - * @memberof PartnerResponseDto - */ - 'storageLabel': string | null; - /** - * - * @type {string} - * @memberof PartnerResponseDto - */ - 'updatedAt': string; -} - - -/** - * - * @export - * @enum {string} - */ - -export const PathEntityType = { - Asset: 'asset', - Person: 'person', - User: 'user' -} as const; - -export type PathEntityType = typeof PathEntityType[keyof typeof PathEntityType]; - - -/** - * - * @export - * @enum {string} - */ - -export const PathType = { - Original: 'original', - JpegThumbnail: 'jpeg_thumbnail', - WebpThumbnail: 'webp_thumbnail', - EncodedVideo: 'encoded_video', - Sidecar: 'sidecar', - Face: 'face', - Profile: 'profile' -} as const; - -export type PathType = typeof PathType[keyof typeof PathType]; - - -/** - * - * @export - * @interface PeopleResponseDto - */ -export interface PeopleResponseDto { - /** - * - * @type {Array} - * @memberof PeopleResponseDto - */ - 'people': Array; - /** - * - * @type {number} - * @memberof PeopleResponseDto - */ - 'total': number; -} -/** - * - * @export - * @interface PeopleUpdateDto - */ -export interface PeopleUpdateDto { - /** - * - * @type {Array} - * @memberof PeopleUpdateDto - */ - 'people': Array; -} -/** - * - * @export - * @interface PeopleUpdateItem - */ -export interface PeopleUpdateItem { - /** - * Person date of birth. Note: the mobile app cannot currently set the birth date to null. - * @type {string} - * @memberof PeopleUpdateItem - */ - 'birthDate'?: string | null; - /** - * Asset is used to get the feature face thumbnail. - * @type {string} - * @memberof PeopleUpdateItem - */ - 'featureFaceAssetId'?: string; - /** - * Person id. - * @type {string} - * @memberof PeopleUpdateItem - */ - 'id': string; - /** - * Person visibility - * @type {boolean} - * @memberof PeopleUpdateItem - */ - 'isHidden'?: boolean; - /** - * Person name. - * @type {string} - * @memberof PeopleUpdateItem - */ - 'name'?: string; -} -/** - * - * @export - * @interface PersonResponseDto - */ -export interface PersonResponseDto { - /** - * - * @type {string} - * @memberof PersonResponseDto - */ - 'birthDate': string | null; - /** - * - * @type {string} - * @memberof PersonResponseDto - */ - 'id': string; - /** - * - * @type {boolean} - * @memberof PersonResponseDto - */ - 'isHidden': boolean; - /** - * - * @type {string} - * @memberof PersonResponseDto - */ - 'name': string; - /** - * - * @type {string} - * @memberof PersonResponseDto - */ - 'thumbnailPath': string; -} -/** - * - * @export - * @interface PersonStatisticsResponseDto - */ -export interface PersonStatisticsResponseDto { - /** - * - * @type {number} - * @memberof PersonStatisticsResponseDto - */ - 'assets': number; -} -/** - * - * @export - * @interface PersonUpdateDto - */ -export interface PersonUpdateDto { - /** - * Person date of birth. Note: the mobile app cannot currently set the birth date to null. - * @type {string} - * @memberof PersonUpdateDto - */ - 'birthDate'?: string | null; - /** - * Asset is used to get the feature face thumbnail. - * @type {string} - * @memberof PersonUpdateDto - */ - 'featureFaceAssetId'?: string; - /** - * Person visibility - * @type {boolean} - * @memberof PersonUpdateDto - */ - 'isHidden'?: boolean; - /** - * Person name. - * @type {string} - * @memberof PersonUpdateDto - */ - 'name'?: string; -} -/** - * - * @export - * @interface PersonWithFacesResponseDto - */ -export interface PersonWithFacesResponseDto { - /** - * - * @type {string} - * @memberof PersonWithFacesResponseDto - */ - 'birthDate': string | null; - /** - * - * @type {Array} - * @memberof PersonWithFacesResponseDto - */ - 'faces': Array; - /** - * - * @type {string} - * @memberof PersonWithFacesResponseDto - */ - 'id': string; - /** - * - * @type {boolean} - * @memberof PersonWithFacesResponseDto - */ - 'isHidden': boolean; - /** - * - * @type {string} - * @memberof PersonWithFacesResponseDto - */ - 'name': string; - /** - * - * @type {string} - * @memberof PersonWithFacesResponseDto - */ - 'thumbnailPath': string; -} -/** - * - * @export - * @interface QueueStatusDto - */ -export interface QueueStatusDto { - /** - * - * @type {boolean} - * @memberof QueueStatusDto - */ - 'isActive': boolean; - /** - * - * @type {boolean} - * @memberof QueueStatusDto - */ - 'isPaused': boolean; -} -/** - * - * @export - * @enum {string} - */ - -export const ReactionLevel = { - Album: 'album', - Asset: 'asset' -} as const; - -export type ReactionLevel = typeof ReactionLevel[keyof typeof ReactionLevel]; - - -/** - * - * @export - * @enum {string} - */ - -export const ReactionType = { - Comment: 'comment', - Like: 'like' -} as const; - -export type ReactionType = typeof ReactionType[keyof typeof ReactionType]; - - -/** - * - * @export - * @interface RecognitionConfig - */ -export interface RecognitionConfig { - /** - * - * @type {boolean} - * @memberof RecognitionConfig - */ - 'enabled': boolean; - /** - * - * @type {number} - * @memberof RecognitionConfig - */ - 'maxDistance': number; - /** - * - * @type {number} - * @memberof RecognitionConfig - */ - 'minFaces': number; - /** - * - * @type {number} - * @memberof RecognitionConfig - */ - 'minScore': number; - /** - * - * @type {string} - * @memberof RecognitionConfig - */ - 'modelName': string; - /** - * - * @type {ModelType} - * @memberof RecognitionConfig - */ - 'modelType'?: ModelType; -} - - -/** - * - * @export - * @interface ScanLibraryDto - */ -export interface ScanLibraryDto { - /** - * - * @type {boolean} - * @memberof ScanLibraryDto - */ - 'refreshAllFiles'?: boolean; - /** - * - * @type {boolean} - * @memberof ScanLibraryDto - */ - 'refreshModifiedFiles'?: boolean; -} -/** - * - * @export - * @interface SearchAlbumResponseDto - */ -export interface SearchAlbumResponseDto { - /** - * - * @type {number} - * @memberof SearchAlbumResponseDto - */ - 'count': number; - /** - * - * @type {Array} - * @memberof SearchAlbumResponseDto - */ - 'facets': Array; - /** - * - * @type {Array} - * @memberof SearchAlbumResponseDto - */ - 'items': Array; - /** - * - * @type {number} - * @memberof SearchAlbumResponseDto - */ - 'total': number; -} -/** - * - * @export - * @interface SearchAssetResponseDto - */ -export interface SearchAssetResponseDto { - /** - * - * @type {number} - * @memberof SearchAssetResponseDto - */ - 'count': number; - /** - * - * @type {Array} - * @memberof SearchAssetResponseDto - */ - 'facets': Array; - /** - * - * @type {Array} - * @memberof SearchAssetResponseDto - */ - 'items': Array; - /** - * - * @type {string} - * @memberof SearchAssetResponseDto - */ - 'nextPage': string | null; - /** - * - * @type {number} - * @memberof SearchAssetResponseDto - */ - 'total': number; -} -/** - * - * @export - * @interface SearchExploreItem - */ -export interface SearchExploreItem { - /** - * - * @type {AssetResponseDto} - * @memberof SearchExploreItem - */ - 'data': AssetResponseDto; - /** - * - * @type {string} - * @memberof SearchExploreItem - */ - 'value': string; -} -/** - * - * @export - * @interface SearchExploreResponseDto - */ -export interface SearchExploreResponseDto { - /** - * - * @type {string} - * @memberof SearchExploreResponseDto - */ - 'fieldName': string; - /** - * - * @type {Array} - * @memberof SearchExploreResponseDto - */ - 'items': Array; -} -/** - * - * @export - * @interface SearchFacetCountResponseDto - */ -export interface SearchFacetCountResponseDto { - /** - * - * @type {number} - * @memberof SearchFacetCountResponseDto - */ - 'count': number; - /** - * - * @type {string} - * @memberof SearchFacetCountResponseDto - */ - 'value': string; -} -/** - * - * @export - * @interface SearchFacetResponseDto - */ -export interface SearchFacetResponseDto { - /** - * - * @type {Array} - * @memberof SearchFacetResponseDto - */ - 'counts': Array; - /** - * - * @type {string} - * @memberof SearchFacetResponseDto - */ - 'fieldName': string; -} -/** - * - * @export - * @interface SearchResponseDto - */ -export interface SearchResponseDto { - /** - * - * @type {SearchAlbumResponseDto} - * @memberof SearchResponseDto - */ - 'albums': SearchAlbumResponseDto; - /** - * - * @type {SearchAssetResponseDto} - * @memberof SearchResponseDto - */ - 'assets': SearchAssetResponseDto; -} -/** - * - * @export - * @enum {string} - */ - -export const SearchSuggestionType = { - Country: 'country', - State: 'state', - City: 'city', - CameraMake: 'camera-make', - CameraModel: 'camera-model' -} as const; - -export type SearchSuggestionType = typeof SearchSuggestionType[keyof typeof SearchSuggestionType]; - - -/** - * - * @export - * @interface ServerConfigDto - */ -export interface ServerConfigDto { - /** - * - * @type {string} - * @memberof ServerConfigDto - */ - 'externalDomain': string; - /** - * - * @type {boolean} - * @memberof ServerConfigDto - */ - 'isInitialized': boolean; - /** - * - * @type {boolean} - * @memberof ServerConfigDto - */ - 'isOnboarded': boolean; - /** - * - * @type {string} - * @memberof ServerConfigDto - */ - 'loginPageMessage': string; - /** - * - * @type {string} - * @memberof ServerConfigDto - */ - 'oauthButtonText': string; - /** - * - * @type {number} - * @memberof ServerConfigDto - */ - 'trashDays': number; -} -/** - * - * @export - * @interface ServerFeaturesDto - */ -export interface ServerFeaturesDto { - /** - * - * @type {boolean} - * @memberof ServerFeaturesDto - */ - 'configFile': boolean; - /** - * - * @type {boolean} - * @memberof ServerFeaturesDto - */ - 'facialRecognition': boolean; - /** - * - * @type {boolean} - * @memberof ServerFeaturesDto - */ - 'map': boolean; - /** - * - * @type {boolean} - * @memberof ServerFeaturesDto - */ - 'oauth': boolean; - /** - * - * @type {boolean} - * @memberof ServerFeaturesDto - */ - 'oauthAutoLaunch': boolean; - /** - * - * @type {boolean} - * @memberof ServerFeaturesDto - */ - 'passwordLogin': boolean; - /** - * - * @type {boolean} - * @memberof ServerFeaturesDto - */ - 'reverseGeocoding': boolean; - /** - * - * @type {boolean} - * @memberof ServerFeaturesDto - */ - 'search': boolean; - /** - * - * @type {boolean} - * @memberof ServerFeaturesDto - */ - 'sidecar': boolean; - /** - * - * @type {boolean} - * @memberof ServerFeaturesDto - */ - 'smartSearch': boolean; - /** - * - * @type {boolean} - * @memberof ServerFeaturesDto - */ - 'trash': boolean; -} -/** - * - * @export - * @interface ServerInfoResponseDto - */ -export interface ServerInfoResponseDto { - /** - * - * @type {string} - * @memberof ServerInfoResponseDto - */ - 'diskAvailable': string; - /** - * - * @type {number} - * @memberof ServerInfoResponseDto - */ - 'diskAvailableRaw': number; - /** - * - * @type {string} - * @memberof ServerInfoResponseDto - */ - 'diskSize': string; - /** - * - * @type {number} - * @memberof ServerInfoResponseDto - */ - 'diskSizeRaw': number; - /** - * - * @type {number} - * @memberof ServerInfoResponseDto - */ - 'diskUsagePercentage': number; - /** - * - * @type {string} - * @memberof ServerInfoResponseDto - */ - 'diskUse': string; - /** - * - * @type {number} - * @memberof ServerInfoResponseDto - */ - 'diskUseRaw': number; -} -/** - * - * @export - * @interface ServerMediaTypesResponseDto - */ -export interface ServerMediaTypesResponseDto { - /** - * - * @type {Array} - * @memberof ServerMediaTypesResponseDto - */ - 'image': Array; - /** - * - * @type {Array} - * @memberof ServerMediaTypesResponseDto - */ - 'sidecar': Array; - /** - * - * @type {Array} - * @memberof ServerMediaTypesResponseDto - */ - 'video': Array; -} -/** - * - * @export - * @interface ServerPingResponse - */ -export interface ServerPingResponse { - /** - * - * @type {string} - * @memberof ServerPingResponse - */ - 'res': string; -} -/** - * - * @export - * @interface ServerStatsResponseDto - */ -export interface ServerStatsResponseDto { - /** - * - * @type {number} - * @memberof ServerStatsResponseDto - */ - 'photos': number; - /** - * - * @type {number} - * @memberof ServerStatsResponseDto - */ - 'usage': number; - /** - * - * @type {Array} - * @memberof ServerStatsResponseDto - */ - 'usageByUser': Array; - /** - * - * @type {number} - * @memberof ServerStatsResponseDto - */ - 'videos': number; -} -/** - * - * @export - * @interface ServerThemeDto - */ -export interface ServerThemeDto { - /** - * - * @type {string} - * @memberof ServerThemeDto - */ - 'customCss': string; -} -/** - * - * @export - * @interface ServerVersionResponseDto - */ -export interface ServerVersionResponseDto { - /** - * - * @type {number} - * @memberof ServerVersionResponseDto - */ - 'major': number; - /** - * - * @type {number} - * @memberof ServerVersionResponseDto - */ - 'minor': number; - /** - * - * @type {number} - * @memberof ServerVersionResponseDto - */ - 'patch': number; -} -/** - * - * @export - * @interface SharedLinkCreateDto - */ -export interface SharedLinkCreateDto { - /** - * - * @type {string} - * @memberof SharedLinkCreateDto - */ - 'albumId'?: string; - /** - * - * @type {boolean} - * @memberof SharedLinkCreateDto - */ - 'allowDownload'?: boolean; - /** - * - * @type {boolean} - * @memberof SharedLinkCreateDto - */ - 'allowUpload'?: boolean; - /** - * - * @type {Array} - * @memberof SharedLinkCreateDto - */ - 'assetIds'?: Array; - /** - * - * @type {string} - * @memberof SharedLinkCreateDto - */ - 'description'?: string; - /** - * - * @type {string} - * @memberof SharedLinkCreateDto - */ - 'expiresAt'?: string | null; - /** - * - * @type {string} - * @memberof SharedLinkCreateDto - */ - 'password'?: string; - /** - * - * @type {boolean} - * @memberof SharedLinkCreateDto - */ - 'showMetadata'?: boolean; - /** - * - * @type {SharedLinkType} - * @memberof SharedLinkCreateDto - */ - 'type': SharedLinkType; -} - - -/** - * - * @export - * @interface SharedLinkEditDto - */ -export interface SharedLinkEditDto { - /** - * - * @type {boolean} - * @memberof SharedLinkEditDto - */ - 'allowDownload'?: boolean; - /** - * - * @type {boolean} - * @memberof SharedLinkEditDto - */ - 'allowUpload'?: boolean; - /** - * Few clients cannot send null to set the expiryTime to never. Setting this flag and not sending expiryAt is considered as null instead. Clients that can send null values can ignore this. - * @type {boolean} - * @memberof SharedLinkEditDto - */ - 'changeExpiryTime'?: boolean; - /** - * - * @type {string} - * @memberof SharedLinkEditDto - */ - 'description'?: string; - /** - * - * @type {string} - * @memberof SharedLinkEditDto - */ - 'expiresAt'?: string | null; - /** - * - * @type {string} - * @memberof SharedLinkEditDto - */ - 'password'?: string; - /** - * - * @type {boolean} - * @memberof SharedLinkEditDto - */ - 'showMetadata'?: boolean; -} -/** - * - * @export - * @interface SharedLinkResponseDto - */ -export interface SharedLinkResponseDto { - /** - * - * @type {AlbumResponseDto} - * @memberof SharedLinkResponseDto - */ - 'album'?: AlbumResponseDto; - /** - * - * @type {boolean} - * @memberof SharedLinkResponseDto - */ - 'allowDownload': boolean; - /** - * - * @type {boolean} - * @memberof SharedLinkResponseDto - */ - 'allowUpload': boolean; - /** - * - * @type {Array} - * @memberof SharedLinkResponseDto - */ - 'assets': Array; - /** - * - * @type {string} - * @memberof SharedLinkResponseDto - */ - 'createdAt': string; - /** - * - * @type {string} - * @memberof SharedLinkResponseDto - */ - 'description': string | null; - /** - * - * @type {string} - * @memberof SharedLinkResponseDto - */ - 'expiresAt': string | null; - /** - * - * @type {string} - * @memberof SharedLinkResponseDto - */ - 'id': string; - /** - * - * @type {string} - * @memberof SharedLinkResponseDto - */ - 'key': string; - /** - * - * @type {string} - * @memberof SharedLinkResponseDto - */ - 'password': string | null; - /** - * - * @type {boolean} - * @memberof SharedLinkResponseDto - */ - 'showMetadata': boolean; - /** - * - * @type {string} - * @memberof SharedLinkResponseDto - */ - 'token'?: string | null; - /** - * - * @type {SharedLinkType} - * @memberof SharedLinkResponseDto - */ - 'type': SharedLinkType; - /** - * - * @type {string} - * @memberof SharedLinkResponseDto - */ - 'userId': string; -} - - -/** - * - * @export - * @enum {string} - */ - -export const SharedLinkType = { - Album: 'ALBUM', - Individual: 'INDIVIDUAL' -} as const; - -export type SharedLinkType = typeof SharedLinkType[keyof typeof SharedLinkType]; - - -/** - * - * @export - * @interface SignUpDto - */ -export interface SignUpDto { - /** - * - * @type {string} - * @memberof SignUpDto - */ - 'email': string; - /** - * - * @type {string} - * @memberof SignUpDto - */ - 'name': string; - /** - * - * @type {string} - * @memberof SignUpDto - */ - 'password': string; -} -/** - * - * @export - * @interface SmartInfoResponseDto - */ -export interface SmartInfoResponseDto { - /** - * - * @type {Array} - * @memberof SmartInfoResponseDto - */ - 'objects'?: Array | null; - /** - * - * @type {Array} - * @memberof SmartInfoResponseDto - */ - 'tags'?: Array | null; -} -/** - * - * @export - * @interface SystemConfigDto - */ -export interface SystemConfigDto { - /** - * - * @type {SystemConfigFFmpegDto} - * @memberof SystemConfigDto - */ - 'ffmpeg': SystemConfigFFmpegDto; - /** - * - * @type {SystemConfigJobDto} - * @memberof SystemConfigDto - */ - 'job': SystemConfigJobDto; - /** - * - * @type {SystemConfigLibraryDto} - * @memberof SystemConfigDto - */ - 'library': SystemConfigLibraryDto; - /** - * - * @type {SystemConfigLoggingDto} - * @memberof SystemConfigDto - */ - 'logging': SystemConfigLoggingDto; - /** - * - * @type {SystemConfigMachineLearningDto} - * @memberof SystemConfigDto - */ - 'machineLearning': SystemConfigMachineLearningDto; - /** - * - * @type {SystemConfigMapDto} - * @memberof SystemConfigDto - */ - 'map': SystemConfigMapDto; - /** - * - * @type {SystemConfigNewVersionCheckDto} - * @memberof SystemConfigDto - */ - 'newVersionCheck': SystemConfigNewVersionCheckDto; - /** - * - * @type {SystemConfigOAuthDto} - * @memberof SystemConfigDto - */ - 'oauth': SystemConfigOAuthDto; - /** - * - * @type {SystemConfigPasswordLoginDto} - * @memberof SystemConfigDto - */ - 'passwordLogin': SystemConfigPasswordLoginDto; - /** - * - * @type {SystemConfigReverseGeocodingDto} - * @memberof SystemConfigDto - */ - 'reverseGeocoding': SystemConfigReverseGeocodingDto; - /** - * - * @type {SystemConfigServerDto} - * @memberof SystemConfigDto - */ - 'server': SystemConfigServerDto; - /** - * - * @type {SystemConfigStorageTemplateDto} - * @memberof SystemConfigDto - */ - 'storageTemplate': SystemConfigStorageTemplateDto; - /** - * - * @type {SystemConfigThemeDto} - * @memberof SystemConfigDto - */ - 'theme': SystemConfigThemeDto; - /** - * - * @type {SystemConfigThumbnailDto} - * @memberof SystemConfigDto - */ - 'thumbnail': SystemConfigThumbnailDto; - /** - * - * @type {SystemConfigTrashDto} - * @memberof SystemConfigDto - */ - 'trash': SystemConfigTrashDto; -} -/** - * - * @export - * @interface SystemConfigFFmpegDto - */ -export interface SystemConfigFFmpegDto { - /** - * - * @type {TranscodeHWAccel} - * @memberof SystemConfigFFmpegDto - */ - 'accel': TranscodeHWAccel; - /** - * - * @type {Array} - * @memberof SystemConfigFFmpegDto - */ - 'acceptedAudioCodecs': Array; - /** - * - * @type {Array} - * @memberof SystemConfigFFmpegDto - */ - 'acceptedVideoCodecs': Array; - /** - * - * @type {number} - * @memberof SystemConfigFFmpegDto - */ - 'bframes': number; - /** - * - * @type {CQMode} - * @memberof SystemConfigFFmpegDto - */ - 'cqMode': CQMode; - /** - * - * @type {number} - * @memberof SystemConfigFFmpegDto - */ - 'crf': number; - /** - * - * @type {number} - * @memberof SystemConfigFFmpegDto - */ - 'gopSize': number; - /** - * - * @type {string} - * @memberof SystemConfigFFmpegDto - */ - 'maxBitrate': string; - /** - * - * @type {number} - * @memberof SystemConfigFFmpegDto - */ - 'npl': number; - /** - * - * @type {string} - * @memberof SystemConfigFFmpegDto - */ - 'preferredHwDevice': string; - /** - * - * @type {string} - * @memberof SystemConfigFFmpegDto - */ - 'preset': string; - /** - * - * @type {number} - * @memberof SystemConfigFFmpegDto - */ - 'refs': number; - /** - * - * @type {AudioCodec} - * @memberof SystemConfigFFmpegDto - */ - 'targetAudioCodec': AudioCodec; - /** - * - * @type {string} - * @memberof SystemConfigFFmpegDto - */ - 'targetResolution': string; - /** - * - * @type {VideoCodec} - * @memberof SystemConfigFFmpegDto - */ - 'targetVideoCodec': VideoCodec; - /** - * - * @type {boolean} - * @memberof SystemConfigFFmpegDto - */ - 'temporalAQ': boolean; - /** - * - * @type {number} - * @memberof SystemConfigFFmpegDto - */ - 'threads': number; - /** - * - * @type {ToneMapping} - * @memberof SystemConfigFFmpegDto - */ - 'tonemap': ToneMapping; - /** - * - * @type {TranscodePolicy} - * @memberof SystemConfigFFmpegDto - */ - 'transcode': TranscodePolicy; - /** - * - * @type {boolean} - * @memberof SystemConfigFFmpegDto - */ - 'twoPass': boolean; -} - - -/** - * - * @export - * @interface SystemConfigJobDto - */ -export interface SystemConfigJobDto { - /** - * - * @type {JobSettingsDto} - * @memberof SystemConfigJobDto - */ - 'backgroundTask': JobSettingsDto; - /** - * - * @type {JobSettingsDto} - * @memberof SystemConfigJobDto - */ - 'faceDetection': JobSettingsDto; - /** - * - * @type {JobSettingsDto} - * @memberof SystemConfigJobDto - */ - 'library': JobSettingsDto; - /** - * - * @type {JobSettingsDto} - * @memberof SystemConfigJobDto - */ - 'metadataExtraction': JobSettingsDto; - /** - * - * @type {JobSettingsDto} - * @memberof SystemConfigJobDto - */ - 'migration': JobSettingsDto; - /** - * - * @type {JobSettingsDto} - * @memberof SystemConfigJobDto - */ - 'search': JobSettingsDto; - /** - * - * @type {JobSettingsDto} - * @memberof SystemConfigJobDto - */ - 'sidecar': JobSettingsDto; - /** - * - * @type {JobSettingsDto} - * @memberof SystemConfigJobDto - */ - 'smartSearch': JobSettingsDto; - /** - * - * @type {JobSettingsDto} - * @memberof SystemConfigJobDto - */ - 'thumbnailGeneration': JobSettingsDto; - /** - * - * @type {JobSettingsDto} - * @memberof SystemConfigJobDto - */ - 'videoConversion': JobSettingsDto; -} -/** - * - * @export - * @interface SystemConfigLibraryDto - */ -export interface SystemConfigLibraryDto { - /** - * - * @type {SystemConfigLibraryScanDto} - * @memberof SystemConfigLibraryDto - */ - 'scan': SystemConfigLibraryScanDto; - /** - * - * @type {SystemConfigLibraryWatchDto} - * @memberof SystemConfigLibraryDto - */ - 'watch': SystemConfigLibraryWatchDto; -} -/** - * - * @export - * @interface SystemConfigLibraryScanDto - */ -export interface SystemConfigLibraryScanDto { - /** - * - * @type {string} - * @memberof SystemConfigLibraryScanDto - */ - 'cronExpression': string; - /** - * - * @type {boolean} - * @memberof SystemConfigLibraryScanDto - */ - 'enabled': boolean; -} -/** - * - * @export - * @interface SystemConfigLibraryWatchDto - */ -export interface SystemConfigLibraryWatchDto { - /** - * - * @type {boolean} - * @memberof SystemConfigLibraryWatchDto - */ - 'enabled': boolean; - /** - * - * @type {number} - * @memberof SystemConfigLibraryWatchDto - */ - 'interval': number; - /** - * - * @type {boolean} - * @memberof SystemConfigLibraryWatchDto - */ - 'usePolling': boolean; -} -/** - * - * @export - * @interface SystemConfigLoggingDto - */ -export interface SystemConfigLoggingDto { - /** - * - * @type {boolean} - * @memberof SystemConfigLoggingDto - */ - 'enabled': boolean; - /** - * - * @type {LogLevel} - * @memberof SystemConfigLoggingDto - */ - 'level': LogLevel; -} - - -/** - * - * @export - * @interface SystemConfigMachineLearningDto - */ -export interface SystemConfigMachineLearningDto { - /** - * - * @type {CLIPConfig} - * @memberof SystemConfigMachineLearningDto - */ - 'clip': CLIPConfig; - /** - * - * @type {boolean} - * @memberof SystemConfigMachineLearningDto - */ - 'enabled': boolean; - /** - * - * @type {RecognitionConfig} - * @memberof SystemConfigMachineLearningDto - */ - 'facialRecognition': RecognitionConfig; - /** - * - * @type {string} - * @memberof SystemConfigMachineLearningDto - */ - 'url': string; -} -/** - * - * @export - * @interface SystemConfigMapDto - */ -export interface SystemConfigMapDto { - /** - * - * @type {string} - * @memberof SystemConfigMapDto - */ - 'darkStyle': string; - /** - * - * @type {boolean} - * @memberof SystemConfigMapDto - */ - 'enabled': boolean; - /** - * - * @type {string} - * @memberof SystemConfigMapDto - */ - 'lightStyle': string; -} -/** - * - * @export - * @interface SystemConfigNewVersionCheckDto - */ -export interface SystemConfigNewVersionCheckDto { - /** - * - * @type {boolean} - * @memberof SystemConfigNewVersionCheckDto - */ - 'enabled': boolean; -} -/** - * - * @export - * @interface SystemConfigOAuthDto - */ -export interface SystemConfigOAuthDto { - /** - * - * @type {boolean} - * @memberof SystemConfigOAuthDto - */ - 'autoLaunch': boolean; - /** - * - * @type {boolean} - * @memberof SystemConfigOAuthDto - */ - 'autoRegister': boolean; - /** - * - * @type {string} - * @memberof SystemConfigOAuthDto - */ - 'buttonText': string; - /** - * - * @type {string} - * @memberof SystemConfigOAuthDto - */ - 'clientId': string; - /** - * - * @type {string} - * @memberof SystemConfigOAuthDto - */ - 'clientSecret': string; - /** - * - * @type {boolean} - * @memberof SystemConfigOAuthDto - */ - 'enabled': boolean; - /** - * - * @type {string} - * @memberof SystemConfigOAuthDto - */ - 'issuerUrl': string; - /** - * - * @type {boolean} - * @memberof SystemConfigOAuthDto - */ - 'mobileOverrideEnabled': boolean; - /** - * - * @type {string} - * @memberof SystemConfigOAuthDto - */ - 'mobileRedirectUri': string; - /** - * - * @type {string} - * @memberof SystemConfigOAuthDto - */ - 'scope': string; - /** - * - * @type {string} - * @memberof SystemConfigOAuthDto - */ - 'signingAlgorithm': string; - /** - * - * @type {string} - * @memberof SystemConfigOAuthDto - */ - 'storageLabelClaim': string; -} -/** - * - * @export - * @interface SystemConfigPasswordLoginDto - */ -export interface SystemConfigPasswordLoginDto { - /** - * - * @type {boolean} - * @memberof SystemConfigPasswordLoginDto - */ - 'enabled': boolean; -} -/** - * - * @export - * @interface SystemConfigReverseGeocodingDto - */ -export interface SystemConfigReverseGeocodingDto { - /** - * - * @type {boolean} - * @memberof SystemConfigReverseGeocodingDto - */ - 'enabled': boolean; -} -/** - * - * @export - * @interface SystemConfigServerDto - */ -export interface SystemConfigServerDto { - /** - * - * @type {string} - * @memberof SystemConfigServerDto - */ - 'externalDomain': string; - /** - * - * @type {string} - * @memberof SystemConfigServerDto - */ - 'loginPageMessage': string; -} -/** - * - * @export - * @interface SystemConfigStorageTemplateDto - */ -export interface SystemConfigStorageTemplateDto { - /** - * - * @type {boolean} - * @memberof SystemConfigStorageTemplateDto - */ - 'enabled': boolean; - /** - * - * @type {boolean} - * @memberof SystemConfigStorageTemplateDto - */ - 'hashVerificationEnabled': boolean; - /** - * - * @type {string} - * @memberof SystemConfigStorageTemplateDto - */ - 'template': string; -} -/** - * - * @export - * @interface SystemConfigTemplateStorageOptionDto - */ -export interface SystemConfigTemplateStorageOptionDto { - /** - * - * @type {Array} - * @memberof SystemConfigTemplateStorageOptionDto - */ - 'dayOptions': Array; - /** - * - * @type {Array} - * @memberof SystemConfigTemplateStorageOptionDto - */ - 'hourOptions': Array; - /** - * - * @type {Array} - * @memberof SystemConfigTemplateStorageOptionDto - */ - 'minuteOptions': Array; - /** - * - * @type {Array} - * @memberof SystemConfigTemplateStorageOptionDto - */ - 'monthOptions': Array; - /** - * - * @type {Array} - * @memberof SystemConfigTemplateStorageOptionDto - */ - 'presetOptions': Array; - /** - * - * @type {Array} - * @memberof SystemConfigTemplateStorageOptionDto - */ - 'secondOptions': Array; - /** - * - * @type {Array} - * @memberof SystemConfigTemplateStorageOptionDto - */ - 'weekOptions': Array; - /** - * - * @type {Array} - * @memberof SystemConfigTemplateStorageOptionDto - */ - 'yearOptions': Array; -} -/** - * - * @export - * @interface SystemConfigThemeDto - */ -export interface SystemConfigThemeDto { - /** - * - * @type {string} - * @memberof SystemConfigThemeDto - */ - 'customCss': string; -} -/** - * - * @export - * @interface SystemConfigThumbnailDto - */ -export interface SystemConfigThumbnailDto { - /** - * - * @type {Colorspace} - * @memberof SystemConfigThumbnailDto - */ - 'colorspace': Colorspace; - /** - * - * @type {number} - * @memberof SystemConfigThumbnailDto - */ - 'jpegSize': number; - /** - * - * @type {number} - * @memberof SystemConfigThumbnailDto - */ - 'quality': number; - /** - * - * @type {number} - * @memberof SystemConfigThumbnailDto - */ - 'webpSize': number; -} - - -/** - * - * @export - * @interface SystemConfigTrashDto - */ -export interface SystemConfigTrashDto { - /** - * - * @type {number} - * @memberof SystemConfigTrashDto - */ - 'days': number; - /** - * - * @type {boolean} - * @memberof SystemConfigTrashDto - */ - 'enabled': boolean; -} -/** - * - * @export - * @interface TagResponseDto - */ -export interface TagResponseDto { - /** - * - * @type {string} - * @memberof TagResponseDto - */ - 'id': string; - /** - * - * @type {string} - * @memberof TagResponseDto - */ - 'name': string; - /** - * - * @type {TagTypeEnum} - * @memberof TagResponseDto - */ - 'type': TagTypeEnum; - /** - * - * @type {string} - * @memberof TagResponseDto - */ - 'userId': string; -} - - -/** - * - * @export - * @enum {string} - */ - -export const TagTypeEnum = { - Object: 'OBJECT', - Face: 'FACE', - Custom: 'CUSTOM' -} as const; - -export type TagTypeEnum = typeof TagTypeEnum[keyof typeof TagTypeEnum]; - - -/** - * - * @export - * @enum {string} - */ - -export const ThumbnailFormat = { - Jpeg: 'JPEG', - Webp: 'WEBP' -} as const; - -export type ThumbnailFormat = typeof ThumbnailFormat[keyof typeof ThumbnailFormat]; - - -/** - * - * @export - * @interface TimeBucketResponseDto - */ -export interface TimeBucketResponseDto { - /** - * - * @type {number} - * @memberof TimeBucketResponseDto - */ - 'count': number; - /** - * - * @type {string} - * @memberof TimeBucketResponseDto - */ - 'timeBucket': string; -} -/** - * - * @export - * @enum {string} - */ - -export const TimeBucketSize = { - Day: 'DAY', - Month: 'MONTH' -} as const; - -export type TimeBucketSize = typeof TimeBucketSize[keyof typeof TimeBucketSize]; - - -/** - * - * @export - * @enum {string} - */ - -export const ToneMapping = { - Hable: 'hable', - Mobius: 'mobius', - Reinhard: 'reinhard', - Disabled: 'disabled' -} as const; - -export type ToneMapping = typeof ToneMapping[keyof typeof ToneMapping]; - - -/** - * - * @export - * @enum {string} - */ - -export const TranscodeHWAccel = { - Nvenc: 'nvenc', - Qsv: 'qsv', - Vaapi: 'vaapi', - Rkmpp: 'rkmpp', - Disabled: 'disabled' -} as const; - -export type TranscodeHWAccel = typeof TranscodeHWAccel[keyof typeof TranscodeHWAccel]; - - -/** - * - * @export - * @enum {string} - */ - -export const TranscodePolicy = { - All: 'all', - Optimal: 'optimal', - Bitrate: 'bitrate', - Required: 'required', - Disabled: 'disabled' -} as const; - -export type TranscodePolicy = typeof TranscodePolicy[keyof typeof TranscodePolicy]; - - -/** - * - * @export - * @interface UpdateAlbumDto - */ -export interface UpdateAlbumDto { - /** - * - * @type {string} - * @memberof UpdateAlbumDto - */ - 'albumName'?: string; - /** - * - * @type {string} - * @memberof UpdateAlbumDto - */ - 'albumThumbnailAssetId'?: string; - /** - * - * @type {string} - * @memberof UpdateAlbumDto - */ - 'description'?: string; - /** - * - * @type {boolean} - * @memberof UpdateAlbumDto - */ - 'isActivityEnabled'?: boolean; -} -/** - * - * @export - * @interface UpdateAssetDto - */ -export interface UpdateAssetDto { - /** - * - * @type {string} - * @memberof UpdateAssetDto - */ - 'dateTimeOriginal'?: string; - /** - * - * @type {string} - * @memberof UpdateAssetDto - */ - 'description'?: string; - /** - * - * @type {boolean} - * @memberof UpdateAssetDto - */ - 'isArchived'?: boolean; - /** - * - * @type {boolean} - * @memberof UpdateAssetDto - */ - 'isFavorite'?: boolean; - /** - * - * @type {number} - * @memberof UpdateAssetDto - */ - 'latitude'?: number; - /** - * - * @type {number} - * @memberof UpdateAssetDto - */ - 'longitude'?: number; -} -/** - * - * @export - * @interface UpdateLibraryDto - */ -export interface UpdateLibraryDto { - /** - * - * @type {Array} - * @memberof UpdateLibraryDto - */ - 'exclusionPatterns'?: Array; - /** - * - * @type {Array} - * @memberof UpdateLibraryDto - */ - 'importPaths'?: Array; - /** - * - * @type {boolean} - * @memberof UpdateLibraryDto - */ - 'isVisible'?: boolean; - /** - * - * @type {string} - * @memberof UpdateLibraryDto - */ - 'name'?: string; -} -/** - * - * @export - * @interface UpdatePartnerDto - */ -export interface UpdatePartnerDto { - /** - * - * @type {boolean} - * @memberof UpdatePartnerDto - */ - 'inTimeline': boolean; -} -/** - * - * @export - * @interface UpdateStackParentDto - */ -export interface UpdateStackParentDto { - /** - * - * @type {string} - * @memberof UpdateStackParentDto - */ - 'newParentId': string; - /** - * - * @type {string} - * @memberof UpdateStackParentDto - */ - 'oldParentId': string; -} -/** - * - * @export - * @interface UpdateTagDto - */ -export interface UpdateTagDto { - /** - * - * @type {string} - * @memberof UpdateTagDto - */ - 'name'?: string; -} -/** - * - * @export - * @interface UpdateUserDto - */ -export interface UpdateUserDto { - /** - * - * @type {UserAvatarColor} - * @memberof UpdateUserDto - */ - 'avatarColor'?: UserAvatarColor; - /** - * - * @type {string} - * @memberof UpdateUserDto - */ - 'email'?: string; - /** - * - * @type {string} - * @memberof UpdateUserDto - */ - 'externalPath'?: string; - /** - * - * @type {string} - * @memberof UpdateUserDto - */ - 'id': string; - /** - * - * @type {boolean} - * @memberof UpdateUserDto - */ - 'isAdmin'?: boolean; - /** - * - * @type {boolean} - * @memberof UpdateUserDto - */ - 'memoriesEnabled'?: boolean; - /** - * - * @type {string} - * @memberof UpdateUserDto - */ - 'name'?: string; - /** - * - * @type {string} - * @memberof UpdateUserDto - */ - 'password'?: string; - /** - * - * @type {number} - * @memberof UpdateUserDto - */ - 'quotaSizeInBytes'?: number | null; - /** - * - * @type {boolean} - * @memberof UpdateUserDto - */ - 'shouldChangePassword'?: boolean; - /** - * - * @type {string} - * @memberof UpdateUserDto - */ - 'storageLabel'?: string; -} - - -/** - * - * @export - * @interface UsageByUserDto - */ -export interface UsageByUserDto { - /** - * - * @type {number} - * @memberof UsageByUserDto - */ - 'photos': number; - /** - * - * @type {number} - * @memberof UsageByUserDto - */ - 'quotaSizeInBytes': number | null; - /** - * - * @type {number} - * @memberof UsageByUserDto - */ - 'usage': number; - /** - * - * @type {string} - * @memberof UsageByUserDto - */ - 'userId': string; - /** - * - * @type {string} - * @memberof UsageByUserDto - */ - 'userName': string; - /** - * - * @type {number} - * @memberof UsageByUserDto - */ - 'videos': number; -} -/** - * - * @export - * @enum {string} - */ - -export const UserAvatarColor = { - Primary: 'primary', - Pink: 'pink', - Red: 'red', - Yellow: 'yellow', - Blue: 'blue', - Green: 'green', - Purple: 'purple', - Orange: 'orange', - Gray: 'gray', - Amber: 'amber' -} as const; - -export type UserAvatarColor = typeof UserAvatarColor[keyof typeof UserAvatarColor]; - - -/** - * - * @export - * @interface UserDto - */ -export interface UserDto { - /** - * - * @type {UserAvatarColor} - * @memberof UserDto - */ - 'avatarColor': UserAvatarColor; - /** - * - * @type {string} - * @memberof UserDto - */ - 'email': string; - /** - * - * @type {string} - * @memberof UserDto - */ - 'id': string; - /** - * - * @type {string} - * @memberof UserDto - */ - 'name': string; - /** - * - * @type {string} - * @memberof UserDto - */ - 'profileImagePath': string; -} - - -/** - * - * @export - * @interface UserResponseDto - */ -export interface UserResponseDto { - /** - * - * @type {UserAvatarColor} - * @memberof UserResponseDto - */ - 'avatarColor': UserAvatarColor; - /** - * - * @type {string} - * @memberof UserResponseDto - */ - 'createdAt': string; - /** - * - * @type {string} - * @memberof UserResponseDto - */ - 'deletedAt': string | null; - /** - * - * @type {string} - * @memberof UserResponseDto - */ - 'email': string; - /** - * - * @type {string} - * @memberof UserResponseDto - */ - 'externalPath': string | null; - /** - * - * @type {string} - * @memberof UserResponseDto - */ - 'id': string; - /** - * - * @type {boolean} - * @memberof UserResponseDto - */ - 'isAdmin': boolean; - /** - * - * @type {boolean} - * @memberof UserResponseDto - */ - 'memoriesEnabled'?: boolean; - /** - * - * @type {string} - * @memberof UserResponseDto - */ - 'name': string; - /** - * - * @type {string} - * @memberof UserResponseDto - */ - 'oauthId': string; - /** - * - * @type {string} - * @memberof UserResponseDto - */ - 'profileImagePath': string; - /** - * - * @type {number} - * @memberof UserResponseDto - */ - 'quotaSizeInBytes': number | null; - /** - * - * @type {number} - * @memberof UserResponseDto - */ - 'quotaUsageInBytes': number | null; - /** - * - * @type {boolean} - * @memberof UserResponseDto - */ - 'shouldChangePassword': boolean; - /** - * - * @type {string} - * @memberof UserResponseDto - */ - 'storageLabel': string | null; - /** - * - * @type {string} - * @memberof UserResponseDto - */ - 'updatedAt': string; -} - - -/** - * - * @export - * @interface ValidateAccessTokenResponseDto - */ -export interface ValidateAccessTokenResponseDto { - /** - * - * @type {boolean} - * @memberof ValidateAccessTokenResponseDto - */ - 'authStatus': boolean; -} -/** - * - * @export - * @enum {string} - */ - -export const VideoCodec = { - H264: 'h264', - Hevc: 'hevc', - Vp9: 'vp9' -} as const; - -export type VideoCodec = typeof VideoCodec[keyof typeof VideoCodec]; - - - -/** - * APIKeyApi - axios parameter creator - * @export - */ -export const APIKeyApiAxiosParamCreator = function (configuration?: Configuration) { - return { - /** - * - * @param {APIKeyCreateDto} aPIKeyCreateDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - createApiKey: async (aPIKeyCreateDto: APIKeyCreateDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'aPIKeyCreateDto' is not null or undefined - assertParamExists('createApiKey', 'aPIKeyCreateDto', aPIKeyCreateDto) - const localVarPath = `/api-key`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(aPIKeyCreateDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - deleteApiKey: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('deleteApiKey', 'id', id) - const localVarPath = `/api-key/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getApiKey: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('getApiKey', 'id', id) - const localVarPath = `/api-key/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getApiKeys: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/api-key`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {APIKeyUpdateDto} aPIKeyUpdateDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updateApiKey: async (id: string, aPIKeyUpdateDto: APIKeyUpdateDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('updateApiKey', 'id', id) - // verify required parameter 'aPIKeyUpdateDto' is not null or undefined - assertParamExists('updateApiKey', 'aPIKeyUpdateDto', aPIKeyUpdateDto) - const localVarPath = `/api-key/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(aPIKeyUpdateDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - } -}; - -/** - * APIKeyApi - functional programming interface - * @export - */ -export const APIKeyApiFp = function(configuration?: Configuration) { - const localVarAxiosParamCreator = APIKeyApiAxiosParamCreator(configuration) - return { - /** - * - * @param {APIKeyCreateDto} aPIKeyCreateDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async createApiKey(aPIKeyCreateDto: APIKeyCreateDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.createApiKey(aPIKeyCreateDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['APIKeyApi.createApiKey']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async deleteApiKey(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.deleteApiKey(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['APIKeyApi.deleteApiKey']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getApiKey(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getApiKey(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['APIKeyApi.getApiKey']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getApiKeys(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getApiKeys(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['APIKeyApi.getApiKeys']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {APIKeyUpdateDto} aPIKeyUpdateDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async updateApiKey(id: string, aPIKeyUpdateDto: APIKeyUpdateDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.updateApiKey(id, aPIKeyUpdateDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['APIKeyApi.updateApiKey']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - } -}; - -/** - * APIKeyApi - factory interface - * @export - */ -export const APIKeyApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { - const localVarFp = APIKeyApiFp(configuration) - return { - /** - * - * @param {APIKeyApiCreateApiKeyRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - createApiKey(requestParameters: APIKeyApiCreateApiKeyRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.createApiKey(requestParameters.aPIKeyCreateDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {APIKeyApiDeleteApiKeyRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - deleteApiKey(requestParameters: APIKeyApiDeleteApiKeyRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.deleteApiKey(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {APIKeyApiGetApiKeyRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getApiKey(requestParameters: APIKeyApiGetApiKeyRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getApiKey(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getApiKeys(options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getApiKeys(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {APIKeyApiUpdateApiKeyRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updateApiKey(requestParameters: APIKeyApiUpdateApiKeyRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.updateApiKey(requestParameters.id, requestParameters.aPIKeyUpdateDto, options).then((request) => request(axios, basePath)); - }, - }; -}; - -/** - * Request parameters for createApiKey operation in APIKeyApi. - * @export - * @interface APIKeyApiCreateApiKeyRequest - */ -export interface APIKeyApiCreateApiKeyRequest { - /** - * - * @type {APIKeyCreateDto} - * @memberof APIKeyApiCreateApiKey - */ - readonly aPIKeyCreateDto: APIKeyCreateDto -} - -/** - * Request parameters for deleteApiKey operation in APIKeyApi. - * @export - * @interface APIKeyApiDeleteApiKeyRequest - */ -export interface APIKeyApiDeleteApiKeyRequest { - /** - * - * @type {string} - * @memberof APIKeyApiDeleteApiKey - */ - readonly id: string -} - -/** - * Request parameters for getApiKey operation in APIKeyApi. - * @export - * @interface APIKeyApiGetApiKeyRequest - */ -export interface APIKeyApiGetApiKeyRequest { - /** - * - * @type {string} - * @memberof APIKeyApiGetApiKey - */ - readonly id: string -} - -/** - * Request parameters for updateApiKey operation in APIKeyApi. - * @export - * @interface APIKeyApiUpdateApiKeyRequest - */ -export interface APIKeyApiUpdateApiKeyRequest { - /** - * - * @type {string} - * @memberof APIKeyApiUpdateApiKey - */ - readonly id: string - - /** - * - * @type {APIKeyUpdateDto} - * @memberof APIKeyApiUpdateApiKey - */ - readonly aPIKeyUpdateDto: APIKeyUpdateDto -} - -/** - * APIKeyApi - object-oriented interface - * @export - * @class APIKeyApi - * @extends {BaseAPI} - */ -export class APIKeyApi extends BaseAPI { - /** - * - * @param {APIKeyApiCreateApiKeyRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof APIKeyApi - */ - public createApiKey(requestParameters: APIKeyApiCreateApiKeyRequest, options?: RawAxiosRequestConfig) { - return APIKeyApiFp(this.configuration).createApiKey(requestParameters.aPIKeyCreateDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {APIKeyApiDeleteApiKeyRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof APIKeyApi - */ - public deleteApiKey(requestParameters: APIKeyApiDeleteApiKeyRequest, options?: RawAxiosRequestConfig) { - return APIKeyApiFp(this.configuration).deleteApiKey(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {APIKeyApiGetApiKeyRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof APIKeyApi - */ - public getApiKey(requestParameters: APIKeyApiGetApiKeyRequest, options?: RawAxiosRequestConfig) { - return APIKeyApiFp(this.configuration).getApiKey(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof APIKeyApi - */ - public getApiKeys(options?: RawAxiosRequestConfig) { - return APIKeyApiFp(this.configuration).getApiKeys(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {APIKeyApiUpdateApiKeyRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof APIKeyApi - */ - public updateApiKey(requestParameters: APIKeyApiUpdateApiKeyRequest, options?: RawAxiosRequestConfig) { - return APIKeyApiFp(this.configuration).updateApiKey(requestParameters.id, requestParameters.aPIKeyUpdateDto, options).then((request) => request(this.axios, this.basePath)); - } -} - - - -/** - * ActivityApi - axios parameter creator - * @export - */ -export const ActivityApiAxiosParamCreator = function (configuration?: Configuration) { - return { - /** - * - * @param {ActivityCreateDto} activityCreateDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - createActivity: async (activityCreateDto: ActivityCreateDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'activityCreateDto' is not null or undefined - assertParamExists('createActivity', 'activityCreateDto', activityCreateDto) - const localVarPath = `/activity`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(activityCreateDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - deleteActivity: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('deleteActivity', 'id', id) - const localVarPath = `/activity/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} albumId - * @param {string} [assetId] - * @param {ReactionLevel} [level] - * @param {ReactionType} [type] - * @param {string} [userId] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getActivities: async (albumId: string, assetId?: string, level?: ReactionLevel, type?: ReactionType, userId?: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'albumId' is not null or undefined - assertParamExists('getActivities', 'albumId', albumId) - const localVarPath = `/activity`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (albumId !== undefined) { - localVarQueryParameter['albumId'] = albumId; - } - - if (assetId !== undefined) { - localVarQueryParameter['assetId'] = assetId; - } - - if (level !== undefined) { - localVarQueryParameter['level'] = level; - } - - if (type !== undefined) { - localVarQueryParameter['type'] = type; - } - - if (userId !== undefined) { - localVarQueryParameter['userId'] = userId; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} albumId - * @param {string} [assetId] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getActivityStatistics: async (albumId: string, assetId?: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'albumId' is not null or undefined - assertParamExists('getActivityStatistics', 'albumId', albumId) - const localVarPath = `/activity/statistics`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (albumId !== undefined) { - localVarQueryParameter['albumId'] = albumId; - } - - if (assetId !== undefined) { - localVarQueryParameter['assetId'] = assetId; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - } -}; - -/** - * ActivityApi - functional programming interface - * @export - */ -export const ActivityApiFp = function(configuration?: Configuration) { - const localVarAxiosParamCreator = ActivityApiAxiosParamCreator(configuration) - return { - /** - * - * @param {ActivityCreateDto} activityCreateDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async createActivity(activityCreateDto: ActivityCreateDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.createActivity(activityCreateDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['ActivityApi.createActivity']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async deleteActivity(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.deleteActivity(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['ActivityApi.deleteActivity']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} albumId - * @param {string} [assetId] - * @param {ReactionLevel} [level] - * @param {ReactionType} [type] - * @param {string} [userId] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getActivities(albumId: string, assetId?: string, level?: ReactionLevel, type?: ReactionType, userId?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getActivities(albumId, assetId, level, type, userId, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['ActivityApi.getActivities']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} albumId - * @param {string} [assetId] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getActivityStatistics(albumId: string, assetId?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getActivityStatistics(albumId, assetId, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['ActivityApi.getActivityStatistics']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - } -}; - -/** - * ActivityApi - factory interface - * @export - */ -export const ActivityApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { - const localVarFp = ActivityApiFp(configuration) - return { - /** - * - * @param {ActivityApiCreateActivityRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - createActivity(requestParameters: ActivityApiCreateActivityRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.createActivity(requestParameters.activityCreateDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {ActivityApiDeleteActivityRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - deleteActivity(requestParameters: ActivityApiDeleteActivityRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.deleteActivity(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {ActivityApiGetActivitiesRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getActivities(requestParameters: ActivityApiGetActivitiesRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getActivities(requestParameters.albumId, requestParameters.assetId, requestParameters.level, requestParameters.type, requestParameters.userId, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {ActivityApiGetActivityStatisticsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getActivityStatistics(requestParameters: ActivityApiGetActivityStatisticsRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getActivityStatistics(requestParameters.albumId, requestParameters.assetId, options).then((request) => request(axios, basePath)); - }, - }; -}; - -/** - * Request parameters for createActivity operation in ActivityApi. - * @export - * @interface ActivityApiCreateActivityRequest - */ -export interface ActivityApiCreateActivityRequest { - /** - * - * @type {ActivityCreateDto} - * @memberof ActivityApiCreateActivity - */ - readonly activityCreateDto: ActivityCreateDto -} - -/** - * Request parameters for deleteActivity operation in ActivityApi. - * @export - * @interface ActivityApiDeleteActivityRequest - */ -export interface ActivityApiDeleteActivityRequest { - /** - * - * @type {string} - * @memberof ActivityApiDeleteActivity - */ - readonly id: string -} - -/** - * Request parameters for getActivities operation in ActivityApi. - * @export - * @interface ActivityApiGetActivitiesRequest - */ -export interface ActivityApiGetActivitiesRequest { - /** - * - * @type {string} - * @memberof ActivityApiGetActivities - */ - readonly albumId: string - - /** - * - * @type {string} - * @memberof ActivityApiGetActivities - */ - readonly assetId?: string - - /** - * - * @type {ReactionLevel} - * @memberof ActivityApiGetActivities - */ - readonly level?: ReactionLevel - - /** - * - * @type {ReactionType} - * @memberof ActivityApiGetActivities - */ - readonly type?: ReactionType - - /** - * - * @type {string} - * @memberof ActivityApiGetActivities - */ - readonly userId?: string -} - -/** - * Request parameters for getActivityStatistics operation in ActivityApi. - * @export - * @interface ActivityApiGetActivityStatisticsRequest - */ -export interface ActivityApiGetActivityStatisticsRequest { - /** - * - * @type {string} - * @memberof ActivityApiGetActivityStatistics - */ - readonly albumId: string - - /** - * - * @type {string} - * @memberof ActivityApiGetActivityStatistics - */ - readonly assetId?: string -} - -/** - * ActivityApi - object-oriented interface - * @export - * @class ActivityApi - * @extends {BaseAPI} - */ -export class ActivityApi extends BaseAPI { - /** - * - * @param {ActivityApiCreateActivityRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof ActivityApi - */ - public createActivity(requestParameters: ActivityApiCreateActivityRequest, options?: RawAxiosRequestConfig) { - return ActivityApiFp(this.configuration).createActivity(requestParameters.activityCreateDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {ActivityApiDeleteActivityRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof ActivityApi - */ - public deleteActivity(requestParameters: ActivityApiDeleteActivityRequest, options?: RawAxiosRequestConfig) { - return ActivityApiFp(this.configuration).deleteActivity(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {ActivityApiGetActivitiesRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof ActivityApi - */ - public getActivities(requestParameters: ActivityApiGetActivitiesRequest, options?: RawAxiosRequestConfig) { - return ActivityApiFp(this.configuration).getActivities(requestParameters.albumId, requestParameters.assetId, requestParameters.level, requestParameters.type, requestParameters.userId, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {ActivityApiGetActivityStatisticsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof ActivityApi - */ - public getActivityStatistics(requestParameters: ActivityApiGetActivityStatisticsRequest, options?: RawAxiosRequestConfig) { - return ActivityApiFp(this.configuration).getActivityStatistics(requestParameters.albumId, requestParameters.assetId, options).then((request) => request(this.axios, this.basePath)); - } -} - - - -/** - * AlbumApi - axios parameter creator - * @export - */ -export const AlbumApiAxiosParamCreator = function (configuration?: Configuration) { - return { - /** - * - * @param {string} id - * @param {BulkIdsDto} bulkIdsDto - * @param {string} [key] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - addAssetsToAlbum: async (id: string, bulkIdsDto: BulkIdsDto, key?: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('addAssetsToAlbum', 'id', id) - // verify required parameter 'bulkIdsDto' is not null or undefined - assertParamExists('addAssetsToAlbum', 'bulkIdsDto', bulkIdsDto) - const localVarPath = `/album/{id}/assets` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (key !== undefined) { - localVarQueryParameter['key'] = key; - } - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(bulkIdsDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {AddUsersDto} addUsersDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - addUsersToAlbum: async (id: string, addUsersDto: AddUsersDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('addUsersToAlbum', 'id', id) - // verify required parameter 'addUsersDto' is not null or undefined - assertParamExists('addUsersToAlbum', 'addUsersDto', addUsersDto) - const localVarPath = `/album/{id}/users` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(addUsersDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {CreateAlbumDto} createAlbumDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - createAlbum: async (createAlbumDto: CreateAlbumDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'createAlbumDto' is not null or undefined - assertParamExists('createAlbum', 'createAlbumDto', createAlbumDto) - const localVarPath = `/album`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(createAlbumDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - deleteAlbum: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('deleteAlbum', 'id', id) - const localVarPath = `/album/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAlbumCount: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/album/count`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {string} [key] - * @param {boolean} [withoutAssets] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAlbumInfo: async (id: string, key?: string, withoutAssets?: boolean, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('getAlbumInfo', 'id', id) - const localVarPath = `/album/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (key !== undefined) { - localVarQueryParameter['key'] = key; - } - - if (withoutAssets !== undefined) { - localVarQueryParameter['withoutAssets'] = withoutAssets; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} [assetId] Only returns albums that contain the asset Ignores the shared parameter undefined: get all albums - * @param {boolean} [shared] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAllAlbums: async (assetId?: string, shared?: boolean, options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/album`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (assetId !== undefined) { - localVarQueryParameter['assetId'] = assetId; - } - - if (shared !== undefined) { - localVarQueryParameter['shared'] = shared; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {BulkIdsDto} bulkIdsDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - removeAssetFromAlbum: async (id: string, bulkIdsDto: BulkIdsDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('removeAssetFromAlbum', 'id', id) - // verify required parameter 'bulkIdsDto' is not null or undefined - assertParamExists('removeAssetFromAlbum', 'bulkIdsDto', bulkIdsDto) - const localVarPath = `/album/{id}/assets` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(bulkIdsDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {string} userId - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - removeUserFromAlbum: async (id: string, userId: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('removeUserFromAlbum', 'id', id) - // verify required parameter 'userId' is not null or undefined - assertParamExists('removeUserFromAlbum', 'userId', userId) - const localVarPath = `/album/{id}/user/{userId}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))) - .replace(`{${"userId"}}`, encodeURIComponent(String(userId))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {UpdateAlbumDto} updateAlbumDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updateAlbumInfo: async (id: string, updateAlbumDto: UpdateAlbumDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('updateAlbumInfo', 'id', id) - // verify required parameter 'updateAlbumDto' is not null or undefined - assertParamExists('updateAlbumInfo', 'updateAlbumDto', updateAlbumDto) - const localVarPath = `/album/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'PATCH', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(updateAlbumDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - } -}; - -/** - * AlbumApi - functional programming interface - * @export - */ -export const AlbumApiFp = function(configuration?: Configuration) { - const localVarAxiosParamCreator = AlbumApiAxiosParamCreator(configuration) - return { - /** - * - * @param {string} id - * @param {BulkIdsDto} bulkIdsDto - * @param {string} [key] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async addAssetsToAlbum(id: string, bulkIdsDto: BulkIdsDto, key?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.addAssetsToAlbum(id, bulkIdsDto, key, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AlbumApi.addAssetsToAlbum']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {AddUsersDto} addUsersDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async addUsersToAlbum(id: string, addUsersDto: AddUsersDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.addUsersToAlbum(id, addUsersDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AlbumApi.addUsersToAlbum']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {CreateAlbumDto} createAlbumDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async createAlbum(createAlbumDto: CreateAlbumDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.createAlbum(createAlbumDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AlbumApi.createAlbum']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async deleteAlbum(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.deleteAlbum(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AlbumApi.deleteAlbum']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getAlbumCount(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAlbumCount(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AlbumApi.getAlbumCount']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {string} [key] - * @param {boolean} [withoutAssets] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getAlbumInfo(id: string, key?: string, withoutAssets?: boolean, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAlbumInfo(id, key, withoutAssets, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AlbumApi.getAlbumInfo']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} [assetId] Only returns albums that contain the asset Ignores the shared parameter undefined: get all albums - * @param {boolean} [shared] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getAllAlbums(assetId?: string, shared?: boolean, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAllAlbums(assetId, shared, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AlbumApi.getAllAlbums']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {BulkIdsDto} bulkIdsDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async removeAssetFromAlbum(id: string, bulkIdsDto: BulkIdsDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.removeAssetFromAlbum(id, bulkIdsDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AlbumApi.removeAssetFromAlbum']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {string} userId - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async removeUserFromAlbum(id: string, userId: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.removeUserFromAlbum(id, userId, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AlbumApi.removeUserFromAlbum']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {UpdateAlbumDto} updateAlbumDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async updateAlbumInfo(id: string, updateAlbumDto: UpdateAlbumDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.updateAlbumInfo(id, updateAlbumDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AlbumApi.updateAlbumInfo']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - } -}; - -/** - * AlbumApi - factory interface - * @export - */ -export const AlbumApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { - const localVarFp = AlbumApiFp(configuration) - return { - /** - * - * @param {AlbumApiAddAssetsToAlbumRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - addAssetsToAlbum(requestParameters: AlbumApiAddAssetsToAlbumRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.addAssetsToAlbum(requestParameters.id, requestParameters.bulkIdsDto, requestParameters.key, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AlbumApiAddUsersToAlbumRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - addUsersToAlbum(requestParameters: AlbumApiAddUsersToAlbumRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.addUsersToAlbum(requestParameters.id, requestParameters.addUsersDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AlbumApiCreateAlbumRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - createAlbum(requestParameters: AlbumApiCreateAlbumRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.createAlbum(requestParameters.createAlbumDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AlbumApiDeleteAlbumRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - deleteAlbum(requestParameters: AlbumApiDeleteAlbumRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.deleteAlbum(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAlbumCount(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getAlbumCount(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AlbumApiGetAlbumInfoRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAlbumInfo(requestParameters: AlbumApiGetAlbumInfoRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getAlbumInfo(requestParameters.id, requestParameters.key, requestParameters.withoutAssets, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AlbumApiGetAllAlbumsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAllAlbums(requestParameters: AlbumApiGetAllAlbumsRequest = {}, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getAllAlbums(requestParameters.assetId, requestParameters.shared, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AlbumApiRemoveAssetFromAlbumRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - removeAssetFromAlbum(requestParameters: AlbumApiRemoveAssetFromAlbumRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.removeAssetFromAlbum(requestParameters.id, requestParameters.bulkIdsDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AlbumApiRemoveUserFromAlbumRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - removeUserFromAlbum(requestParameters: AlbumApiRemoveUserFromAlbumRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.removeUserFromAlbum(requestParameters.id, requestParameters.userId, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AlbumApiUpdateAlbumInfoRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updateAlbumInfo(requestParameters: AlbumApiUpdateAlbumInfoRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.updateAlbumInfo(requestParameters.id, requestParameters.updateAlbumDto, options).then((request) => request(axios, basePath)); - }, - }; -}; - -/** - * Request parameters for addAssetsToAlbum operation in AlbumApi. - * @export - * @interface AlbumApiAddAssetsToAlbumRequest - */ -export interface AlbumApiAddAssetsToAlbumRequest { - /** - * - * @type {string} - * @memberof AlbumApiAddAssetsToAlbum - */ - readonly id: string - - /** - * - * @type {BulkIdsDto} - * @memberof AlbumApiAddAssetsToAlbum - */ - readonly bulkIdsDto: BulkIdsDto - - /** - * - * @type {string} - * @memberof AlbumApiAddAssetsToAlbum - */ - readonly key?: string -} - -/** - * Request parameters for addUsersToAlbum operation in AlbumApi. - * @export - * @interface AlbumApiAddUsersToAlbumRequest - */ -export interface AlbumApiAddUsersToAlbumRequest { - /** - * - * @type {string} - * @memberof AlbumApiAddUsersToAlbum - */ - readonly id: string - - /** - * - * @type {AddUsersDto} - * @memberof AlbumApiAddUsersToAlbum - */ - readonly addUsersDto: AddUsersDto -} - -/** - * Request parameters for createAlbum operation in AlbumApi. - * @export - * @interface AlbumApiCreateAlbumRequest - */ -export interface AlbumApiCreateAlbumRequest { - /** - * - * @type {CreateAlbumDto} - * @memberof AlbumApiCreateAlbum - */ - readonly createAlbumDto: CreateAlbumDto -} - -/** - * Request parameters for deleteAlbum operation in AlbumApi. - * @export - * @interface AlbumApiDeleteAlbumRequest - */ -export interface AlbumApiDeleteAlbumRequest { - /** - * - * @type {string} - * @memberof AlbumApiDeleteAlbum - */ - readonly id: string -} - -/** - * Request parameters for getAlbumInfo operation in AlbumApi. - * @export - * @interface AlbumApiGetAlbumInfoRequest - */ -export interface AlbumApiGetAlbumInfoRequest { - /** - * - * @type {string} - * @memberof AlbumApiGetAlbumInfo - */ - readonly id: string - - /** - * - * @type {string} - * @memberof AlbumApiGetAlbumInfo - */ - readonly key?: string - - /** - * - * @type {boolean} - * @memberof AlbumApiGetAlbumInfo - */ - readonly withoutAssets?: boolean -} - -/** - * Request parameters for getAllAlbums operation in AlbumApi. - * @export - * @interface AlbumApiGetAllAlbumsRequest - */ -export interface AlbumApiGetAllAlbumsRequest { - /** - * Only returns albums that contain the asset Ignores the shared parameter undefined: get all albums - * @type {string} - * @memberof AlbumApiGetAllAlbums - */ - readonly assetId?: string - - /** - * - * @type {boolean} - * @memberof AlbumApiGetAllAlbums - */ - readonly shared?: boolean -} - -/** - * Request parameters for removeAssetFromAlbum operation in AlbumApi. - * @export - * @interface AlbumApiRemoveAssetFromAlbumRequest - */ -export interface AlbumApiRemoveAssetFromAlbumRequest { - /** - * - * @type {string} - * @memberof AlbumApiRemoveAssetFromAlbum - */ - readonly id: string - - /** - * - * @type {BulkIdsDto} - * @memberof AlbumApiRemoveAssetFromAlbum - */ - readonly bulkIdsDto: BulkIdsDto -} - -/** - * Request parameters for removeUserFromAlbum operation in AlbumApi. - * @export - * @interface AlbumApiRemoveUserFromAlbumRequest - */ -export interface AlbumApiRemoveUserFromAlbumRequest { - /** - * - * @type {string} - * @memberof AlbumApiRemoveUserFromAlbum - */ - readonly id: string - - /** - * - * @type {string} - * @memberof AlbumApiRemoveUserFromAlbum - */ - readonly userId: string -} - -/** - * Request parameters for updateAlbumInfo operation in AlbumApi. - * @export - * @interface AlbumApiUpdateAlbumInfoRequest - */ -export interface AlbumApiUpdateAlbumInfoRequest { - /** - * - * @type {string} - * @memberof AlbumApiUpdateAlbumInfo - */ - readonly id: string - - /** - * - * @type {UpdateAlbumDto} - * @memberof AlbumApiUpdateAlbumInfo - */ - readonly updateAlbumDto: UpdateAlbumDto -} - -/** - * AlbumApi - object-oriented interface - * @export - * @class AlbumApi - * @extends {BaseAPI} - */ -export class AlbumApi extends BaseAPI { - /** - * - * @param {AlbumApiAddAssetsToAlbumRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AlbumApi - */ - public addAssetsToAlbum(requestParameters: AlbumApiAddAssetsToAlbumRequest, options?: RawAxiosRequestConfig) { - return AlbumApiFp(this.configuration).addAssetsToAlbum(requestParameters.id, requestParameters.bulkIdsDto, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AlbumApiAddUsersToAlbumRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AlbumApi - */ - public addUsersToAlbum(requestParameters: AlbumApiAddUsersToAlbumRequest, options?: RawAxiosRequestConfig) { - return AlbumApiFp(this.configuration).addUsersToAlbum(requestParameters.id, requestParameters.addUsersDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AlbumApiCreateAlbumRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AlbumApi - */ - public createAlbum(requestParameters: AlbumApiCreateAlbumRequest, options?: RawAxiosRequestConfig) { - return AlbumApiFp(this.configuration).createAlbum(requestParameters.createAlbumDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AlbumApiDeleteAlbumRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AlbumApi - */ - public deleteAlbum(requestParameters: AlbumApiDeleteAlbumRequest, options?: RawAxiosRequestConfig) { - return AlbumApiFp(this.configuration).deleteAlbum(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AlbumApi - */ - public getAlbumCount(options?: RawAxiosRequestConfig) { - return AlbumApiFp(this.configuration).getAlbumCount(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AlbumApiGetAlbumInfoRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AlbumApi - */ - public getAlbumInfo(requestParameters: AlbumApiGetAlbumInfoRequest, options?: RawAxiosRequestConfig) { - return AlbumApiFp(this.configuration).getAlbumInfo(requestParameters.id, requestParameters.key, requestParameters.withoutAssets, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AlbumApiGetAllAlbumsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AlbumApi - */ - public getAllAlbums(requestParameters: AlbumApiGetAllAlbumsRequest = {}, options?: RawAxiosRequestConfig) { - return AlbumApiFp(this.configuration).getAllAlbums(requestParameters.assetId, requestParameters.shared, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AlbumApiRemoveAssetFromAlbumRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AlbumApi - */ - public removeAssetFromAlbum(requestParameters: AlbumApiRemoveAssetFromAlbumRequest, options?: RawAxiosRequestConfig) { - return AlbumApiFp(this.configuration).removeAssetFromAlbum(requestParameters.id, requestParameters.bulkIdsDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AlbumApiRemoveUserFromAlbumRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AlbumApi - */ - public removeUserFromAlbum(requestParameters: AlbumApiRemoveUserFromAlbumRequest, options?: RawAxiosRequestConfig) { - return AlbumApiFp(this.configuration).removeUserFromAlbum(requestParameters.id, requestParameters.userId, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AlbumApiUpdateAlbumInfoRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AlbumApi - */ - public updateAlbumInfo(requestParameters: AlbumApiUpdateAlbumInfoRequest, options?: RawAxiosRequestConfig) { - return AlbumApiFp(this.configuration).updateAlbumInfo(requestParameters.id, requestParameters.updateAlbumDto, options).then((request) => request(this.axios, this.basePath)); - } -} - - - -/** - * AssetApi - axios parameter creator - * @export - */ -export const AssetApiAxiosParamCreator = function (configuration?: Configuration) { - return { - /** - * Checks if assets exist by checksums - * @param {AssetBulkUploadCheckDto} assetBulkUploadCheckDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - checkBulkUpload: async (assetBulkUploadCheckDto: AssetBulkUploadCheckDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'assetBulkUploadCheckDto' is not null or undefined - assertParamExists('checkBulkUpload', 'assetBulkUploadCheckDto', assetBulkUploadCheckDto) - const localVarPath = `/asset/bulk-upload-check`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(assetBulkUploadCheckDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * Checks if multiple assets exist on the server and returns all existing - used by background backup - * @param {CheckExistingAssetsDto} checkExistingAssetsDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - checkExistingAssets: async (checkExistingAssetsDto: CheckExistingAssetsDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'checkExistingAssetsDto' is not null or undefined - assertParamExists('checkExistingAssets', 'checkExistingAssetsDto', checkExistingAssetsDto) - const localVarPath = `/asset/exist`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(checkExistingAssetsDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {AssetBulkDeleteDto} assetBulkDeleteDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - deleteAssets: async (assetBulkDeleteDto: AssetBulkDeleteDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'assetBulkDeleteDto' is not null or undefined - assertParamExists('deleteAssets', 'assetBulkDeleteDto', assetBulkDeleteDto) - const localVarPath = `/asset`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(assetBulkDeleteDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * Get all AssetEntity belong to the user - * @param {string} [ifNoneMatch] ETag of data already cached on the client - * @param {boolean} [isArchived] - * @param {boolean} [isFavorite] - * @param {number} [skip] - * @param {number} [take] - * @param {string} [updatedAfter] - * @param {string} [updatedBefore] - * @param {string} [userId] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAllAssets: async (ifNoneMatch?: string, isArchived?: boolean, isFavorite?: boolean, skip?: number, take?: number, updatedAfter?: string, updatedBefore?: string, userId?: string, options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/asset`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (isArchived !== undefined) { - localVarQueryParameter['isArchived'] = isArchived; - } - - if (isFavorite !== undefined) { - localVarQueryParameter['isFavorite'] = isFavorite; - } - - if (skip !== undefined) { - localVarQueryParameter['skip'] = skip; - } - - if (take !== undefined) { - localVarQueryParameter['take'] = take; - } - - if (updatedAfter !== undefined) { - localVarQueryParameter['updatedAfter'] = (updatedAfter as any instanceof Date) ? - (updatedAfter as any).toISOString() : - updatedAfter; - } - - if (updatedBefore !== undefined) { - localVarQueryParameter['updatedBefore'] = (updatedBefore as any instanceof Date) ? - (updatedBefore as any).toISOString() : - updatedBefore; - } - - if (userId !== undefined) { - localVarQueryParameter['userId'] = userId; - } - - if (ifNoneMatch != null) { - localVarHeaderParameter['if-none-match'] = String(ifNoneMatch); - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * Get all asset of a device that are in the database, ID only. - * @param {string} deviceId - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAllUserAssetsByDeviceId: async (deviceId: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'deviceId' is not null or undefined - assertParamExists('getAllUserAssetsByDeviceId', 'deviceId', deviceId) - const localVarPath = `/asset/device/{deviceId}` - .replace(`{${"deviceId"}}`, encodeURIComponent(String(deviceId))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {string} [key] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAssetInfo: async (id: string, key?: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('getAssetInfo', 'id', id) - const localVarPath = `/asset/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (key !== undefined) { - localVarQueryParameter['key'] = key; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAssetSearchTerms: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/asset/search-terms`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {boolean} [isArchived] - * @param {boolean} [isFavorite] - * @param {boolean} [isTrashed] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAssetStatistics: async (isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/asset/statistics`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (isArchived !== undefined) { - localVarQueryParameter['isArchived'] = isArchived; - } - - if (isFavorite !== undefined) { - localVarQueryParameter['isFavorite'] = isFavorite; - } - - if (isTrashed !== undefined) { - localVarQueryParameter['isTrashed'] = isTrashed; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {ThumbnailFormat} [format] - * @param {string} [key] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAssetThumbnail: async (id: string, format?: ThumbnailFormat, key?: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('getAssetThumbnail', 'id', id) - const localVarPath = `/asset/thumbnail/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (format !== undefined) { - localVarQueryParameter['format'] = format; - } - - if (key !== undefined) { - localVarQueryParameter['key'] = key; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getCuratedLocations: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/asset/curated-locations`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getCuratedObjects: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/asset/curated-objects`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} [fileCreatedAfter] - * @param {string} [fileCreatedBefore] - * @param {boolean} [isArchived] - * @param {boolean} [isFavorite] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getMapMarkers: async (fileCreatedAfter?: string, fileCreatedBefore?: string, isArchived?: boolean, isFavorite?: boolean, options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/asset/map-marker`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (fileCreatedAfter !== undefined) { - localVarQueryParameter['fileCreatedAfter'] = (fileCreatedAfter as any instanceof Date) ? - (fileCreatedAfter as any).toISOString() : - fileCreatedAfter; - } - - if (fileCreatedBefore !== undefined) { - localVarQueryParameter['fileCreatedBefore'] = (fileCreatedBefore as any instanceof Date) ? - (fileCreatedBefore as any).toISOString() : - fileCreatedBefore; - } - - if (isArchived !== undefined) { - localVarQueryParameter['isArchived'] = isArchived; - } - - if (isFavorite !== undefined) { - localVarQueryParameter['isFavorite'] = isFavorite; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {number} day - * @param {number} month - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getMemoryLane: async (day: number, month: number, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'day' is not null or undefined - assertParamExists('getMemoryLane', 'day', day) - // verify required parameter 'month' is not null or undefined - assertParamExists('getMemoryLane', 'month', month) - const localVarPath = `/asset/memory-lane`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (day !== undefined) { - localVarQueryParameter['day'] = day; - } - - if (month !== undefined) { - localVarQueryParameter['month'] = month; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {number} [count] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getRandom: async (count?: number, options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/asset/random`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (count !== undefined) { - localVarQueryParameter['count'] = count; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {TimeBucketSize} size - * @param {string} timeBucket - * @param {string} [albumId] - * @param {boolean} [isArchived] - * @param {boolean} [isFavorite] - * @param {boolean} [isTrashed] - * @param {string} [key] - * @param {string} [personId] - * @param {string} [userId] - * @param {boolean} [withPartners] - * @param {boolean} [withStacked] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getTimeBucket: async (size: TimeBucketSize, timeBucket: string, albumId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, key?: string, personId?: string, userId?: string, withPartners?: boolean, withStacked?: boolean, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'size' is not null or undefined - assertParamExists('getTimeBucket', 'size', size) - // verify required parameter 'timeBucket' is not null or undefined - assertParamExists('getTimeBucket', 'timeBucket', timeBucket) - const localVarPath = `/asset/time-bucket`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (albumId !== undefined) { - localVarQueryParameter['albumId'] = albumId; - } - - if (isArchived !== undefined) { - localVarQueryParameter['isArchived'] = isArchived; - } - - if (isFavorite !== undefined) { - localVarQueryParameter['isFavorite'] = isFavorite; - } - - if (isTrashed !== undefined) { - localVarQueryParameter['isTrashed'] = isTrashed; - } - - if (key !== undefined) { - localVarQueryParameter['key'] = key; - } - - if (personId !== undefined) { - localVarQueryParameter['personId'] = personId; - } - - if (size !== undefined) { - localVarQueryParameter['size'] = size; - } - - if (timeBucket !== undefined) { - localVarQueryParameter['timeBucket'] = timeBucket; - } - - if (userId !== undefined) { - localVarQueryParameter['userId'] = userId; - } - - if (withPartners !== undefined) { - localVarQueryParameter['withPartners'] = withPartners; - } - - if (withStacked !== undefined) { - localVarQueryParameter['withStacked'] = withStacked; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {TimeBucketSize} size - * @param {string} [albumId] - * @param {boolean} [isArchived] - * @param {boolean} [isFavorite] - * @param {boolean} [isTrashed] - * @param {string} [key] - * @param {string} [personId] - * @param {string} [userId] - * @param {boolean} [withPartners] - * @param {boolean} [withStacked] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getTimeBuckets: async (size: TimeBucketSize, albumId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, key?: string, personId?: string, userId?: string, withPartners?: boolean, withStacked?: boolean, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'size' is not null or undefined - assertParamExists('getTimeBuckets', 'size', size) - const localVarPath = `/asset/time-buckets`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (albumId !== undefined) { - localVarQueryParameter['albumId'] = albumId; - } - - if (isArchived !== undefined) { - localVarQueryParameter['isArchived'] = isArchived; - } - - if (isFavorite !== undefined) { - localVarQueryParameter['isFavorite'] = isFavorite; - } - - if (isTrashed !== undefined) { - localVarQueryParameter['isTrashed'] = isTrashed; - } - - if (key !== undefined) { - localVarQueryParameter['key'] = key; - } - - if (personId !== undefined) { - localVarQueryParameter['personId'] = personId; - } - - if (size !== undefined) { - localVarQueryParameter['size'] = size; - } - - if (userId !== undefined) { - localVarQueryParameter['userId'] = userId; - } - - if (withPartners !== undefined) { - localVarQueryParameter['withPartners'] = withPartners; - } - - if (withStacked !== undefined) { - localVarQueryParameter['withStacked'] = withStacked; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {AssetJobsDto} assetJobsDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - runAssetJobs: async (assetJobsDto: AssetJobsDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'assetJobsDto' is not null or undefined - assertParamExists('runAssetJobs', 'assetJobsDto', assetJobsDto) - const localVarPath = `/asset/jobs`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(assetJobsDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} [checksum] - * @param {string} [city] - * @param {string} [country] - * @param {string} [createdAfter] - * @param {string} [createdBefore] - * @param {string} [deviceAssetId] - * @param {string} [deviceId] - * @param {string} [encodedVideoPath] - * @param {string} [id] - * @param {boolean} [isArchived] - * @param {boolean} [isEncoded] - * @param {boolean} [isExternal] - * @param {boolean} [isFavorite] - * @param {boolean} [isMotion] - * @param {boolean} [isOffline] - * @param {boolean} [isReadOnly] - * @param {boolean} [isVisible] - * @param {string} [lensModel] - * @param {string} [libraryId] - * @param {string} [make] - * @param {string} [model] - * @param {AssetOrder} [order] - * @param {string} [originalFileName] - * @param {string} [originalPath] - * @param {number} [page] - * @param {string} [resizePath] - * @param {number} [size] - * @param {string} [state] - * @param {string} [takenAfter] - * @param {string} [takenBefore] - * @param {string} [trashedAfter] - * @param {string} [trashedBefore] - * @param {AssetTypeEnum} [type] - * @param {string} [updatedAfter] - * @param {string} [updatedBefore] - * @param {string} [webpPath] - * @param {boolean} [withArchived] - * @param {boolean} [withDeleted] - * @param {boolean} [withExif] - * @param {boolean} [withPeople] - * @param {boolean} [withStacked] - * @param {*} [options] Override http request option. - * @deprecated - * @throws {RequiredError} - */ - searchAssets: async (checksum?: string, city?: string, country?: string, createdAfter?: string, createdBefore?: string, deviceAssetId?: string, deviceId?: string, encodedVideoPath?: string, id?: string, isArchived?: boolean, isEncoded?: boolean, isExternal?: boolean, isFavorite?: boolean, isMotion?: boolean, isOffline?: boolean, isReadOnly?: boolean, isVisible?: boolean, lensModel?: string, libraryId?: string, make?: string, model?: string, order?: AssetOrder, originalFileName?: string, originalPath?: string, page?: number, resizePath?: string, size?: number, state?: string, takenAfter?: string, takenBefore?: string, trashedAfter?: string, trashedBefore?: string, type?: AssetTypeEnum, updatedAfter?: string, updatedBefore?: string, webpPath?: string, withArchived?: boolean, withDeleted?: boolean, withExif?: boolean, withPeople?: boolean, withStacked?: boolean, options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/assets`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (checksum !== undefined) { - localVarQueryParameter['checksum'] = checksum; - } - - if (city !== undefined) { - localVarQueryParameter['city'] = city; - } - - if (country !== undefined) { - localVarQueryParameter['country'] = country; - } - - if (createdAfter !== undefined) { - localVarQueryParameter['createdAfter'] = (createdAfter as any instanceof Date) ? - (createdAfter as any).toISOString() : - createdAfter; - } - - if (createdBefore !== undefined) { - localVarQueryParameter['createdBefore'] = (createdBefore as any instanceof Date) ? - (createdBefore as any).toISOString() : - createdBefore; - } - - if (deviceAssetId !== undefined) { - localVarQueryParameter['deviceAssetId'] = deviceAssetId; - } - - if (deviceId !== undefined) { - localVarQueryParameter['deviceId'] = deviceId; - } - - if (encodedVideoPath !== undefined) { - localVarQueryParameter['encodedVideoPath'] = encodedVideoPath; - } - - if (id !== undefined) { - localVarQueryParameter['id'] = id; - } - - if (isArchived !== undefined) { - localVarQueryParameter['isArchived'] = isArchived; - } - - if (isEncoded !== undefined) { - localVarQueryParameter['isEncoded'] = isEncoded; - } - - if (isExternal !== undefined) { - localVarQueryParameter['isExternal'] = isExternal; - } - - if (isFavorite !== undefined) { - localVarQueryParameter['isFavorite'] = isFavorite; - } - - if (isMotion !== undefined) { - localVarQueryParameter['isMotion'] = isMotion; - } - - if (isOffline !== undefined) { - localVarQueryParameter['isOffline'] = isOffline; - } - - if (isReadOnly !== undefined) { - localVarQueryParameter['isReadOnly'] = isReadOnly; - } - - if (isVisible !== undefined) { - localVarQueryParameter['isVisible'] = isVisible; - } - - if (lensModel !== undefined) { - localVarQueryParameter['lensModel'] = lensModel; - } - - if (libraryId !== undefined) { - localVarQueryParameter['libraryId'] = libraryId; - } - - if (make !== undefined) { - localVarQueryParameter['make'] = make; - } - - if (model !== undefined) { - localVarQueryParameter['model'] = model; - } - - if (order !== undefined) { - localVarQueryParameter['order'] = order; - } - - if (originalFileName !== undefined) { - localVarQueryParameter['originalFileName'] = originalFileName; - } - - if (originalPath !== undefined) { - localVarQueryParameter['originalPath'] = originalPath; - } - - if (page !== undefined) { - localVarQueryParameter['page'] = page; - } - - if (resizePath !== undefined) { - localVarQueryParameter['resizePath'] = resizePath; - } - - if (size !== undefined) { - localVarQueryParameter['size'] = size; - } - - if (state !== undefined) { - localVarQueryParameter['state'] = state; - } - - if (takenAfter !== undefined) { - localVarQueryParameter['takenAfter'] = (takenAfter as any instanceof Date) ? - (takenAfter as any).toISOString() : - takenAfter; - } - - if (takenBefore !== undefined) { - localVarQueryParameter['takenBefore'] = (takenBefore as any instanceof Date) ? - (takenBefore as any).toISOString() : - takenBefore; - } - - if (trashedAfter !== undefined) { - localVarQueryParameter['trashedAfter'] = (trashedAfter as any instanceof Date) ? - (trashedAfter as any).toISOString() : - trashedAfter; - } - - if (trashedBefore !== undefined) { - localVarQueryParameter['trashedBefore'] = (trashedBefore as any instanceof Date) ? - (trashedBefore as any).toISOString() : - trashedBefore; - } - - if (type !== undefined) { - localVarQueryParameter['type'] = type; - } - - if (updatedAfter !== undefined) { - localVarQueryParameter['updatedAfter'] = (updatedAfter as any instanceof Date) ? - (updatedAfter as any).toISOString() : - updatedAfter; - } - - if (updatedBefore !== undefined) { - localVarQueryParameter['updatedBefore'] = (updatedBefore as any instanceof Date) ? - (updatedBefore as any).toISOString() : - updatedBefore; - } - - if (webpPath !== undefined) { - localVarQueryParameter['webpPath'] = webpPath; - } - - if (withArchived !== undefined) { - localVarQueryParameter['withArchived'] = withArchived; - } - - if (withDeleted !== undefined) { - localVarQueryParameter['withDeleted'] = withDeleted; - } - - if (withExif !== undefined) { - localVarQueryParameter['withExif'] = withExif; - } - - if (withPeople !== undefined) { - localVarQueryParameter['withPeople'] = withPeople; - } - - if (withStacked !== undefined) { - localVarQueryParameter['withStacked'] = withStacked; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {boolean} [isThumb] - * @param {boolean} [isWeb] - * @param {string} [key] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - serveFile: async (id: string, isThumb?: boolean, isWeb?: boolean, key?: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('serveFile', 'id', id) - const localVarPath = `/asset/file/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (isThumb !== undefined) { - localVarQueryParameter['isThumb'] = isThumb; - } - - if (isWeb !== undefined) { - localVarQueryParameter['isWeb'] = isWeb; - } - - if (key !== undefined) { - localVarQueryParameter['key'] = key; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {UpdateAssetDto} updateAssetDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updateAsset: async (id: string, updateAssetDto: UpdateAssetDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('updateAsset', 'id', id) - // verify required parameter 'updateAssetDto' is not null or undefined - assertParamExists('updateAsset', 'updateAssetDto', updateAssetDto) - const localVarPath = `/asset/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(updateAssetDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {AssetBulkUpdateDto} assetBulkUpdateDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updateAssets: async (assetBulkUpdateDto: AssetBulkUpdateDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'assetBulkUpdateDto' is not null or undefined - assertParamExists('updateAssets', 'assetBulkUpdateDto', assetBulkUpdateDto) - const localVarPath = `/asset`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(assetBulkUpdateDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {UpdateStackParentDto} updateStackParentDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updateStackParent: async (updateStackParentDto: UpdateStackParentDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'updateStackParentDto' is not null or undefined - assertParamExists('updateStackParent', 'updateStackParentDto', updateStackParentDto) - const localVarPath = `/asset/stack/parent`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(updateStackParentDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {File} assetData - * @param {string} deviceAssetId - * @param {string} deviceId - * @param {string} fileCreatedAt - * @param {string} fileModifiedAt - * @param {string} [key] - * @param {string} [duration] - * @param {boolean} [isArchived] - * @param {boolean} [isExternal] - * @param {boolean} [isFavorite] - * @param {boolean} [isOffline] - * @param {boolean} [isReadOnly] - * @param {boolean} [isVisible] - * @param {string} [libraryId] - * @param {File} [livePhotoData] - * @param {File} [sidecarData] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - uploadFile: async (assetData: File, deviceAssetId: string, deviceId: string, fileCreatedAt: string, fileModifiedAt: string, key?: string, duration?: string, isArchived?: boolean, isExternal?: boolean, isFavorite?: boolean, isOffline?: boolean, isReadOnly?: boolean, isVisible?: boolean, libraryId?: string, livePhotoData?: File, sidecarData?: File, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'assetData' is not null or undefined - assertParamExists('uploadFile', 'assetData', assetData) - // verify required parameter 'deviceAssetId' is not null or undefined - assertParamExists('uploadFile', 'deviceAssetId', deviceAssetId) - // verify required parameter 'deviceId' is not null or undefined - assertParamExists('uploadFile', 'deviceId', deviceId) - // verify required parameter 'fileCreatedAt' is not null or undefined - assertParamExists('uploadFile', 'fileCreatedAt', fileCreatedAt) - // verify required parameter 'fileModifiedAt' is not null or undefined - assertParamExists('uploadFile', 'fileModifiedAt', fileModifiedAt) - const localVarPath = `/asset/upload`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - const localVarFormParams = new ((configuration && configuration.formDataCtor) || FormData)(); - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (key !== undefined) { - localVarQueryParameter['key'] = key; - } - - - if (assetData !== undefined) { - localVarFormParams.append('assetData', assetData as any); - } - - if (deviceAssetId !== undefined) { - localVarFormParams.append('deviceAssetId', deviceAssetId as any); - } - - if (deviceId !== undefined) { - localVarFormParams.append('deviceId', deviceId as any); - } - - if (duration !== undefined) { - localVarFormParams.append('duration', duration as any); - } - - if (fileCreatedAt !== undefined) { - localVarFormParams.append('fileCreatedAt', fileCreatedAt as any); - } - - if (fileModifiedAt !== undefined) { - localVarFormParams.append('fileModifiedAt', fileModifiedAt as any); - } - - if (isArchived !== undefined) { - localVarFormParams.append('isArchived', isArchived as any); - } - - if (isExternal !== undefined) { - localVarFormParams.append('isExternal', isExternal as any); - } - - if (isFavorite !== undefined) { - localVarFormParams.append('isFavorite', isFavorite as any); - } - - if (isOffline !== undefined) { - localVarFormParams.append('isOffline', isOffline as any); - } - - if (isReadOnly !== undefined) { - localVarFormParams.append('isReadOnly', isReadOnly as any); - } - - if (isVisible !== undefined) { - localVarFormParams.append('isVisible', isVisible as any); - } - - if (libraryId !== undefined) { - localVarFormParams.append('libraryId', libraryId as any); - } - - if (livePhotoData !== undefined) { - localVarFormParams.append('livePhotoData', livePhotoData as any); - } - - if (sidecarData !== undefined) { - localVarFormParams.append('sidecarData', sidecarData as any); - } - - - localVarHeaderParameter['Content-Type'] = 'multipart/form-data'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = localVarFormParams; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - } -}; - -/** - * AssetApi - functional programming interface - * @export - */ -export const AssetApiFp = function(configuration?: Configuration) { - const localVarAxiosParamCreator = AssetApiAxiosParamCreator(configuration) - return { - /** - * Checks if assets exist by checksums - * @param {AssetBulkUploadCheckDto} assetBulkUploadCheckDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async checkBulkUpload(assetBulkUploadCheckDto: AssetBulkUploadCheckDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.checkBulkUpload(assetBulkUploadCheckDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.checkBulkUpload']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * Checks if multiple assets exist on the server and returns all existing - used by background backup - * @param {CheckExistingAssetsDto} checkExistingAssetsDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async checkExistingAssets(checkExistingAssetsDto: CheckExistingAssetsDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.checkExistingAssets(checkExistingAssetsDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.checkExistingAssets']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {AssetBulkDeleteDto} assetBulkDeleteDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async deleteAssets(assetBulkDeleteDto: AssetBulkDeleteDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.deleteAssets(assetBulkDeleteDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.deleteAssets']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * Get all AssetEntity belong to the user - * @param {string} [ifNoneMatch] ETag of data already cached on the client - * @param {boolean} [isArchived] - * @param {boolean} [isFavorite] - * @param {number} [skip] - * @param {number} [take] - * @param {string} [updatedAfter] - * @param {string} [updatedBefore] - * @param {string} [userId] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getAllAssets(ifNoneMatch?: string, isArchived?: boolean, isFavorite?: boolean, skip?: number, take?: number, updatedAfter?: string, updatedBefore?: string, userId?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAllAssets(ifNoneMatch, isArchived, isFavorite, skip, take, updatedAfter, updatedBefore, userId, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.getAllAssets']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * Get all asset of a device that are in the database, ID only. - * @param {string} deviceId - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getAllUserAssetsByDeviceId(deviceId: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAllUserAssetsByDeviceId(deviceId, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.getAllUserAssetsByDeviceId']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {string} [key] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getAssetInfo(id: string, key?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetInfo(id, key, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.getAssetInfo']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getAssetSearchTerms(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetSearchTerms(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.getAssetSearchTerms']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {boolean} [isArchived] - * @param {boolean} [isFavorite] - * @param {boolean} [isTrashed] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getAssetStatistics(isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetStatistics(isArchived, isFavorite, isTrashed, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.getAssetStatistics']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {ThumbnailFormat} [format] - * @param {string} [key] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getAssetThumbnail(id: string, format?: ThumbnailFormat, key?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetThumbnail(id, format, key, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.getAssetThumbnail']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getCuratedLocations(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getCuratedLocations(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.getCuratedLocations']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getCuratedObjects(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getCuratedObjects(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.getCuratedObjects']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} [fileCreatedAfter] - * @param {string} [fileCreatedBefore] - * @param {boolean} [isArchived] - * @param {boolean} [isFavorite] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getMapMarkers(fileCreatedAfter?: string, fileCreatedBefore?: string, isArchived?: boolean, isFavorite?: boolean, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getMapMarkers(fileCreatedAfter, fileCreatedBefore, isArchived, isFavorite, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.getMapMarkers']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {number} day - * @param {number} month - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getMemoryLane(day: number, month: number, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getMemoryLane(day, month, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.getMemoryLane']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {number} [count] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getRandom(count?: number, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getRandom(count, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.getRandom']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {TimeBucketSize} size - * @param {string} timeBucket - * @param {string} [albumId] - * @param {boolean} [isArchived] - * @param {boolean} [isFavorite] - * @param {boolean} [isTrashed] - * @param {string} [key] - * @param {string} [personId] - * @param {string} [userId] - * @param {boolean} [withPartners] - * @param {boolean} [withStacked] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getTimeBucket(size: TimeBucketSize, timeBucket: string, albumId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, key?: string, personId?: string, userId?: string, withPartners?: boolean, withStacked?: boolean, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getTimeBucket(size, timeBucket, albumId, isArchived, isFavorite, isTrashed, key, personId, userId, withPartners, withStacked, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.getTimeBucket']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {TimeBucketSize} size - * @param {string} [albumId] - * @param {boolean} [isArchived] - * @param {boolean} [isFavorite] - * @param {boolean} [isTrashed] - * @param {string} [key] - * @param {string} [personId] - * @param {string} [userId] - * @param {boolean} [withPartners] - * @param {boolean} [withStacked] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getTimeBuckets(size: TimeBucketSize, albumId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, key?: string, personId?: string, userId?: string, withPartners?: boolean, withStacked?: boolean, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getTimeBuckets(size, albumId, isArchived, isFavorite, isTrashed, key, personId, userId, withPartners, withStacked, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.getTimeBuckets']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {AssetJobsDto} assetJobsDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async runAssetJobs(assetJobsDto: AssetJobsDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.runAssetJobs(assetJobsDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.runAssetJobs']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} [checksum] - * @param {string} [city] - * @param {string} [country] - * @param {string} [createdAfter] - * @param {string} [createdBefore] - * @param {string} [deviceAssetId] - * @param {string} [deviceId] - * @param {string} [encodedVideoPath] - * @param {string} [id] - * @param {boolean} [isArchived] - * @param {boolean} [isEncoded] - * @param {boolean} [isExternal] - * @param {boolean} [isFavorite] - * @param {boolean} [isMotion] - * @param {boolean} [isOffline] - * @param {boolean} [isReadOnly] - * @param {boolean} [isVisible] - * @param {string} [lensModel] - * @param {string} [libraryId] - * @param {string} [make] - * @param {string} [model] - * @param {AssetOrder} [order] - * @param {string} [originalFileName] - * @param {string} [originalPath] - * @param {number} [page] - * @param {string} [resizePath] - * @param {number} [size] - * @param {string} [state] - * @param {string} [takenAfter] - * @param {string} [takenBefore] - * @param {string} [trashedAfter] - * @param {string} [trashedBefore] - * @param {AssetTypeEnum} [type] - * @param {string} [updatedAfter] - * @param {string} [updatedBefore] - * @param {string} [webpPath] - * @param {boolean} [withArchived] - * @param {boolean} [withDeleted] - * @param {boolean} [withExif] - * @param {boolean} [withPeople] - * @param {boolean} [withStacked] - * @param {*} [options] Override http request option. - * @deprecated - * @throws {RequiredError} - */ - async searchAssets(checksum?: string, city?: string, country?: string, createdAfter?: string, createdBefore?: string, deviceAssetId?: string, deviceId?: string, encodedVideoPath?: string, id?: string, isArchived?: boolean, isEncoded?: boolean, isExternal?: boolean, isFavorite?: boolean, isMotion?: boolean, isOffline?: boolean, isReadOnly?: boolean, isVisible?: boolean, lensModel?: string, libraryId?: string, make?: string, model?: string, order?: AssetOrder, originalFileName?: string, originalPath?: string, page?: number, resizePath?: string, size?: number, state?: string, takenAfter?: string, takenBefore?: string, trashedAfter?: string, trashedBefore?: string, type?: AssetTypeEnum, updatedAfter?: string, updatedBefore?: string, webpPath?: string, withArchived?: boolean, withDeleted?: boolean, withExif?: boolean, withPeople?: boolean, withStacked?: boolean, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.searchAssets(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.searchAssets']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {boolean} [isThumb] - * @param {boolean} [isWeb] - * @param {string} [key] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async serveFile(id: string, isThumb?: boolean, isWeb?: boolean, key?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.serveFile(id, isThumb, isWeb, key, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.serveFile']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {UpdateAssetDto} updateAssetDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async updateAsset(id: string, updateAssetDto: UpdateAssetDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.updateAsset(id, updateAssetDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.updateAsset']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {AssetBulkUpdateDto} assetBulkUpdateDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async updateAssets(assetBulkUpdateDto: AssetBulkUpdateDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.updateAssets(assetBulkUpdateDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.updateAssets']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {UpdateStackParentDto} updateStackParentDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async updateStackParent(updateStackParentDto: UpdateStackParentDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.updateStackParent(updateStackParentDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.updateStackParent']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {File} assetData - * @param {string} deviceAssetId - * @param {string} deviceId - * @param {string} fileCreatedAt - * @param {string} fileModifiedAt - * @param {string} [key] - * @param {string} [duration] - * @param {boolean} [isArchived] - * @param {boolean} [isExternal] - * @param {boolean} [isFavorite] - * @param {boolean} [isOffline] - * @param {boolean} [isReadOnly] - * @param {boolean} [isVisible] - * @param {string} [libraryId] - * @param {File} [livePhotoData] - * @param {File} [sidecarData] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async uploadFile(assetData: File, deviceAssetId: string, deviceId: string, fileCreatedAt: string, fileModifiedAt: string, key?: string, duration?: string, isArchived?: boolean, isExternal?: boolean, isFavorite?: boolean, isOffline?: boolean, isReadOnly?: boolean, isVisible?: boolean, libraryId?: string, livePhotoData?: File, sidecarData?: File, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.uploadFile(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key, duration, isArchived, isExternal, isFavorite, isOffline, isReadOnly, isVisible, libraryId, livePhotoData, sidecarData, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AssetApi.uploadFile']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - } -}; - -/** - * AssetApi - factory interface - * @export - */ -export const AssetApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { - const localVarFp = AssetApiFp(configuration) - return { - /** - * Checks if assets exist by checksums - * @param {AssetApiCheckBulkUploadRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - checkBulkUpload(requestParameters: AssetApiCheckBulkUploadRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.checkBulkUpload(requestParameters.assetBulkUploadCheckDto, options).then((request) => request(axios, basePath)); - }, - /** - * Checks if multiple assets exist on the server and returns all existing - used by background backup - * @param {AssetApiCheckExistingAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - checkExistingAssets(requestParameters: AssetApiCheckExistingAssetsRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.checkExistingAssets(requestParameters.checkExistingAssetsDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AssetApiDeleteAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - deleteAssets(requestParameters: AssetApiDeleteAssetsRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.deleteAssets(requestParameters.assetBulkDeleteDto, options).then((request) => request(axios, basePath)); - }, - /** - * Get all AssetEntity belong to the user - * @param {AssetApiGetAllAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAllAssets(requestParameters: AssetApiGetAllAssetsRequest = {}, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getAllAssets(requestParameters.ifNoneMatch, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.skip, requestParameters.take, requestParameters.updatedAfter, requestParameters.updatedBefore, requestParameters.userId, options).then((request) => request(axios, basePath)); - }, - /** - * Get all asset of a device that are in the database, ID only. - * @param {AssetApiGetAllUserAssetsByDeviceIdRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAllUserAssetsByDeviceId(requestParameters: AssetApiGetAllUserAssetsByDeviceIdRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getAllUserAssetsByDeviceId(requestParameters.deviceId, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AssetApiGetAssetInfoRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAssetInfo(requestParameters: AssetApiGetAssetInfoRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getAssetInfo(requestParameters.id, requestParameters.key, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAssetSearchTerms(options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getAssetSearchTerms(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AssetApiGetAssetStatisticsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAssetStatistics(requestParameters: AssetApiGetAssetStatisticsRequest = {}, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getAssetStatistics(requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AssetApiGetAssetThumbnailRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAssetThumbnail(requestParameters: AssetApiGetAssetThumbnailRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getAssetThumbnail(requestParameters.id, requestParameters.format, requestParameters.key, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getCuratedLocations(options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getCuratedLocations(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getCuratedObjects(options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getCuratedObjects(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AssetApiGetMapMarkersRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getMapMarkers(requestParameters: AssetApiGetMapMarkersRequest = {}, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getMapMarkers(requestParameters.fileCreatedAfter, requestParameters.fileCreatedBefore, requestParameters.isArchived, requestParameters.isFavorite, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AssetApiGetMemoryLaneRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getMemoryLane(requestParameters: AssetApiGetMemoryLaneRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getMemoryLane(requestParameters.day, requestParameters.month, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AssetApiGetRandomRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getRandom(requestParameters: AssetApiGetRandomRequest = {}, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getRandom(requestParameters.count, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AssetApiGetTimeBucketRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getTimeBucket(requestParameters: AssetApiGetTimeBucketRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.albumId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.key, requestParameters.personId, requestParameters.userId, requestParameters.withPartners, requestParameters.withStacked, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AssetApiGetTimeBucketsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getTimeBuckets(requestParameters.size, requestParameters.albumId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.key, requestParameters.personId, requestParameters.userId, requestParameters.withPartners, requestParameters.withStacked, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AssetApiRunAssetJobsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - runAssetJobs(requestParameters: AssetApiRunAssetJobsRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.runAssetJobs(requestParameters.assetJobsDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AssetApiSearchAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @deprecated - * @throws {RequiredError} - */ - searchAssets(requestParameters: AssetApiSearchAssetsRequest = {}, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.searchAssets(requestParameters.checksum, requestParameters.city, requestParameters.country, requestParameters.createdAfter, requestParameters.createdBefore, requestParameters.deviceAssetId, requestParameters.deviceId, requestParameters.encodedVideoPath, requestParameters.id, requestParameters.isArchived, requestParameters.isEncoded, requestParameters.isExternal, requestParameters.isFavorite, requestParameters.isMotion, requestParameters.isOffline, requestParameters.isReadOnly, requestParameters.isVisible, requestParameters.lensModel, requestParameters.libraryId, requestParameters.make, requestParameters.model, requestParameters.order, requestParameters.originalFileName, requestParameters.originalPath, requestParameters.page, requestParameters.resizePath, requestParameters.size, requestParameters.state, requestParameters.takenAfter, requestParameters.takenBefore, requestParameters.trashedAfter, requestParameters.trashedBefore, requestParameters.type, requestParameters.updatedAfter, requestParameters.updatedBefore, requestParameters.webpPath, requestParameters.withArchived, requestParameters.withDeleted, requestParameters.withExif, requestParameters.withPeople, requestParameters.withStacked, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AssetApiServeFileRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - serveFile(requestParameters: AssetApiServeFileRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.serveFile(requestParameters.id, requestParameters.isThumb, requestParameters.isWeb, requestParameters.key, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AssetApiUpdateAssetRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updateAsset(requestParameters: AssetApiUpdateAssetRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.updateAsset(requestParameters.id, requestParameters.updateAssetDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AssetApiUpdateAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updateAssets(requestParameters: AssetApiUpdateAssetsRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.updateAssets(requestParameters.assetBulkUpdateDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AssetApiUpdateStackParentRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updateStackParent(requestParameters: AssetApiUpdateStackParentRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.updateStackParent(requestParameters.updateStackParentDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AssetApiUploadFileRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - uploadFile(requestParameters: AssetApiUploadFileRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.uploadFile(requestParameters.assetData, requestParameters.deviceAssetId, requestParameters.deviceId, requestParameters.fileCreatedAt, requestParameters.fileModifiedAt, requestParameters.key, requestParameters.duration, requestParameters.isArchived, requestParameters.isExternal, requestParameters.isFavorite, requestParameters.isOffline, requestParameters.isReadOnly, requestParameters.isVisible, requestParameters.libraryId, requestParameters.livePhotoData, requestParameters.sidecarData, options).then((request) => request(axios, basePath)); - }, - }; -}; - -/** - * Request parameters for checkBulkUpload operation in AssetApi. - * @export - * @interface AssetApiCheckBulkUploadRequest - */ -export interface AssetApiCheckBulkUploadRequest { - /** - * - * @type {AssetBulkUploadCheckDto} - * @memberof AssetApiCheckBulkUpload - */ - readonly assetBulkUploadCheckDto: AssetBulkUploadCheckDto -} - -/** - * Request parameters for checkExistingAssets operation in AssetApi. - * @export - * @interface AssetApiCheckExistingAssetsRequest - */ -export interface AssetApiCheckExistingAssetsRequest { - /** - * - * @type {CheckExistingAssetsDto} - * @memberof AssetApiCheckExistingAssets - */ - readonly checkExistingAssetsDto: CheckExistingAssetsDto -} - -/** - * Request parameters for deleteAssets operation in AssetApi. - * @export - * @interface AssetApiDeleteAssetsRequest - */ -export interface AssetApiDeleteAssetsRequest { - /** - * - * @type {AssetBulkDeleteDto} - * @memberof AssetApiDeleteAssets - */ - readonly assetBulkDeleteDto: AssetBulkDeleteDto -} - -/** - * Request parameters for getAllAssets operation in AssetApi. - * @export - * @interface AssetApiGetAllAssetsRequest - */ -export interface AssetApiGetAllAssetsRequest { - /** - * ETag of data already cached on the client - * @type {string} - * @memberof AssetApiGetAllAssets - */ - readonly ifNoneMatch?: string - - /** - * - * @type {boolean} - * @memberof AssetApiGetAllAssets - */ - readonly isArchived?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiGetAllAssets - */ - readonly isFavorite?: boolean - - /** - * - * @type {number} - * @memberof AssetApiGetAllAssets - */ - readonly skip?: number - - /** - * - * @type {number} - * @memberof AssetApiGetAllAssets - */ - readonly take?: number - - /** - * - * @type {string} - * @memberof AssetApiGetAllAssets - */ - readonly updatedAfter?: string - - /** - * - * @type {string} - * @memberof AssetApiGetAllAssets - */ - readonly updatedBefore?: string - - /** - * - * @type {string} - * @memberof AssetApiGetAllAssets - */ - readonly userId?: string -} - -/** - * Request parameters for getAllUserAssetsByDeviceId operation in AssetApi. - * @export - * @interface AssetApiGetAllUserAssetsByDeviceIdRequest - */ -export interface AssetApiGetAllUserAssetsByDeviceIdRequest { - /** - * - * @type {string} - * @memberof AssetApiGetAllUserAssetsByDeviceId - */ - readonly deviceId: string -} - -/** - * Request parameters for getAssetInfo operation in AssetApi. - * @export - * @interface AssetApiGetAssetInfoRequest - */ -export interface AssetApiGetAssetInfoRequest { - /** - * - * @type {string} - * @memberof AssetApiGetAssetInfo - */ - readonly id: string - - /** - * - * @type {string} - * @memberof AssetApiGetAssetInfo - */ - readonly key?: string -} - -/** - * Request parameters for getAssetStatistics operation in AssetApi. - * @export - * @interface AssetApiGetAssetStatisticsRequest - */ -export interface AssetApiGetAssetStatisticsRequest { - /** - * - * @type {boolean} - * @memberof AssetApiGetAssetStatistics - */ - readonly isArchived?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiGetAssetStatistics - */ - readonly isFavorite?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiGetAssetStatistics - */ - readonly isTrashed?: boolean -} - -/** - * Request parameters for getAssetThumbnail operation in AssetApi. - * @export - * @interface AssetApiGetAssetThumbnailRequest - */ -export interface AssetApiGetAssetThumbnailRequest { - /** - * - * @type {string} - * @memberof AssetApiGetAssetThumbnail - */ - readonly id: string - - /** - * - * @type {ThumbnailFormat} - * @memberof AssetApiGetAssetThumbnail - */ - readonly format?: ThumbnailFormat - - /** - * - * @type {string} - * @memberof AssetApiGetAssetThumbnail - */ - readonly key?: string -} - -/** - * Request parameters for getMapMarkers operation in AssetApi. - * @export - * @interface AssetApiGetMapMarkersRequest - */ -export interface AssetApiGetMapMarkersRequest { - /** - * - * @type {string} - * @memberof AssetApiGetMapMarkers - */ - readonly fileCreatedAfter?: string - - /** - * - * @type {string} - * @memberof AssetApiGetMapMarkers - */ - readonly fileCreatedBefore?: string - - /** - * - * @type {boolean} - * @memberof AssetApiGetMapMarkers - */ - readonly isArchived?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiGetMapMarkers - */ - readonly isFavorite?: boolean -} - -/** - * Request parameters for getMemoryLane operation in AssetApi. - * @export - * @interface AssetApiGetMemoryLaneRequest - */ -export interface AssetApiGetMemoryLaneRequest { - /** - * - * @type {number} - * @memberof AssetApiGetMemoryLane - */ - readonly day: number - - /** - * - * @type {number} - * @memberof AssetApiGetMemoryLane - */ - readonly month: number -} - -/** - * Request parameters for getRandom operation in AssetApi. - * @export - * @interface AssetApiGetRandomRequest - */ -export interface AssetApiGetRandomRequest { - /** - * - * @type {number} - * @memberof AssetApiGetRandom - */ - readonly count?: number -} - -/** - * Request parameters for getTimeBucket operation in AssetApi. - * @export - * @interface AssetApiGetTimeBucketRequest - */ -export interface AssetApiGetTimeBucketRequest { - /** - * - * @type {TimeBucketSize} - * @memberof AssetApiGetTimeBucket - */ - readonly size: TimeBucketSize - - /** - * - * @type {string} - * @memberof AssetApiGetTimeBucket - */ - readonly timeBucket: string - - /** - * - * @type {string} - * @memberof AssetApiGetTimeBucket - */ - readonly albumId?: string - - /** - * - * @type {boolean} - * @memberof AssetApiGetTimeBucket - */ - readonly isArchived?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiGetTimeBucket - */ - readonly isFavorite?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiGetTimeBucket - */ - readonly isTrashed?: boolean - - /** - * - * @type {string} - * @memberof AssetApiGetTimeBucket - */ - readonly key?: string - - /** - * - * @type {string} - * @memberof AssetApiGetTimeBucket - */ - readonly personId?: string - - /** - * - * @type {string} - * @memberof AssetApiGetTimeBucket - */ - readonly userId?: string - - /** - * - * @type {boolean} - * @memberof AssetApiGetTimeBucket - */ - readonly withPartners?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiGetTimeBucket - */ - readonly withStacked?: boolean -} - -/** - * Request parameters for getTimeBuckets operation in AssetApi. - * @export - * @interface AssetApiGetTimeBucketsRequest - */ -export interface AssetApiGetTimeBucketsRequest { - /** - * - * @type {TimeBucketSize} - * @memberof AssetApiGetTimeBuckets - */ - readonly size: TimeBucketSize - - /** - * - * @type {string} - * @memberof AssetApiGetTimeBuckets - */ - readonly albumId?: string - - /** - * - * @type {boolean} - * @memberof AssetApiGetTimeBuckets - */ - readonly isArchived?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiGetTimeBuckets - */ - readonly isFavorite?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiGetTimeBuckets - */ - readonly isTrashed?: boolean - - /** - * - * @type {string} - * @memberof AssetApiGetTimeBuckets - */ - readonly key?: string - - /** - * - * @type {string} - * @memberof AssetApiGetTimeBuckets - */ - readonly personId?: string - - /** - * - * @type {string} - * @memberof AssetApiGetTimeBuckets - */ - readonly userId?: string - - /** - * - * @type {boolean} - * @memberof AssetApiGetTimeBuckets - */ - readonly withPartners?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiGetTimeBuckets - */ - readonly withStacked?: boolean -} - -/** - * Request parameters for runAssetJobs operation in AssetApi. - * @export - * @interface AssetApiRunAssetJobsRequest - */ -export interface AssetApiRunAssetJobsRequest { - /** - * - * @type {AssetJobsDto} - * @memberof AssetApiRunAssetJobs - */ - readonly assetJobsDto: AssetJobsDto -} - -/** - * Request parameters for searchAssets operation in AssetApi. - * @export - * @interface AssetApiSearchAssetsRequest - */ -export interface AssetApiSearchAssetsRequest { - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly checksum?: string - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly city?: string - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly country?: string - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly createdAfter?: string - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly createdBefore?: string - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly deviceAssetId?: string - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly deviceId?: string - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly encodedVideoPath?: string - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly id?: string - - /** - * - * @type {boolean} - * @memberof AssetApiSearchAssets - */ - readonly isArchived?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiSearchAssets - */ - readonly isEncoded?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiSearchAssets - */ - readonly isExternal?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiSearchAssets - */ - readonly isFavorite?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiSearchAssets - */ - readonly isMotion?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiSearchAssets - */ - readonly isOffline?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiSearchAssets - */ - readonly isReadOnly?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiSearchAssets - */ - readonly isVisible?: boolean - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly lensModel?: string - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly libraryId?: string - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly make?: string - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly model?: string - - /** - * - * @type {AssetOrder} - * @memberof AssetApiSearchAssets - */ - readonly order?: AssetOrder - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly originalFileName?: string - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly originalPath?: string - - /** - * - * @type {number} - * @memberof AssetApiSearchAssets - */ - readonly page?: number - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly resizePath?: string - - /** - * - * @type {number} - * @memberof AssetApiSearchAssets - */ - readonly size?: number - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly state?: string - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly takenAfter?: string - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly takenBefore?: string - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly trashedAfter?: string - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly trashedBefore?: string - - /** - * - * @type {AssetTypeEnum} - * @memberof AssetApiSearchAssets - */ - readonly type?: AssetTypeEnum - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly updatedAfter?: string - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly updatedBefore?: string - - /** - * - * @type {string} - * @memberof AssetApiSearchAssets - */ - readonly webpPath?: string - - /** - * - * @type {boolean} - * @memberof AssetApiSearchAssets - */ - readonly withArchived?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiSearchAssets - */ - readonly withDeleted?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiSearchAssets - */ - readonly withExif?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiSearchAssets - */ - readonly withPeople?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiSearchAssets - */ - readonly withStacked?: boolean -} - -/** - * Request parameters for serveFile operation in AssetApi. - * @export - * @interface AssetApiServeFileRequest - */ -export interface AssetApiServeFileRequest { - /** - * - * @type {string} - * @memberof AssetApiServeFile - */ - readonly id: string - - /** - * - * @type {boolean} - * @memberof AssetApiServeFile - */ - readonly isThumb?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiServeFile - */ - readonly isWeb?: boolean - - /** - * - * @type {string} - * @memberof AssetApiServeFile - */ - readonly key?: string -} - -/** - * Request parameters for updateAsset operation in AssetApi. - * @export - * @interface AssetApiUpdateAssetRequest - */ -export interface AssetApiUpdateAssetRequest { - /** - * - * @type {string} - * @memberof AssetApiUpdateAsset - */ - readonly id: string - - /** - * - * @type {UpdateAssetDto} - * @memberof AssetApiUpdateAsset - */ - readonly updateAssetDto: UpdateAssetDto -} - -/** - * Request parameters for updateAssets operation in AssetApi. - * @export - * @interface AssetApiUpdateAssetsRequest - */ -export interface AssetApiUpdateAssetsRequest { - /** - * - * @type {AssetBulkUpdateDto} - * @memberof AssetApiUpdateAssets - */ - readonly assetBulkUpdateDto: AssetBulkUpdateDto -} - -/** - * Request parameters for updateStackParent operation in AssetApi. - * @export - * @interface AssetApiUpdateStackParentRequest - */ -export interface AssetApiUpdateStackParentRequest { - /** - * - * @type {UpdateStackParentDto} - * @memberof AssetApiUpdateStackParent - */ - readonly updateStackParentDto: UpdateStackParentDto -} - -/** - * Request parameters for uploadFile operation in AssetApi. - * @export - * @interface AssetApiUploadFileRequest - */ -export interface AssetApiUploadFileRequest { - /** - * - * @type {File} - * @memberof AssetApiUploadFile - */ - readonly assetData: File - - /** - * - * @type {string} - * @memberof AssetApiUploadFile - */ - readonly deviceAssetId: string - - /** - * - * @type {string} - * @memberof AssetApiUploadFile - */ - readonly deviceId: string - - /** - * - * @type {string} - * @memberof AssetApiUploadFile - */ - readonly fileCreatedAt: string - - /** - * - * @type {string} - * @memberof AssetApiUploadFile - */ - readonly fileModifiedAt: string - - /** - * - * @type {string} - * @memberof AssetApiUploadFile - */ - readonly key?: string - - /** - * - * @type {string} - * @memberof AssetApiUploadFile - */ - readonly duration?: string - - /** - * - * @type {boolean} - * @memberof AssetApiUploadFile - */ - readonly isArchived?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiUploadFile - */ - readonly isExternal?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiUploadFile - */ - readonly isFavorite?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiUploadFile - */ - readonly isOffline?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiUploadFile - */ - readonly isReadOnly?: boolean - - /** - * - * @type {boolean} - * @memberof AssetApiUploadFile - */ - readonly isVisible?: boolean - - /** - * - * @type {string} - * @memberof AssetApiUploadFile - */ - readonly libraryId?: string - - /** - * - * @type {File} - * @memberof AssetApiUploadFile - */ - readonly livePhotoData?: File - - /** - * - * @type {File} - * @memberof AssetApiUploadFile - */ - readonly sidecarData?: File -} - -/** - * AssetApi - object-oriented interface - * @export - * @class AssetApi - * @extends {BaseAPI} - */ -export class AssetApi extends BaseAPI { - /** - * Checks if assets exist by checksums - * @param {AssetApiCheckBulkUploadRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public checkBulkUpload(requestParameters: AssetApiCheckBulkUploadRequest, options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).checkBulkUpload(requestParameters.assetBulkUploadCheckDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * Checks if multiple assets exist on the server and returns all existing - used by background backup - * @param {AssetApiCheckExistingAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public checkExistingAssets(requestParameters: AssetApiCheckExistingAssetsRequest, options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).checkExistingAssets(requestParameters.checkExistingAssetsDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AssetApiDeleteAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public deleteAssets(requestParameters: AssetApiDeleteAssetsRequest, options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).deleteAssets(requestParameters.assetBulkDeleteDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * Get all AssetEntity belong to the user - * @param {AssetApiGetAllAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public getAllAssets(requestParameters: AssetApiGetAllAssetsRequest = {}, options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).getAllAssets(requestParameters.ifNoneMatch, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.skip, requestParameters.take, requestParameters.updatedAfter, requestParameters.updatedBefore, requestParameters.userId, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * Get all asset of a device that are in the database, ID only. - * @param {AssetApiGetAllUserAssetsByDeviceIdRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public getAllUserAssetsByDeviceId(requestParameters: AssetApiGetAllUserAssetsByDeviceIdRequest, options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).getAllUserAssetsByDeviceId(requestParameters.deviceId, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AssetApiGetAssetInfoRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public getAssetInfo(requestParameters: AssetApiGetAssetInfoRequest, options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).getAssetInfo(requestParameters.id, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public getAssetSearchTerms(options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).getAssetSearchTerms(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AssetApiGetAssetStatisticsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public getAssetStatistics(requestParameters: AssetApiGetAssetStatisticsRequest = {}, options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).getAssetStatistics(requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AssetApiGetAssetThumbnailRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public getAssetThumbnail(requestParameters: AssetApiGetAssetThumbnailRequest, options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).getAssetThumbnail(requestParameters.id, requestParameters.format, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public getCuratedLocations(options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).getCuratedLocations(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public getCuratedObjects(options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).getCuratedObjects(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AssetApiGetMapMarkersRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public getMapMarkers(requestParameters: AssetApiGetMapMarkersRequest = {}, options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).getMapMarkers(requestParameters.fileCreatedAfter, requestParameters.fileCreatedBefore, requestParameters.isArchived, requestParameters.isFavorite, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AssetApiGetMemoryLaneRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public getMemoryLane(requestParameters: AssetApiGetMemoryLaneRequest, options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).getMemoryLane(requestParameters.day, requestParameters.month, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AssetApiGetRandomRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public getRandom(requestParameters: AssetApiGetRandomRequest = {}, options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).getRandom(requestParameters.count, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AssetApiGetTimeBucketRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public getTimeBucket(requestParameters: AssetApiGetTimeBucketRequest, options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).getTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.albumId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.key, requestParameters.personId, requestParameters.userId, requestParameters.withPartners, requestParameters.withStacked, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AssetApiGetTimeBucketsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).getTimeBuckets(requestParameters.size, requestParameters.albumId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.key, requestParameters.personId, requestParameters.userId, requestParameters.withPartners, requestParameters.withStacked, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AssetApiRunAssetJobsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public runAssetJobs(requestParameters: AssetApiRunAssetJobsRequest, options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).runAssetJobs(requestParameters.assetJobsDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AssetApiSearchAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @deprecated - * @throws {RequiredError} - * @memberof AssetApi - */ - public searchAssets(requestParameters: AssetApiSearchAssetsRequest = {}, options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).searchAssets(requestParameters.checksum, requestParameters.city, requestParameters.country, requestParameters.createdAfter, requestParameters.createdBefore, requestParameters.deviceAssetId, requestParameters.deviceId, requestParameters.encodedVideoPath, requestParameters.id, requestParameters.isArchived, requestParameters.isEncoded, requestParameters.isExternal, requestParameters.isFavorite, requestParameters.isMotion, requestParameters.isOffline, requestParameters.isReadOnly, requestParameters.isVisible, requestParameters.lensModel, requestParameters.libraryId, requestParameters.make, requestParameters.model, requestParameters.order, requestParameters.originalFileName, requestParameters.originalPath, requestParameters.page, requestParameters.resizePath, requestParameters.size, requestParameters.state, requestParameters.takenAfter, requestParameters.takenBefore, requestParameters.trashedAfter, requestParameters.trashedBefore, requestParameters.type, requestParameters.updatedAfter, requestParameters.updatedBefore, requestParameters.webpPath, requestParameters.withArchived, requestParameters.withDeleted, requestParameters.withExif, requestParameters.withPeople, requestParameters.withStacked, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AssetApiServeFileRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public serveFile(requestParameters: AssetApiServeFileRequest, options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).serveFile(requestParameters.id, requestParameters.isThumb, requestParameters.isWeb, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AssetApiUpdateAssetRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public updateAsset(requestParameters: AssetApiUpdateAssetRequest, options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).updateAsset(requestParameters.id, requestParameters.updateAssetDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AssetApiUpdateAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public updateAssets(requestParameters: AssetApiUpdateAssetsRequest, options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).updateAssets(requestParameters.assetBulkUpdateDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AssetApiUpdateStackParentRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public updateStackParent(requestParameters: AssetApiUpdateStackParentRequest, options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).updateStackParent(requestParameters.updateStackParentDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AssetApiUploadFileRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public uploadFile(requestParameters: AssetApiUploadFileRequest, options?: RawAxiosRequestConfig) { - return AssetApiFp(this.configuration).uploadFile(requestParameters.assetData, requestParameters.deviceAssetId, requestParameters.deviceId, requestParameters.fileCreatedAt, requestParameters.fileModifiedAt, requestParameters.key, requestParameters.duration, requestParameters.isArchived, requestParameters.isExternal, requestParameters.isFavorite, requestParameters.isOffline, requestParameters.isReadOnly, requestParameters.isVisible, requestParameters.libraryId, requestParameters.livePhotoData, requestParameters.sidecarData, options).then((request) => request(this.axios, this.basePath)); - } -} - - - -/** - * AuditApi - axios parameter creator - * @export - */ -export const AuditApiAxiosParamCreator = function (configuration?: Configuration) { - return { - /** - * - * @param {FileReportFixDto} fileReportFixDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - fixAuditFiles: async (fileReportFixDto: FileReportFixDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'fileReportFixDto' is not null or undefined - assertParamExists('fixAuditFiles', 'fileReportFixDto', fileReportFixDto) - const localVarPath = `/audit/file-report/fix`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(fileReportFixDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} after - * @param {EntityType} entityType - * @param {string} [userId] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAuditDeletes: async (after: string, entityType: EntityType, userId?: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'after' is not null or undefined - assertParamExists('getAuditDeletes', 'after', after) - // verify required parameter 'entityType' is not null or undefined - assertParamExists('getAuditDeletes', 'entityType', entityType) - const localVarPath = `/audit/deletes`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (after !== undefined) { - localVarQueryParameter['after'] = (after as any instanceof Date) ? - (after as any).toISOString() : - after; - } - - if (entityType !== undefined) { - localVarQueryParameter['entityType'] = entityType; - } - - if (userId !== undefined) { - localVarQueryParameter['userId'] = userId; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAuditFiles: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/audit/file-report`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {FileChecksumDto} fileChecksumDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getFileChecksums: async (fileChecksumDto: FileChecksumDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'fileChecksumDto' is not null or undefined - assertParamExists('getFileChecksums', 'fileChecksumDto', fileChecksumDto) - const localVarPath = `/audit/file-report/checksum`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(fileChecksumDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - } -}; - -/** - * AuditApi - functional programming interface - * @export - */ -export const AuditApiFp = function(configuration?: Configuration) { - const localVarAxiosParamCreator = AuditApiAxiosParamCreator(configuration) - return { - /** - * - * @param {FileReportFixDto} fileReportFixDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async fixAuditFiles(fileReportFixDto: FileReportFixDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.fixAuditFiles(fileReportFixDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AuditApi.fixAuditFiles']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} after - * @param {EntityType} entityType - * @param {string} [userId] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getAuditDeletes(after: string, entityType: EntityType, userId?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAuditDeletes(after, entityType, userId, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AuditApi.getAuditDeletes']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getAuditFiles(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAuditFiles(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AuditApi.getAuditFiles']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {FileChecksumDto} fileChecksumDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getFileChecksums(fileChecksumDto: FileChecksumDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getFileChecksums(fileChecksumDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AuditApi.getFileChecksums']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - } -}; - -/** - * AuditApi - factory interface - * @export - */ -export const AuditApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { - const localVarFp = AuditApiFp(configuration) - return { - /** - * - * @param {AuditApiFixAuditFilesRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - fixAuditFiles(requestParameters: AuditApiFixAuditFilesRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.fixAuditFiles(requestParameters.fileReportFixDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AuditApiGetAuditDeletesRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAuditDeletes(requestParameters: AuditApiGetAuditDeletesRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getAuditDeletes(requestParameters.after, requestParameters.entityType, requestParameters.userId, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAuditFiles(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getAuditFiles(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AuditApiGetFileChecksumsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getFileChecksums(requestParameters: AuditApiGetFileChecksumsRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getFileChecksums(requestParameters.fileChecksumDto, options).then((request) => request(axios, basePath)); - }, - }; -}; - -/** - * Request parameters for fixAuditFiles operation in AuditApi. - * @export - * @interface AuditApiFixAuditFilesRequest - */ -export interface AuditApiFixAuditFilesRequest { - /** - * - * @type {FileReportFixDto} - * @memberof AuditApiFixAuditFiles - */ - readonly fileReportFixDto: FileReportFixDto -} - -/** - * Request parameters for getAuditDeletes operation in AuditApi. - * @export - * @interface AuditApiGetAuditDeletesRequest - */ -export interface AuditApiGetAuditDeletesRequest { - /** - * - * @type {string} - * @memberof AuditApiGetAuditDeletes - */ - readonly after: string - - /** - * - * @type {EntityType} - * @memberof AuditApiGetAuditDeletes - */ - readonly entityType: EntityType - - /** - * - * @type {string} - * @memberof AuditApiGetAuditDeletes - */ - readonly userId?: string -} - -/** - * Request parameters for getFileChecksums operation in AuditApi. - * @export - * @interface AuditApiGetFileChecksumsRequest - */ -export interface AuditApiGetFileChecksumsRequest { - /** - * - * @type {FileChecksumDto} - * @memberof AuditApiGetFileChecksums - */ - readonly fileChecksumDto: FileChecksumDto -} - -/** - * AuditApi - object-oriented interface - * @export - * @class AuditApi - * @extends {BaseAPI} - */ -export class AuditApi extends BaseAPI { - /** - * - * @param {AuditApiFixAuditFilesRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AuditApi - */ - public fixAuditFiles(requestParameters: AuditApiFixAuditFilesRequest, options?: RawAxiosRequestConfig) { - return AuditApiFp(this.configuration).fixAuditFiles(requestParameters.fileReportFixDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AuditApiGetAuditDeletesRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AuditApi - */ - public getAuditDeletes(requestParameters: AuditApiGetAuditDeletesRequest, options?: RawAxiosRequestConfig) { - return AuditApiFp(this.configuration).getAuditDeletes(requestParameters.after, requestParameters.entityType, requestParameters.userId, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AuditApi - */ - public getAuditFiles(options?: RawAxiosRequestConfig) { - return AuditApiFp(this.configuration).getAuditFiles(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AuditApiGetFileChecksumsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AuditApi - */ - public getFileChecksums(requestParameters: AuditApiGetFileChecksumsRequest, options?: RawAxiosRequestConfig) { - return AuditApiFp(this.configuration).getFileChecksums(requestParameters.fileChecksumDto, options).then((request) => request(this.axios, this.basePath)); - } -} - - - -/** - * AuthenticationApi - axios parameter creator - * @export - */ -export const AuthenticationApiAxiosParamCreator = function (configuration?: Configuration) { - return { - /** - * - * @param {ChangePasswordDto} changePasswordDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - changePassword: async (changePasswordDto: ChangePasswordDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'changePasswordDto' is not null or undefined - assertParamExists('changePassword', 'changePasswordDto', changePasswordDto) - const localVarPath = `/auth/change-password`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(changePasswordDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAuthDevices: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/auth/devices`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {LoginCredentialDto} loginCredentialDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - login: async (loginCredentialDto: LoginCredentialDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'loginCredentialDto' is not null or undefined - assertParamExists('login', 'loginCredentialDto', loginCredentialDto) - const localVarPath = `/auth/login`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(loginCredentialDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - logout: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/auth/logout`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - logoutAuthDevice: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('logoutAuthDevice', 'id', id) - const localVarPath = `/auth/devices/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - logoutAuthDevices: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/auth/devices`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {SignUpDto} signUpDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - signUpAdmin: async (signUpDto: SignUpDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'signUpDto' is not null or undefined - assertParamExists('signUpAdmin', 'signUpDto', signUpDto) - const localVarPath = `/auth/admin-sign-up`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(signUpDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - validateAccessToken: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/auth/validateToken`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - } -}; - -/** - * AuthenticationApi - functional programming interface - * @export - */ -export const AuthenticationApiFp = function(configuration?: Configuration) { - const localVarAxiosParamCreator = AuthenticationApiAxiosParamCreator(configuration) - return { - /** - * - * @param {ChangePasswordDto} changePasswordDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async changePassword(changePasswordDto: ChangePasswordDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.changePassword(changePasswordDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AuthenticationApi.changePassword']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getAuthDevices(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAuthDevices(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AuthenticationApi.getAuthDevices']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {LoginCredentialDto} loginCredentialDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async login(loginCredentialDto: LoginCredentialDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.login(loginCredentialDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AuthenticationApi.login']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async logout(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.logout(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AuthenticationApi.logout']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async logoutAuthDevice(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.logoutAuthDevice(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AuthenticationApi.logoutAuthDevice']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async logoutAuthDevices(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.logoutAuthDevices(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AuthenticationApi.logoutAuthDevices']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {SignUpDto} signUpDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async signUpAdmin(signUpDto: SignUpDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.signUpAdmin(signUpDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AuthenticationApi.signUpAdmin']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async validateAccessToken(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.validateAccessToken(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['AuthenticationApi.validateAccessToken']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - } -}; - -/** - * AuthenticationApi - factory interface - * @export - */ -export const AuthenticationApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { - const localVarFp = AuthenticationApiFp(configuration) - return { - /** - * - * @param {AuthenticationApiChangePasswordRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - changePassword(requestParameters: AuthenticationApiChangePasswordRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.changePassword(requestParameters.changePasswordDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAuthDevices(options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getAuthDevices(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AuthenticationApiLoginRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - login(requestParameters: AuthenticationApiLoginRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.login(requestParameters.loginCredentialDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - logout(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.logout(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AuthenticationApiLogoutAuthDeviceRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - logoutAuthDevice(requestParameters: AuthenticationApiLogoutAuthDeviceRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.logoutAuthDevice(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - logoutAuthDevices(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.logoutAuthDevices(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AuthenticationApiSignUpAdminRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - signUpAdmin(requestParameters: AuthenticationApiSignUpAdminRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.signUpAdmin(requestParameters.signUpDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - validateAccessToken(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.validateAccessToken(options).then((request) => request(axios, basePath)); - }, - }; -}; - -/** - * Request parameters for changePassword operation in AuthenticationApi. - * @export - * @interface AuthenticationApiChangePasswordRequest - */ -export interface AuthenticationApiChangePasswordRequest { - /** - * - * @type {ChangePasswordDto} - * @memberof AuthenticationApiChangePassword - */ - readonly changePasswordDto: ChangePasswordDto -} - -/** - * Request parameters for login operation in AuthenticationApi. - * @export - * @interface AuthenticationApiLoginRequest - */ -export interface AuthenticationApiLoginRequest { - /** - * - * @type {LoginCredentialDto} - * @memberof AuthenticationApiLogin - */ - readonly loginCredentialDto: LoginCredentialDto -} - -/** - * Request parameters for logoutAuthDevice operation in AuthenticationApi. - * @export - * @interface AuthenticationApiLogoutAuthDeviceRequest - */ -export interface AuthenticationApiLogoutAuthDeviceRequest { - /** - * - * @type {string} - * @memberof AuthenticationApiLogoutAuthDevice - */ - readonly id: string -} - -/** - * Request parameters for signUpAdmin operation in AuthenticationApi. - * @export - * @interface AuthenticationApiSignUpAdminRequest - */ -export interface AuthenticationApiSignUpAdminRequest { - /** - * - * @type {SignUpDto} - * @memberof AuthenticationApiSignUpAdmin - */ - readonly signUpDto: SignUpDto -} - -/** - * AuthenticationApi - object-oriented interface - * @export - * @class AuthenticationApi - * @extends {BaseAPI} - */ -export class AuthenticationApi extends BaseAPI { - /** - * - * @param {AuthenticationApiChangePasswordRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AuthenticationApi - */ - public changePassword(requestParameters: AuthenticationApiChangePasswordRequest, options?: RawAxiosRequestConfig) { - return AuthenticationApiFp(this.configuration).changePassword(requestParameters.changePasswordDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AuthenticationApi - */ - public getAuthDevices(options?: RawAxiosRequestConfig) { - return AuthenticationApiFp(this.configuration).getAuthDevices(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AuthenticationApiLoginRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AuthenticationApi - */ - public login(requestParameters: AuthenticationApiLoginRequest, options?: RawAxiosRequestConfig) { - return AuthenticationApiFp(this.configuration).login(requestParameters.loginCredentialDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AuthenticationApi - */ - public logout(options?: RawAxiosRequestConfig) { - return AuthenticationApiFp(this.configuration).logout(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AuthenticationApiLogoutAuthDeviceRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AuthenticationApi - */ - public logoutAuthDevice(requestParameters: AuthenticationApiLogoutAuthDeviceRequest, options?: RawAxiosRequestConfig) { - return AuthenticationApiFp(this.configuration).logoutAuthDevice(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AuthenticationApi - */ - public logoutAuthDevices(options?: RawAxiosRequestConfig) { - return AuthenticationApiFp(this.configuration).logoutAuthDevices(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AuthenticationApiSignUpAdminRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AuthenticationApi - */ - public signUpAdmin(requestParameters: AuthenticationApiSignUpAdminRequest, options?: RawAxiosRequestConfig) { - return AuthenticationApiFp(this.configuration).signUpAdmin(requestParameters.signUpDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AuthenticationApi - */ - public validateAccessToken(options?: RawAxiosRequestConfig) { - return AuthenticationApiFp(this.configuration).validateAccessToken(options).then((request) => request(this.axios, this.basePath)); - } -} - - - -/** - * DownloadApi - axios parameter creator - * @export - */ -export const DownloadApiAxiosParamCreator = function (configuration?: Configuration) { - return { - /** - * - * @param {AssetIdsDto} assetIdsDto - * @param {string} [key] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - downloadArchive: async (assetIdsDto: AssetIdsDto, key?: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'assetIdsDto' is not null or undefined - assertParamExists('downloadArchive', 'assetIdsDto', assetIdsDto) - const localVarPath = `/download/archive`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (key !== undefined) { - localVarQueryParameter['key'] = key; - } - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(assetIdsDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {string} [key] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - downloadFile: async (id: string, key?: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('downloadFile', 'id', id) - const localVarPath = `/download/asset/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (key !== undefined) { - localVarQueryParameter['key'] = key; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {DownloadInfoDto} downloadInfoDto - * @param {string} [key] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getDownloadInfo: async (downloadInfoDto: DownloadInfoDto, key?: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'downloadInfoDto' is not null or undefined - assertParamExists('getDownloadInfo', 'downloadInfoDto', downloadInfoDto) - const localVarPath = `/download/info`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (key !== undefined) { - localVarQueryParameter['key'] = key; - } - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(downloadInfoDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - } -}; - -/** - * DownloadApi - functional programming interface - * @export - */ -export const DownloadApiFp = function(configuration?: Configuration) { - const localVarAxiosParamCreator = DownloadApiAxiosParamCreator(configuration) - return { - /** - * - * @param {AssetIdsDto} assetIdsDto - * @param {string} [key] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async downloadArchive(assetIdsDto: AssetIdsDto, key?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.downloadArchive(assetIdsDto, key, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['DownloadApi.downloadArchive']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {string} [key] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async downloadFile(id: string, key?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.downloadFile(id, key, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['DownloadApi.downloadFile']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {DownloadInfoDto} downloadInfoDto - * @param {string} [key] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getDownloadInfo(downloadInfoDto: DownloadInfoDto, key?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getDownloadInfo(downloadInfoDto, key, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['DownloadApi.getDownloadInfo']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - } -}; - -/** - * DownloadApi - factory interface - * @export - */ -export const DownloadApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { - const localVarFp = DownloadApiFp(configuration) - return { - /** - * - * @param {DownloadApiDownloadArchiveRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - downloadArchive(requestParameters: DownloadApiDownloadArchiveRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.downloadArchive(requestParameters.assetIdsDto, requestParameters.key, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {DownloadApiDownloadFileRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - downloadFile(requestParameters: DownloadApiDownloadFileRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.downloadFile(requestParameters.id, requestParameters.key, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {DownloadApiGetDownloadInfoRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getDownloadInfo(requestParameters: DownloadApiGetDownloadInfoRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getDownloadInfo(requestParameters.downloadInfoDto, requestParameters.key, options).then((request) => request(axios, basePath)); - }, - }; -}; - -/** - * Request parameters for downloadArchive operation in DownloadApi. - * @export - * @interface DownloadApiDownloadArchiveRequest - */ -export interface DownloadApiDownloadArchiveRequest { - /** - * - * @type {AssetIdsDto} - * @memberof DownloadApiDownloadArchive - */ - readonly assetIdsDto: AssetIdsDto - - /** - * - * @type {string} - * @memberof DownloadApiDownloadArchive - */ - readonly key?: string -} - -/** - * Request parameters for downloadFile operation in DownloadApi. - * @export - * @interface DownloadApiDownloadFileRequest - */ -export interface DownloadApiDownloadFileRequest { - /** - * - * @type {string} - * @memberof DownloadApiDownloadFile - */ - readonly id: string - - /** - * - * @type {string} - * @memberof DownloadApiDownloadFile - */ - readonly key?: string -} - -/** - * Request parameters for getDownloadInfo operation in DownloadApi. - * @export - * @interface DownloadApiGetDownloadInfoRequest - */ -export interface DownloadApiGetDownloadInfoRequest { - /** - * - * @type {DownloadInfoDto} - * @memberof DownloadApiGetDownloadInfo - */ - readonly downloadInfoDto: DownloadInfoDto - - /** - * - * @type {string} - * @memberof DownloadApiGetDownloadInfo - */ - readonly key?: string -} - -/** - * DownloadApi - object-oriented interface - * @export - * @class DownloadApi - * @extends {BaseAPI} - */ -export class DownloadApi extends BaseAPI { - /** - * - * @param {DownloadApiDownloadArchiveRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof DownloadApi - */ - public downloadArchive(requestParameters: DownloadApiDownloadArchiveRequest, options?: RawAxiosRequestConfig) { - return DownloadApiFp(this.configuration).downloadArchive(requestParameters.assetIdsDto, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {DownloadApiDownloadFileRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof DownloadApi - */ - public downloadFile(requestParameters: DownloadApiDownloadFileRequest, options?: RawAxiosRequestConfig) { - return DownloadApiFp(this.configuration).downloadFile(requestParameters.id, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {DownloadApiGetDownloadInfoRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof DownloadApi - */ - public getDownloadInfo(requestParameters: DownloadApiGetDownloadInfoRequest, options?: RawAxiosRequestConfig) { - return DownloadApiFp(this.configuration).getDownloadInfo(requestParameters.downloadInfoDto, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); - } -} - - - -/** - * FaceApi - axios parameter creator - * @export - */ -export const FaceApiAxiosParamCreator = function (configuration?: Configuration) { - return { - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getFaces: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('getFaces', 'id', id) - const localVarPath = `/face`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (id !== undefined) { - localVarQueryParameter['id'] = id; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {FaceDto} faceDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - reassignFacesById: async (id: string, faceDto: FaceDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('reassignFacesById', 'id', id) - // verify required parameter 'faceDto' is not null or undefined - assertParamExists('reassignFacesById', 'faceDto', faceDto) - const localVarPath = `/face/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(faceDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - } -}; - -/** - * FaceApi - functional programming interface - * @export - */ -export const FaceApiFp = function(configuration?: Configuration) { - const localVarAxiosParamCreator = FaceApiAxiosParamCreator(configuration) - return { - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getFaces(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getFaces(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['FaceApi.getFaces']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {FaceDto} faceDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async reassignFacesById(id: string, faceDto: FaceDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.reassignFacesById(id, faceDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['FaceApi.reassignFacesById']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - } -}; - -/** - * FaceApi - factory interface - * @export - */ -export const FaceApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { - const localVarFp = FaceApiFp(configuration) - return { - /** - * - * @param {FaceApiGetFacesRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getFaces(requestParameters: FaceApiGetFacesRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getFaces(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {FaceApiReassignFacesByIdRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - reassignFacesById(requestParameters: FaceApiReassignFacesByIdRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.reassignFacesById(requestParameters.id, requestParameters.faceDto, options).then((request) => request(axios, basePath)); - }, - }; -}; - -/** - * Request parameters for getFaces operation in FaceApi. - * @export - * @interface FaceApiGetFacesRequest - */ -export interface FaceApiGetFacesRequest { - /** - * - * @type {string} - * @memberof FaceApiGetFaces - */ - readonly id: string -} - -/** - * Request parameters for reassignFacesById operation in FaceApi. - * @export - * @interface FaceApiReassignFacesByIdRequest - */ -export interface FaceApiReassignFacesByIdRequest { - /** - * - * @type {string} - * @memberof FaceApiReassignFacesById - */ - readonly id: string - - /** - * - * @type {FaceDto} - * @memberof FaceApiReassignFacesById - */ - readonly faceDto: FaceDto -} - -/** - * FaceApi - object-oriented interface - * @export - * @class FaceApi - * @extends {BaseAPI} - */ -export class FaceApi extends BaseAPI { - /** - * - * @param {FaceApiGetFacesRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof FaceApi - */ - public getFaces(requestParameters: FaceApiGetFacesRequest, options?: RawAxiosRequestConfig) { - return FaceApiFp(this.configuration).getFaces(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {FaceApiReassignFacesByIdRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof FaceApi - */ - public reassignFacesById(requestParameters: FaceApiReassignFacesByIdRequest, options?: RawAxiosRequestConfig) { - return FaceApiFp(this.configuration).reassignFacesById(requestParameters.id, requestParameters.faceDto, options).then((request) => request(this.axios, this.basePath)); - } -} - - - -/** - * JobApi - axios parameter creator - * @export - */ -export const JobApiAxiosParamCreator = function (configuration?: Configuration) { - return { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAllJobsStatus: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/jobs`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {JobName} id - * @param {JobCommandDto} jobCommandDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - sendJobCommand: async (id: JobName, jobCommandDto: JobCommandDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('sendJobCommand', 'id', id) - // verify required parameter 'jobCommandDto' is not null or undefined - assertParamExists('sendJobCommand', 'jobCommandDto', jobCommandDto) - const localVarPath = `/jobs/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(jobCommandDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - } -}; - -/** - * JobApi - functional programming interface - * @export - */ -export const JobApiFp = function(configuration?: Configuration) { - const localVarAxiosParamCreator = JobApiAxiosParamCreator(configuration) - return { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getAllJobsStatus(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAllJobsStatus(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['JobApi.getAllJobsStatus']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {JobName} id - * @param {JobCommandDto} jobCommandDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async sendJobCommand(id: JobName, jobCommandDto: JobCommandDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.sendJobCommand(id, jobCommandDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['JobApi.sendJobCommand']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - } -}; - -/** - * JobApi - factory interface - * @export - */ -export const JobApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { - const localVarFp = JobApiFp(configuration) - return { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAllJobsStatus(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getAllJobsStatus(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {JobApiSendJobCommandRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - sendJobCommand(requestParameters: JobApiSendJobCommandRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.sendJobCommand(requestParameters.id, requestParameters.jobCommandDto, options).then((request) => request(axios, basePath)); - }, - }; -}; - -/** - * Request parameters for sendJobCommand operation in JobApi. - * @export - * @interface JobApiSendJobCommandRequest - */ -export interface JobApiSendJobCommandRequest { - /** - * - * @type {JobName} - * @memberof JobApiSendJobCommand - */ - readonly id: JobName - - /** - * - * @type {JobCommandDto} - * @memberof JobApiSendJobCommand - */ - readonly jobCommandDto: JobCommandDto -} - -/** - * JobApi - object-oriented interface - * @export - * @class JobApi - * @extends {BaseAPI} - */ -export class JobApi extends BaseAPI { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof JobApi - */ - public getAllJobsStatus(options?: RawAxiosRequestConfig) { - return JobApiFp(this.configuration).getAllJobsStatus(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {JobApiSendJobCommandRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof JobApi - */ - public sendJobCommand(requestParameters: JobApiSendJobCommandRequest, options?: RawAxiosRequestConfig) { - return JobApiFp(this.configuration).sendJobCommand(requestParameters.id, requestParameters.jobCommandDto, options).then((request) => request(this.axios, this.basePath)); - } -} - - - -/** - * LibraryApi - axios parameter creator - * @export - */ -export const LibraryApiAxiosParamCreator = function (configuration?: Configuration) { - return { - /** - * - * @param {CreateLibraryDto} createLibraryDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - createLibrary: async (createLibraryDto: CreateLibraryDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'createLibraryDto' is not null or undefined - assertParamExists('createLibrary', 'createLibraryDto', createLibraryDto) - const localVarPath = `/library`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(createLibraryDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - deleteLibrary: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('deleteLibrary', 'id', id) - const localVarPath = `/library/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getLibraries: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/library`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getLibraryInfo: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('getLibraryInfo', 'id', id) - const localVarPath = `/library/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getLibraryStatistics: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('getLibraryStatistics', 'id', id) - const localVarPath = `/library/{id}/statistics` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - removeOfflineFiles: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('removeOfflineFiles', 'id', id) - const localVarPath = `/library/{id}/removeOffline` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {ScanLibraryDto} scanLibraryDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - scanLibrary: async (id: string, scanLibraryDto: ScanLibraryDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('scanLibrary', 'id', id) - // verify required parameter 'scanLibraryDto' is not null or undefined - assertParamExists('scanLibrary', 'scanLibraryDto', scanLibraryDto) - const localVarPath = `/library/{id}/scan` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(scanLibraryDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {UpdateLibraryDto} updateLibraryDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updateLibrary: async (id: string, updateLibraryDto: UpdateLibraryDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('updateLibrary', 'id', id) - // verify required parameter 'updateLibraryDto' is not null or undefined - assertParamExists('updateLibrary', 'updateLibraryDto', updateLibraryDto) - const localVarPath = `/library/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(updateLibraryDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - } -}; - -/** - * LibraryApi - functional programming interface - * @export - */ -export const LibraryApiFp = function(configuration?: Configuration) { - const localVarAxiosParamCreator = LibraryApiAxiosParamCreator(configuration) - return { - /** - * - * @param {CreateLibraryDto} createLibraryDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async createLibrary(createLibraryDto: CreateLibraryDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.createLibrary(createLibraryDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['LibraryApi.createLibrary']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async deleteLibrary(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.deleteLibrary(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['LibraryApi.deleteLibrary']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getLibraries(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getLibraries(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['LibraryApi.getLibraries']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getLibraryInfo(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getLibraryInfo(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['LibraryApi.getLibraryInfo']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getLibraryStatistics(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getLibraryStatistics(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['LibraryApi.getLibraryStatistics']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async removeOfflineFiles(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.removeOfflineFiles(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['LibraryApi.removeOfflineFiles']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {ScanLibraryDto} scanLibraryDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async scanLibrary(id: string, scanLibraryDto: ScanLibraryDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.scanLibrary(id, scanLibraryDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['LibraryApi.scanLibrary']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {UpdateLibraryDto} updateLibraryDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async updateLibrary(id: string, updateLibraryDto: UpdateLibraryDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.updateLibrary(id, updateLibraryDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['LibraryApi.updateLibrary']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - } -}; - -/** - * LibraryApi - factory interface - * @export - */ -export const LibraryApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { - const localVarFp = LibraryApiFp(configuration) - return { - /** - * - * @param {LibraryApiCreateLibraryRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - createLibrary(requestParameters: LibraryApiCreateLibraryRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.createLibrary(requestParameters.createLibraryDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {LibraryApiDeleteLibraryRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - deleteLibrary(requestParameters: LibraryApiDeleteLibraryRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.deleteLibrary(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getLibraries(options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getLibraries(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {LibraryApiGetLibraryInfoRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getLibraryInfo(requestParameters: LibraryApiGetLibraryInfoRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getLibraryInfo(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {LibraryApiGetLibraryStatisticsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getLibraryStatistics(requestParameters: LibraryApiGetLibraryStatisticsRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getLibraryStatistics(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {LibraryApiRemoveOfflineFilesRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - removeOfflineFiles(requestParameters: LibraryApiRemoveOfflineFilesRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.removeOfflineFiles(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {LibraryApiScanLibraryRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - scanLibrary(requestParameters: LibraryApiScanLibraryRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.scanLibrary(requestParameters.id, requestParameters.scanLibraryDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {LibraryApiUpdateLibraryRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updateLibrary(requestParameters: LibraryApiUpdateLibraryRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.updateLibrary(requestParameters.id, requestParameters.updateLibraryDto, options).then((request) => request(axios, basePath)); - }, - }; -}; - -/** - * Request parameters for createLibrary operation in LibraryApi. - * @export - * @interface LibraryApiCreateLibraryRequest - */ -export interface LibraryApiCreateLibraryRequest { - /** - * - * @type {CreateLibraryDto} - * @memberof LibraryApiCreateLibrary - */ - readonly createLibraryDto: CreateLibraryDto -} - -/** - * Request parameters for deleteLibrary operation in LibraryApi. - * @export - * @interface LibraryApiDeleteLibraryRequest - */ -export interface LibraryApiDeleteLibraryRequest { - /** - * - * @type {string} - * @memberof LibraryApiDeleteLibrary - */ - readonly id: string -} - -/** - * Request parameters for getLibraryInfo operation in LibraryApi. - * @export - * @interface LibraryApiGetLibraryInfoRequest - */ -export interface LibraryApiGetLibraryInfoRequest { - /** - * - * @type {string} - * @memberof LibraryApiGetLibraryInfo - */ - readonly id: string -} - -/** - * Request parameters for getLibraryStatistics operation in LibraryApi. - * @export - * @interface LibraryApiGetLibraryStatisticsRequest - */ -export interface LibraryApiGetLibraryStatisticsRequest { - /** - * - * @type {string} - * @memberof LibraryApiGetLibraryStatistics - */ - readonly id: string -} - -/** - * Request parameters for removeOfflineFiles operation in LibraryApi. - * @export - * @interface LibraryApiRemoveOfflineFilesRequest - */ -export interface LibraryApiRemoveOfflineFilesRequest { - /** - * - * @type {string} - * @memberof LibraryApiRemoveOfflineFiles - */ - readonly id: string -} - -/** - * Request parameters for scanLibrary operation in LibraryApi. - * @export - * @interface LibraryApiScanLibraryRequest - */ -export interface LibraryApiScanLibraryRequest { - /** - * - * @type {string} - * @memberof LibraryApiScanLibrary - */ - readonly id: string - - /** - * - * @type {ScanLibraryDto} - * @memberof LibraryApiScanLibrary - */ - readonly scanLibraryDto: ScanLibraryDto -} - -/** - * Request parameters for updateLibrary operation in LibraryApi. - * @export - * @interface LibraryApiUpdateLibraryRequest - */ -export interface LibraryApiUpdateLibraryRequest { - /** - * - * @type {string} - * @memberof LibraryApiUpdateLibrary - */ - readonly id: string - - /** - * - * @type {UpdateLibraryDto} - * @memberof LibraryApiUpdateLibrary - */ - readonly updateLibraryDto: UpdateLibraryDto -} - -/** - * LibraryApi - object-oriented interface - * @export - * @class LibraryApi - * @extends {BaseAPI} - */ -export class LibraryApi extends BaseAPI { - /** - * - * @param {LibraryApiCreateLibraryRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof LibraryApi - */ - public createLibrary(requestParameters: LibraryApiCreateLibraryRequest, options?: RawAxiosRequestConfig) { - return LibraryApiFp(this.configuration).createLibrary(requestParameters.createLibraryDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {LibraryApiDeleteLibraryRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof LibraryApi - */ - public deleteLibrary(requestParameters: LibraryApiDeleteLibraryRequest, options?: RawAxiosRequestConfig) { - return LibraryApiFp(this.configuration).deleteLibrary(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof LibraryApi - */ - public getLibraries(options?: RawAxiosRequestConfig) { - return LibraryApiFp(this.configuration).getLibraries(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {LibraryApiGetLibraryInfoRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof LibraryApi - */ - public getLibraryInfo(requestParameters: LibraryApiGetLibraryInfoRequest, options?: RawAxiosRequestConfig) { - return LibraryApiFp(this.configuration).getLibraryInfo(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {LibraryApiGetLibraryStatisticsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof LibraryApi - */ - public getLibraryStatistics(requestParameters: LibraryApiGetLibraryStatisticsRequest, options?: RawAxiosRequestConfig) { - return LibraryApiFp(this.configuration).getLibraryStatistics(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {LibraryApiRemoveOfflineFilesRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof LibraryApi - */ - public removeOfflineFiles(requestParameters: LibraryApiRemoveOfflineFilesRequest, options?: RawAxiosRequestConfig) { - return LibraryApiFp(this.configuration).removeOfflineFiles(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {LibraryApiScanLibraryRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof LibraryApi - */ - public scanLibrary(requestParameters: LibraryApiScanLibraryRequest, options?: RawAxiosRequestConfig) { - return LibraryApiFp(this.configuration).scanLibrary(requestParameters.id, requestParameters.scanLibraryDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {LibraryApiUpdateLibraryRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof LibraryApi - */ - public updateLibrary(requestParameters: LibraryApiUpdateLibraryRequest, options?: RawAxiosRequestConfig) { - return LibraryApiFp(this.configuration).updateLibrary(requestParameters.id, requestParameters.updateLibraryDto, options).then((request) => request(this.axios, this.basePath)); - } -} - - - -/** - * OAuthApi - axios parameter creator - * @export - */ -export const OAuthApiAxiosParamCreator = function (configuration?: Configuration) { - return { - /** - * - * @param {OAuthCallbackDto} oAuthCallbackDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - finishOAuth: async (oAuthCallbackDto: OAuthCallbackDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'oAuthCallbackDto' is not null or undefined - assertParamExists('finishOAuth', 'oAuthCallbackDto', oAuthCallbackDto) - const localVarPath = `/oauth/callback`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(oAuthCallbackDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {OAuthCallbackDto} oAuthCallbackDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - linkOAuthAccount: async (oAuthCallbackDto: OAuthCallbackDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'oAuthCallbackDto' is not null or undefined - assertParamExists('linkOAuthAccount', 'oAuthCallbackDto', oAuthCallbackDto) - const localVarPath = `/oauth/link`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(oAuthCallbackDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - redirectOAuthToMobile: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/oauth/mobile-redirect`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {OAuthConfigDto} oAuthConfigDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - startOAuth: async (oAuthConfigDto: OAuthConfigDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'oAuthConfigDto' is not null or undefined - assertParamExists('startOAuth', 'oAuthConfigDto', oAuthConfigDto) - const localVarPath = `/oauth/authorize`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(oAuthConfigDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - unlinkOAuthAccount: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/oauth/unlink`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - } -}; - -/** - * OAuthApi - functional programming interface - * @export - */ -export const OAuthApiFp = function(configuration?: Configuration) { - const localVarAxiosParamCreator = OAuthApiAxiosParamCreator(configuration) - return { - /** - * - * @param {OAuthCallbackDto} oAuthCallbackDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async finishOAuth(oAuthCallbackDto: OAuthCallbackDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.finishOAuth(oAuthCallbackDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['OAuthApi.finishOAuth']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {OAuthCallbackDto} oAuthCallbackDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async linkOAuthAccount(oAuthCallbackDto: OAuthCallbackDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.linkOAuthAccount(oAuthCallbackDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['OAuthApi.linkOAuthAccount']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async redirectOAuthToMobile(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.redirectOAuthToMobile(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['OAuthApi.redirectOAuthToMobile']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {OAuthConfigDto} oAuthConfigDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async startOAuth(oAuthConfigDto: OAuthConfigDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.startOAuth(oAuthConfigDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['OAuthApi.startOAuth']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async unlinkOAuthAccount(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.unlinkOAuthAccount(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['OAuthApi.unlinkOAuthAccount']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - } -}; - -/** - * OAuthApi - factory interface - * @export - */ -export const OAuthApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { - const localVarFp = OAuthApiFp(configuration) - return { - /** - * - * @param {OAuthApiFinishOAuthRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - finishOAuth(requestParameters: OAuthApiFinishOAuthRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.finishOAuth(requestParameters.oAuthCallbackDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {OAuthApiLinkOAuthAccountRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - linkOAuthAccount(requestParameters: OAuthApiLinkOAuthAccountRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.linkOAuthAccount(requestParameters.oAuthCallbackDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - redirectOAuthToMobile(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.redirectOAuthToMobile(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {OAuthApiStartOAuthRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - startOAuth(requestParameters: OAuthApiStartOAuthRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.startOAuth(requestParameters.oAuthConfigDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - unlinkOAuthAccount(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.unlinkOAuthAccount(options).then((request) => request(axios, basePath)); - }, - }; -}; - -/** - * Request parameters for finishOAuth operation in OAuthApi. - * @export - * @interface OAuthApiFinishOAuthRequest - */ -export interface OAuthApiFinishOAuthRequest { - /** - * - * @type {OAuthCallbackDto} - * @memberof OAuthApiFinishOAuth - */ - readonly oAuthCallbackDto: OAuthCallbackDto -} - -/** - * Request parameters for linkOAuthAccount operation in OAuthApi. - * @export - * @interface OAuthApiLinkOAuthAccountRequest - */ -export interface OAuthApiLinkOAuthAccountRequest { - /** - * - * @type {OAuthCallbackDto} - * @memberof OAuthApiLinkOAuthAccount - */ - readonly oAuthCallbackDto: OAuthCallbackDto -} - -/** - * Request parameters for startOAuth operation in OAuthApi. - * @export - * @interface OAuthApiStartOAuthRequest - */ -export interface OAuthApiStartOAuthRequest { - /** - * - * @type {OAuthConfigDto} - * @memberof OAuthApiStartOAuth - */ - readonly oAuthConfigDto: OAuthConfigDto -} - -/** - * OAuthApi - object-oriented interface - * @export - * @class OAuthApi - * @extends {BaseAPI} - */ -export class OAuthApi extends BaseAPI { - /** - * - * @param {OAuthApiFinishOAuthRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof OAuthApi - */ - public finishOAuth(requestParameters: OAuthApiFinishOAuthRequest, options?: RawAxiosRequestConfig) { - return OAuthApiFp(this.configuration).finishOAuth(requestParameters.oAuthCallbackDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {OAuthApiLinkOAuthAccountRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof OAuthApi - */ - public linkOAuthAccount(requestParameters: OAuthApiLinkOAuthAccountRequest, options?: RawAxiosRequestConfig) { - return OAuthApiFp(this.configuration).linkOAuthAccount(requestParameters.oAuthCallbackDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof OAuthApi - */ - public redirectOAuthToMobile(options?: RawAxiosRequestConfig) { - return OAuthApiFp(this.configuration).redirectOAuthToMobile(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {OAuthApiStartOAuthRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof OAuthApi - */ - public startOAuth(requestParameters: OAuthApiStartOAuthRequest, options?: RawAxiosRequestConfig) { - return OAuthApiFp(this.configuration).startOAuth(requestParameters.oAuthConfigDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof OAuthApi - */ - public unlinkOAuthAccount(options?: RawAxiosRequestConfig) { - return OAuthApiFp(this.configuration).unlinkOAuthAccount(options).then((request) => request(this.axios, this.basePath)); - } -} - - - -/** - * PartnerApi - axios parameter creator - * @export - */ -export const PartnerApiAxiosParamCreator = function (configuration?: Configuration) { - return { - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - createPartner: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('createPartner', 'id', id) - const localVarPath = `/partner/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {GetPartnersDirectionEnum} direction - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getPartners: async (direction: GetPartnersDirectionEnum, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'direction' is not null or undefined - assertParamExists('getPartners', 'direction', direction) - const localVarPath = `/partner`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (direction !== undefined) { - localVarQueryParameter['direction'] = direction; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - removePartner: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('removePartner', 'id', id) - const localVarPath = `/partner/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {UpdatePartnerDto} updatePartnerDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updatePartner: async (id: string, updatePartnerDto: UpdatePartnerDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('updatePartner', 'id', id) - // verify required parameter 'updatePartnerDto' is not null or undefined - assertParamExists('updatePartner', 'updatePartnerDto', updatePartnerDto) - const localVarPath = `/partner/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(updatePartnerDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - } -}; - -/** - * PartnerApi - functional programming interface - * @export - */ -export const PartnerApiFp = function(configuration?: Configuration) { - const localVarAxiosParamCreator = PartnerApiAxiosParamCreator(configuration) - return { - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async createPartner(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.createPartner(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['PartnerApi.createPartner']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {GetPartnersDirectionEnum} direction - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getPartners(direction: GetPartnersDirectionEnum, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getPartners(direction, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['PartnerApi.getPartners']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async removePartner(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.removePartner(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['PartnerApi.removePartner']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {UpdatePartnerDto} updatePartnerDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async updatePartner(id: string, updatePartnerDto: UpdatePartnerDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.updatePartner(id, updatePartnerDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['PartnerApi.updatePartner']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - } -}; - -/** - * PartnerApi - factory interface - * @export - */ -export const PartnerApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { - const localVarFp = PartnerApiFp(configuration) - return { - /** - * - * @param {PartnerApiCreatePartnerRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - createPartner(requestParameters: PartnerApiCreatePartnerRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.createPartner(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {PartnerApiGetPartnersRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getPartners(requestParameters: PartnerApiGetPartnersRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getPartners(requestParameters.direction, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {PartnerApiRemovePartnerRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - removePartner(requestParameters: PartnerApiRemovePartnerRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.removePartner(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {PartnerApiUpdatePartnerRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updatePartner(requestParameters: PartnerApiUpdatePartnerRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.updatePartner(requestParameters.id, requestParameters.updatePartnerDto, options).then((request) => request(axios, basePath)); - }, - }; -}; - -/** - * Request parameters for createPartner operation in PartnerApi. - * @export - * @interface PartnerApiCreatePartnerRequest - */ -export interface PartnerApiCreatePartnerRequest { - /** - * - * @type {string} - * @memberof PartnerApiCreatePartner - */ - readonly id: string -} - -/** - * Request parameters for getPartners operation in PartnerApi. - * @export - * @interface PartnerApiGetPartnersRequest - */ -export interface PartnerApiGetPartnersRequest { - /** - * - * @type {'shared-by' | 'shared-with'} - * @memberof PartnerApiGetPartners - */ - readonly direction: GetPartnersDirectionEnum -} - -/** - * Request parameters for removePartner operation in PartnerApi. - * @export - * @interface PartnerApiRemovePartnerRequest - */ -export interface PartnerApiRemovePartnerRequest { - /** - * - * @type {string} - * @memberof PartnerApiRemovePartner - */ - readonly id: string -} - -/** - * Request parameters for updatePartner operation in PartnerApi. - * @export - * @interface PartnerApiUpdatePartnerRequest - */ -export interface PartnerApiUpdatePartnerRequest { - /** - * - * @type {string} - * @memberof PartnerApiUpdatePartner - */ - readonly id: string - - /** - * - * @type {UpdatePartnerDto} - * @memberof PartnerApiUpdatePartner - */ - readonly updatePartnerDto: UpdatePartnerDto -} - -/** - * PartnerApi - object-oriented interface - * @export - * @class PartnerApi - * @extends {BaseAPI} - */ -export class PartnerApi extends BaseAPI { - /** - * - * @param {PartnerApiCreatePartnerRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof PartnerApi - */ - public createPartner(requestParameters: PartnerApiCreatePartnerRequest, options?: RawAxiosRequestConfig) { - return PartnerApiFp(this.configuration).createPartner(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {PartnerApiGetPartnersRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof PartnerApi - */ - public getPartners(requestParameters: PartnerApiGetPartnersRequest, options?: RawAxiosRequestConfig) { - return PartnerApiFp(this.configuration).getPartners(requestParameters.direction, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {PartnerApiRemovePartnerRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof PartnerApi - */ - public removePartner(requestParameters: PartnerApiRemovePartnerRequest, options?: RawAxiosRequestConfig) { - return PartnerApiFp(this.configuration).removePartner(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {PartnerApiUpdatePartnerRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof PartnerApi - */ - public updatePartner(requestParameters: PartnerApiUpdatePartnerRequest, options?: RawAxiosRequestConfig) { - return PartnerApiFp(this.configuration).updatePartner(requestParameters.id, requestParameters.updatePartnerDto, options).then((request) => request(this.axios, this.basePath)); - } -} - -/** - * @export - */ -export const GetPartnersDirectionEnum = { - By: 'shared-by', - With: 'shared-with' -} as const; -export type GetPartnersDirectionEnum = typeof GetPartnersDirectionEnum[keyof typeof GetPartnersDirectionEnum]; - - -/** - * PersonApi - axios parameter creator - * @export - */ -export const PersonApiAxiosParamCreator = function (configuration?: Configuration) { - return { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - createPerson: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/person`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {boolean} [withHidden] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAllPeople: async (withHidden?: boolean, options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/person`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (withHidden !== undefined) { - localVarQueryParameter['withHidden'] = withHidden; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getPerson: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('getPerson', 'id', id) - const localVarPath = `/person/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getPersonAssets: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('getPersonAssets', 'id', id) - const localVarPath = `/person/{id}/assets` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getPersonStatistics: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('getPersonStatistics', 'id', id) - const localVarPath = `/person/{id}/statistics` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getPersonThumbnail: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('getPersonThumbnail', 'id', id) - const localVarPath = `/person/{id}/thumbnail` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {MergePersonDto} mergePersonDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - mergePerson: async (id: string, mergePersonDto: MergePersonDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('mergePerson', 'id', id) - // verify required parameter 'mergePersonDto' is not null or undefined - assertParamExists('mergePerson', 'mergePersonDto', mergePersonDto) - const localVarPath = `/person/{id}/merge` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(mergePersonDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {AssetFaceUpdateDto} assetFaceUpdateDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - reassignFaces: async (id: string, assetFaceUpdateDto: AssetFaceUpdateDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('reassignFaces', 'id', id) - // verify required parameter 'assetFaceUpdateDto' is not null or undefined - assertParamExists('reassignFaces', 'assetFaceUpdateDto', assetFaceUpdateDto) - const localVarPath = `/person/{id}/reassign` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(assetFaceUpdateDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {PeopleUpdateDto} peopleUpdateDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updatePeople: async (peopleUpdateDto: PeopleUpdateDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'peopleUpdateDto' is not null or undefined - assertParamExists('updatePeople', 'peopleUpdateDto', peopleUpdateDto) - const localVarPath = `/person`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(peopleUpdateDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {PersonUpdateDto} personUpdateDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updatePerson: async (id: string, personUpdateDto: PersonUpdateDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('updatePerson', 'id', id) - // verify required parameter 'personUpdateDto' is not null or undefined - assertParamExists('updatePerson', 'personUpdateDto', personUpdateDto) - const localVarPath = `/person/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(personUpdateDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - } -}; - -/** - * PersonApi - functional programming interface - * @export - */ -export const PersonApiFp = function(configuration?: Configuration) { - const localVarAxiosParamCreator = PersonApiAxiosParamCreator(configuration) - return { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async createPerson(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.createPerson(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['PersonApi.createPerson']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {boolean} [withHidden] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getAllPeople(withHidden?: boolean, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAllPeople(withHidden, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['PersonApi.getAllPeople']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getPerson(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getPerson(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['PersonApi.getPerson']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getPersonAssets(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getPersonAssets(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['PersonApi.getPersonAssets']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getPersonStatistics(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getPersonStatistics(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['PersonApi.getPersonStatistics']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getPersonThumbnail(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getPersonThumbnail(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['PersonApi.getPersonThumbnail']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {MergePersonDto} mergePersonDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async mergePerson(id: string, mergePersonDto: MergePersonDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.mergePerson(id, mergePersonDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['PersonApi.mergePerson']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {AssetFaceUpdateDto} assetFaceUpdateDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async reassignFaces(id: string, assetFaceUpdateDto: AssetFaceUpdateDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.reassignFaces(id, assetFaceUpdateDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['PersonApi.reassignFaces']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {PeopleUpdateDto} peopleUpdateDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async updatePeople(peopleUpdateDto: PeopleUpdateDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.updatePeople(peopleUpdateDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['PersonApi.updatePeople']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {PersonUpdateDto} personUpdateDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async updatePerson(id: string, personUpdateDto: PersonUpdateDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.updatePerson(id, personUpdateDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['PersonApi.updatePerson']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - } -}; - -/** - * PersonApi - factory interface - * @export - */ -export const PersonApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { - const localVarFp = PersonApiFp(configuration) - return { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - createPerson(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.createPerson(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {PersonApiGetAllPeopleRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAllPeople(requestParameters: PersonApiGetAllPeopleRequest = {}, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getAllPeople(requestParameters.withHidden, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {PersonApiGetPersonRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getPerson(requestParameters: PersonApiGetPersonRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getPerson(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {PersonApiGetPersonAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getPersonAssets(requestParameters: PersonApiGetPersonAssetsRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getPersonAssets(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {PersonApiGetPersonStatisticsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getPersonStatistics(requestParameters: PersonApiGetPersonStatisticsRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getPersonStatistics(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {PersonApiGetPersonThumbnailRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getPersonThumbnail(requestParameters: PersonApiGetPersonThumbnailRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getPersonThumbnail(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {PersonApiMergePersonRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - mergePerson(requestParameters: PersonApiMergePersonRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.mergePerson(requestParameters.id, requestParameters.mergePersonDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {PersonApiReassignFacesRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - reassignFaces(requestParameters: PersonApiReassignFacesRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.reassignFaces(requestParameters.id, requestParameters.assetFaceUpdateDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {PersonApiUpdatePeopleRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updatePeople(requestParameters: PersonApiUpdatePeopleRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.updatePeople(requestParameters.peopleUpdateDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {PersonApiUpdatePersonRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updatePerson(requestParameters: PersonApiUpdatePersonRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.updatePerson(requestParameters.id, requestParameters.personUpdateDto, options).then((request) => request(axios, basePath)); - }, - }; -}; - -/** - * Request parameters for getAllPeople operation in PersonApi. - * @export - * @interface PersonApiGetAllPeopleRequest - */ -export interface PersonApiGetAllPeopleRequest { - /** - * - * @type {boolean} - * @memberof PersonApiGetAllPeople - */ - readonly withHidden?: boolean -} - -/** - * Request parameters for getPerson operation in PersonApi. - * @export - * @interface PersonApiGetPersonRequest - */ -export interface PersonApiGetPersonRequest { - /** - * - * @type {string} - * @memberof PersonApiGetPerson - */ - readonly id: string -} - -/** - * Request parameters for getPersonAssets operation in PersonApi. - * @export - * @interface PersonApiGetPersonAssetsRequest - */ -export interface PersonApiGetPersonAssetsRequest { - /** - * - * @type {string} - * @memberof PersonApiGetPersonAssets - */ - readonly id: string -} - -/** - * Request parameters for getPersonStatistics operation in PersonApi. - * @export - * @interface PersonApiGetPersonStatisticsRequest - */ -export interface PersonApiGetPersonStatisticsRequest { - /** - * - * @type {string} - * @memberof PersonApiGetPersonStatistics - */ - readonly id: string -} - -/** - * Request parameters for getPersonThumbnail operation in PersonApi. - * @export - * @interface PersonApiGetPersonThumbnailRequest - */ -export interface PersonApiGetPersonThumbnailRequest { - /** - * - * @type {string} - * @memberof PersonApiGetPersonThumbnail - */ - readonly id: string -} - -/** - * Request parameters for mergePerson operation in PersonApi. - * @export - * @interface PersonApiMergePersonRequest - */ -export interface PersonApiMergePersonRequest { - /** - * - * @type {string} - * @memberof PersonApiMergePerson - */ - readonly id: string - - /** - * - * @type {MergePersonDto} - * @memberof PersonApiMergePerson - */ - readonly mergePersonDto: MergePersonDto -} - -/** - * Request parameters for reassignFaces operation in PersonApi. - * @export - * @interface PersonApiReassignFacesRequest - */ -export interface PersonApiReassignFacesRequest { - /** - * - * @type {string} - * @memberof PersonApiReassignFaces - */ - readonly id: string - - /** - * - * @type {AssetFaceUpdateDto} - * @memberof PersonApiReassignFaces - */ - readonly assetFaceUpdateDto: AssetFaceUpdateDto -} - -/** - * Request parameters for updatePeople operation in PersonApi. - * @export - * @interface PersonApiUpdatePeopleRequest - */ -export interface PersonApiUpdatePeopleRequest { - /** - * - * @type {PeopleUpdateDto} - * @memberof PersonApiUpdatePeople - */ - readonly peopleUpdateDto: PeopleUpdateDto -} - -/** - * Request parameters for updatePerson operation in PersonApi. - * @export - * @interface PersonApiUpdatePersonRequest - */ -export interface PersonApiUpdatePersonRequest { - /** - * - * @type {string} - * @memberof PersonApiUpdatePerson - */ - readonly id: string - - /** - * - * @type {PersonUpdateDto} - * @memberof PersonApiUpdatePerson - */ - readonly personUpdateDto: PersonUpdateDto -} - -/** - * PersonApi - object-oriented interface - * @export - * @class PersonApi - * @extends {BaseAPI} - */ -export class PersonApi extends BaseAPI { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof PersonApi - */ - public createPerson(options?: RawAxiosRequestConfig) { - return PersonApiFp(this.configuration).createPerson(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {PersonApiGetAllPeopleRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof PersonApi - */ - public getAllPeople(requestParameters: PersonApiGetAllPeopleRequest = {}, options?: RawAxiosRequestConfig) { - return PersonApiFp(this.configuration).getAllPeople(requestParameters.withHidden, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {PersonApiGetPersonRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof PersonApi - */ - public getPerson(requestParameters: PersonApiGetPersonRequest, options?: RawAxiosRequestConfig) { - return PersonApiFp(this.configuration).getPerson(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {PersonApiGetPersonAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof PersonApi - */ - public getPersonAssets(requestParameters: PersonApiGetPersonAssetsRequest, options?: RawAxiosRequestConfig) { - return PersonApiFp(this.configuration).getPersonAssets(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {PersonApiGetPersonStatisticsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof PersonApi - */ - public getPersonStatistics(requestParameters: PersonApiGetPersonStatisticsRequest, options?: RawAxiosRequestConfig) { - return PersonApiFp(this.configuration).getPersonStatistics(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {PersonApiGetPersonThumbnailRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof PersonApi - */ - public getPersonThumbnail(requestParameters: PersonApiGetPersonThumbnailRequest, options?: RawAxiosRequestConfig) { - return PersonApiFp(this.configuration).getPersonThumbnail(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {PersonApiMergePersonRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof PersonApi - */ - public mergePerson(requestParameters: PersonApiMergePersonRequest, options?: RawAxiosRequestConfig) { - return PersonApiFp(this.configuration).mergePerson(requestParameters.id, requestParameters.mergePersonDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {PersonApiReassignFacesRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof PersonApi - */ - public reassignFaces(requestParameters: PersonApiReassignFacesRequest, options?: RawAxiosRequestConfig) { - return PersonApiFp(this.configuration).reassignFaces(requestParameters.id, requestParameters.assetFaceUpdateDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {PersonApiUpdatePeopleRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof PersonApi - */ - public updatePeople(requestParameters: PersonApiUpdatePeopleRequest, options?: RawAxiosRequestConfig) { - return PersonApiFp(this.configuration).updatePeople(requestParameters.peopleUpdateDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {PersonApiUpdatePersonRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof PersonApi - */ - public updatePerson(requestParameters: PersonApiUpdatePersonRequest, options?: RawAxiosRequestConfig) { - return PersonApiFp(this.configuration).updatePerson(requestParameters.id, requestParameters.personUpdateDto, options).then((request) => request(this.axios, this.basePath)); - } -} - - - -/** - * SearchApi - axios parameter creator - * @export - */ -export const SearchApiAxiosParamCreator = function (configuration?: Configuration) { - return { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getExploreData: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/search/explore`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {SearchSuggestionType} type - * @param {string} [country] - * @param {string} [make] - * @param {string} [model] - * @param {string} [state] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getSearchSuggestions: async (type: SearchSuggestionType, country?: string, make?: string, model?: string, state?: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'type' is not null or undefined - assertParamExists('getSearchSuggestions', 'type', type) - const localVarPath = `/search/suggestions`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (country !== undefined) { - localVarQueryParameter['country'] = country; - } - - if (make !== undefined) { - localVarQueryParameter['make'] = make; - } - - if (model !== undefined) { - localVarQueryParameter['model'] = model; - } - - if (state !== undefined) { - localVarQueryParameter['state'] = state; - } - - if (type !== undefined) { - localVarQueryParameter['type'] = type; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {boolean} [clip] - * @param {boolean} [motion] - * @param {number} [page] - * @param {string} [q] - * @param {string} [query] - * @param {boolean} [recent] - * @param {number} [size] - * @param {boolean} [smart] - * @param {SearchTypeEnum} [type] - * @param {boolean} [withArchived] - * @param {*} [options] Override http request option. - * @deprecated - * @throws {RequiredError} - */ - search: async (clip?: boolean, motion?: boolean, page?: number, q?: string, query?: string, recent?: boolean, size?: number, smart?: boolean, type?: SearchTypeEnum, withArchived?: boolean, options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/search`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (clip !== undefined) { - localVarQueryParameter['clip'] = clip; - } - - if (motion !== undefined) { - localVarQueryParameter['motion'] = motion; - } - - if (page !== undefined) { - localVarQueryParameter['page'] = page; - } - - if (q !== undefined) { - localVarQueryParameter['q'] = q; - } - - if (query !== undefined) { - localVarQueryParameter['query'] = query; - } - - if (recent !== undefined) { - localVarQueryParameter['recent'] = recent; - } - - if (size !== undefined) { - localVarQueryParameter['size'] = size; - } - - if (smart !== undefined) { - localVarQueryParameter['smart'] = smart; - } - - if (type !== undefined) { - localVarQueryParameter['type'] = type; - } - - if (withArchived !== undefined) { - localVarQueryParameter['withArchived'] = withArchived; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} [checksum] - * @param {string} [city] - * @param {string} [country] - * @param {string} [createdAfter] - * @param {string} [createdBefore] - * @param {string} [deviceAssetId] - * @param {string} [deviceId] - * @param {string} [encodedVideoPath] - * @param {string} [id] - * @param {boolean} [isArchived] - * @param {boolean} [isEncoded] - * @param {boolean} [isExternal] - * @param {boolean} [isFavorite] - * @param {boolean} [isMotion] - * @param {boolean} [isOffline] - * @param {boolean} [isReadOnly] - * @param {boolean} [isVisible] - * @param {string} [lensModel] - * @param {string} [libraryId] - * @param {string} [make] - * @param {string} [model] - * @param {AssetOrder} [order] - * @param {string} [originalFileName] - * @param {string} [originalPath] - * @param {number} [page] - * @param {string} [resizePath] - * @param {number} [size] - * @param {string} [state] - * @param {string} [takenAfter] - * @param {string} [takenBefore] - * @param {string} [trashedAfter] - * @param {string} [trashedBefore] - * @param {AssetTypeEnum} [type] - * @param {string} [updatedAfter] - * @param {string} [updatedBefore] - * @param {string} [webpPath] - * @param {boolean} [withArchived] - * @param {boolean} [withDeleted] - * @param {boolean} [withExif] - * @param {boolean} [withPeople] - * @param {boolean} [withStacked] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - searchMetadata: async (checksum?: string, city?: string, country?: string, createdAfter?: string, createdBefore?: string, deviceAssetId?: string, deviceId?: string, encodedVideoPath?: string, id?: string, isArchived?: boolean, isEncoded?: boolean, isExternal?: boolean, isFavorite?: boolean, isMotion?: boolean, isOffline?: boolean, isReadOnly?: boolean, isVisible?: boolean, lensModel?: string, libraryId?: string, make?: string, model?: string, order?: AssetOrder, originalFileName?: string, originalPath?: string, page?: number, resizePath?: string, size?: number, state?: string, takenAfter?: string, takenBefore?: string, trashedAfter?: string, trashedBefore?: string, type?: AssetTypeEnum, updatedAfter?: string, updatedBefore?: string, webpPath?: string, withArchived?: boolean, withDeleted?: boolean, withExif?: boolean, withPeople?: boolean, withStacked?: boolean, options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/search/metadata`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (checksum !== undefined) { - localVarQueryParameter['checksum'] = checksum; - } - - if (city !== undefined) { - localVarQueryParameter['city'] = city; - } - - if (country !== undefined) { - localVarQueryParameter['country'] = country; - } - - if (createdAfter !== undefined) { - localVarQueryParameter['createdAfter'] = (createdAfter as any instanceof Date) ? - (createdAfter as any).toISOString() : - createdAfter; - } - - if (createdBefore !== undefined) { - localVarQueryParameter['createdBefore'] = (createdBefore as any instanceof Date) ? - (createdBefore as any).toISOString() : - createdBefore; - } - - if (deviceAssetId !== undefined) { - localVarQueryParameter['deviceAssetId'] = deviceAssetId; - } - - if (deviceId !== undefined) { - localVarQueryParameter['deviceId'] = deviceId; - } - - if (encodedVideoPath !== undefined) { - localVarQueryParameter['encodedVideoPath'] = encodedVideoPath; - } - - if (id !== undefined) { - localVarQueryParameter['id'] = id; - } - - if (isArchived !== undefined) { - localVarQueryParameter['isArchived'] = isArchived; - } - - if (isEncoded !== undefined) { - localVarQueryParameter['isEncoded'] = isEncoded; - } - - if (isExternal !== undefined) { - localVarQueryParameter['isExternal'] = isExternal; - } - - if (isFavorite !== undefined) { - localVarQueryParameter['isFavorite'] = isFavorite; - } - - if (isMotion !== undefined) { - localVarQueryParameter['isMotion'] = isMotion; - } - - if (isOffline !== undefined) { - localVarQueryParameter['isOffline'] = isOffline; - } - - if (isReadOnly !== undefined) { - localVarQueryParameter['isReadOnly'] = isReadOnly; - } - - if (isVisible !== undefined) { - localVarQueryParameter['isVisible'] = isVisible; - } - - if (lensModel !== undefined) { - localVarQueryParameter['lensModel'] = lensModel; - } - - if (libraryId !== undefined) { - localVarQueryParameter['libraryId'] = libraryId; - } - - if (make !== undefined) { - localVarQueryParameter['make'] = make; - } - - if (model !== undefined) { - localVarQueryParameter['model'] = model; - } - - if (order !== undefined) { - localVarQueryParameter['order'] = order; - } - - if (originalFileName !== undefined) { - localVarQueryParameter['originalFileName'] = originalFileName; - } - - if (originalPath !== undefined) { - localVarQueryParameter['originalPath'] = originalPath; - } - - if (page !== undefined) { - localVarQueryParameter['page'] = page; - } - - if (resizePath !== undefined) { - localVarQueryParameter['resizePath'] = resizePath; - } - - if (size !== undefined) { - localVarQueryParameter['size'] = size; - } - - if (state !== undefined) { - localVarQueryParameter['state'] = state; - } - - if (takenAfter !== undefined) { - localVarQueryParameter['takenAfter'] = (takenAfter as any instanceof Date) ? - (takenAfter as any).toISOString() : - takenAfter; - } - - if (takenBefore !== undefined) { - localVarQueryParameter['takenBefore'] = (takenBefore as any instanceof Date) ? - (takenBefore as any).toISOString() : - takenBefore; - } - - if (trashedAfter !== undefined) { - localVarQueryParameter['trashedAfter'] = (trashedAfter as any instanceof Date) ? - (trashedAfter as any).toISOString() : - trashedAfter; - } - - if (trashedBefore !== undefined) { - localVarQueryParameter['trashedBefore'] = (trashedBefore as any instanceof Date) ? - (trashedBefore as any).toISOString() : - trashedBefore; - } - - if (type !== undefined) { - localVarQueryParameter['type'] = type; - } - - if (updatedAfter !== undefined) { - localVarQueryParameter['updatedAfter'] = (updatedAfter as any instanceof Date) ? - (updatedAfter as any).toISOString() : - updatedAfter; - } - - if (updatedBefore !== undefined) { - localVarQueryParameter['updatedBefore'] = (updatedBefore as any instanceof Date) ? - (updatedBefore as any).toISOString() : - updatedBefore; - } - - if (webpPath !== undefined) { - localVarQueryParameter['webpPath'] = webpPath; - } - - if (withArchived !== undefined) { - localVarQueryParameter['withArchived'] = withArchived; - } - - if (withDeleted !== undefined) { - localVarQueryParameter['withDeleted'] = withDeleted; - } - - if (withExif !== undefined) { - localVarQueryParameter['withExif'] = withExif; - } - - if (withPeople !== undefined) { - localVarQueryParameter['withPeople'] = withPeople; - } - - if (withStacked !== undefined) { - localVarQueryParameter['withStacked'] = withStacked; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} name - * @param {boolean} [withHidden] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - searchPerson: async (name: string, withHidden?: boolean, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'name' is not null or undefined - assertParamExists('searchPerson', 'name', name) - const localVarPath = `/search/person`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (name !== undefined) { - localVarQueryParameter['name'] = name; - } - - if (withHidden !== undefined) { - localVarQueryParameter['withHidden'] = withHidden; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} query - * @param {string} [city] - * @param {string} [country] - * @param {string} [createdAfter] - * @param {string} [createdBefore] - * @param {string} [deviceId] - * @param {boolean} [isArchived] - * @param {boolean} [isEncoded] - * @param {boolean} [isExternal] - * @param {boolean} [isFavorite] - * @param {boolean} [isMotion] - * @param {boolean} [isOffline] - * @param {boolean} [isReadOnly] - * @param {boolean} [isVisible] - * @param {string} [lensModel] - * @param {string} [libraryId] - * @param {string} [make] - * @param {string} [model] - * @param {number} [page] - * @param {number} [size] - * @param {string} [state] - * @param {string} [takenAfter] - * @param {string} [takenBefore] - * @param {string} [trashedAfter] - * @param {string} [trashedBefore] - * @param {AssetTypeEnum} [type] - * @param {string} [updatedAfter] - * @param {string} [updatedBefore] - * @param {boolean} [withArchived] - * @param {boolean} [withDeleted] - * @param {boolean} [withExif] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - searchSmart: async (query: string, city?: string, country?: string, createdAfter?: string, createdBefore?: string, deviceId?: string, isArchived?: boolean, isEncoded?: boolean, isExternal?: boolean, isFavorite?: boolean, isMotion?: boolean, isOffline?: boolean, isReadOnly?: boolean, isVisible?: boolean, lensModel?: string, libraryId?: string, make?: string, model?: string, page?: number, size?: number, state?: string, takenAfter?: string, takenBefore?: string, trashedAfter?: string, trashedBefore?: string, type?: AssetTypeEnum, updatedAfter?: string, updatedBefore?: string, withArchived?: boolean, withDeleted?: boolean, withExif?: boolean, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'query' is not null or undefined - assertParamExists('searchSmart', 'query', query) - const localVarPath = `/search/smart`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (city !== undefined) { - localVarQueryParameter['city'] = city; - } - - if (country !== undefined) { - localVarQueryParameter['country'] = country; - } - - if (createdAfter !== undefined) { - localVarQueryParameter['createdAfter'] = (createdAfter as any instanceof Date) ? - (createdAfter as any).toISOString() : - createdAfter; - } - - if (createdBefore !== undefined) { - localVarQueryParameter['createdBefore'] = (createdBefore as any instanceof Date) ? - (createdBefore as any).toISOString() : - createdBefore; - } - - if (deviceId !== undefined) { - localVarQueryParameter['deviceId'] = deviceId; - } - - if (isArchived !== undefined) { - localVarQueryParameter['isArchived'] = isArchived; - } - - if (isEncoded !== undefined) { - localVarQueryParameter['isEncoded'] = isEncoded; - } - - if (isExternal !== undefined) { - localVarQueryParameter['isExternal'] = isExternal; - } - - if (isFavorite !== undefined) { - localVarQueryParameter['isFavorite'] = isFavorite; - } - - if (isMotion !== undefined) { - localVarQueryParameter['isMotion'] = isMotion; - } - - if (isOffline !== undefined) { - localVarQueryParameter['isOffline'] = isOffline; - } - - if (isReadOnly !== undefined) { - localVarQueryParameter['isReadOnly'] = isReadOnly; - } - - if (isVisible !== undefined) { - localVarQueryParameter['isVisible'] = isVisible; - } - - if (lensModel !== undefined) { - localVarQueryParameter['lensModel'] = lensModel; - } - - if (libraryId !== undefined) { - localVarQueryParameter['libraryId'] = libraryId; - } - - if (make !== undefined) { - localVarQueryParameter['make'] = make; - } - - if (model !== undefined) { - localVarQueryParameter['model'] = model; - } - - if (page !== undefined) { - localVarQueryParameter['page'] = page; - } - - if (query !== undefined) { - localVarQueryParameter['query'] = query; - } - - if (size !== undefined) { - localVarQueryParameter['size'] = size; - } - - if (state !== undefined) { - localVarQueryParameter['state'] = state; - } - - if (takenAfter !== undefined) { - localVarQueryParameter['takenAfter'] = (takenAfter as any instanceof Date) ? - (takenAfter as any).toISOString() : - takenAfter; - } - - if (takenBefore !== undefined) { - localVarQueryParameter['takenBefore'] = (takenBefore as any instanceof Date) ? - (takenBefore as any).toISOString() : - takenBefore; - } - - if (trashedAfter !== undefined) { - localVarQueryParameter['trashedAfter'] = (trashedAfter as any instanceof Date) ? - (trashedAfter as any).toISOString() : - trashedAfter; - } - - if (trashedBefore !== undefined) { - localVarQueryParameter['trashedBefore'] = (trashedBefore as any instanceof Date) ? - (trashedBefore as any).toISOString() : - trashedBefore; - } - - if (type !== undefined) { - localVarQueryParameter['type'] = type; - } - - if (updatedAfter !== undefined) { - localVarQueryParameter['updatedAfter'] = (updatedAfter as any instanceof Date) ? - (updatedAfter as any).toISOString() : - updatedAfter; - } - - if (updatedBefore !== undefined) { - localVarQueryParameter['updatedBefore'] = (updatedBefore as any instanceof Date) ? - (updatedBefore as any).toISOString() : - updatedBefore; - } - - if (withArchived !== undefined) { - localVarQueryParameter['withArchived'] = withArchived; - } - - if (withDeleted !== undefined) { - localVarQueryParameter['withDeleted'] = withDeleted; - } - - if (withExif !== undefined) { - localVarQueryParameter['withExif'] = withExif; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - } -}; - -/** - * SearchApi - functional programming interface - * @export - */ -export const SearchApiFp = function(configuration?: Configuration) { - const localVarAxiosParamCreator = SearchApiAxiosParamCreator(configuration) - return { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getExploreData(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getExploreData(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['SearchApi.getExploreData']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {SearchSuggestionType} type - * @param {string} [country] - * @param {string} [make] - * @param {string} [model] - * @param {string} [state] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getSearchSuggestions(type: SearchSuggestionType, country?: string, make?: string, model?: string, state?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getSearchSuggestions(type, country, make, model, state, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['SearchApi.getSearchSuggestions']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {boolean} [clip] - * @param {boolean} [motion] - * @param {number} [page] - * @param {string} [q] - * @param {string} [query] - * @param {boolean} [recent] - * @param {number} [size] - * @param {boolean} [smart] - * @param {SearchTypeEnum} [type] - * @param {boolean} [withArchived] - * @param {*} [options] Override http request option. - * @deprecated - * @throws {RequiredError} - */ - async search(clip?: boolean, motion?: boolean, page?: number, q?: string, query?: string, recent?: boolean, size?: number, smart?: boolean, type?: SearchTypeEnum, withArchived?: boolean, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.search(clip, motion, page, q, query, recent, size, smart, type, withArchived, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['SearchApi.search']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} [checksum] - * @param {string} [city] - * @param {string} [country] - * @param {string} [createdAfter] - * @param {string} [createdBefore] - * @param {string} [deviceAssetId] - * @param {string} [deviceId] - * @param {string} [encodedVideoPath] - * @param {string} [id] - * @param {boolean} [isArchived] - * @param {boolean} [isEncoded] - * @param {boolean} [isExternal] - * @param {boolean} [isFavorite] - * @param {boolean} [isMotion] - * @param {boolean} [isOffline] - * @param {boolean} [isReadOnly] - * @param {boolean} [isVisible] - * @param {string} [lensModel] - * @param {string} [libraryId] - * @param {string} [make] - * @param {string} [model] - * @param {AssetOrder} [order] - * @param {string} [originalFileName] - * @param {string} [originalPath] - * @param {number} [page] - * @param {string} [resizePath] - * @param {number} [size] - * @param {string} [state] - * @param {string} [takenAfter] - * @param {string} [takenBefore] - * @param {string} [trashedAfter] - * @param {string} [trashedBefore] - * @param {AssetTypeEnum} [type] - * @param {string} [updatedAfter] - * @param {string} [updatedBefore] - * @param {string} [webpPath] - * @param {boolean} [withArchived] - * @param {boolean} [withDeleted] - * @param {boolean} [withExif] - * @param {boolean} [withPeople] - * @param {boolean} [withStacked] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async searchMetadata(checksum?: string, city?: string, country?: string, createdAfter?: string, createdBefore?: string, deviceAssetId?: string, deviceId?: string, encodedVideoPath?: string, id?: string, isArchived?: boolean, isEncoded?: boolean, isExternal?: boolean, isFavorite?: boolean, isMotion?: boolean, isOffline?: boolean, isReadOnly?: boolean, isVisible?: boolean, lensModel?: string, libraryId?: string, make?: string, model?: string, order?: AssetOrder, originalFileName?: string, originalPath?: string, page?: number, resizePath?: string, size?: number, state?: string, takenAfter?: string, takenBefore?: string, trashedAfter?: string, trashedBefore?: string, type?: AssetTypeEnum, updatedAfter?: string, updatedBefore?: string, webpPath?: string, withArchived?: boolean, withDeleted?: boolean, withExif?: boolean, withPeople?: boolean, withStacked?: boolean, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.searchMetadata(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['SearchApi.searchMetadata']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} name - * @param {boolean} [withHidden] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async searchPerson(name: string, withHidden?: boolean, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.searchPerson(name, withHidden, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['SearchApi.searchPerson']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} query - * @param {string} [city] - * @param {string} [country] - * @param {string} [createdAfter] - * @param {string} [createdBefore] - * @param {string} [deviceId] - * @param {boolean} [isArchived] - * @param {boolean} [isEncoded] - * @param {boolean} [isExternal] - * @param {boolean} [isFavorite] - * @param {boolean} [isMotion] - * @param {boolean} [isOffline] - * @param {boolean} [isReadOnly] - * @param {boolean} [isVisible] - * @param {string} [lensModel] - * @param {string} [libraryId] - * @param {string} [make] - * @param {string} [model] - * @param {number} [page] - * @param {number} [size] - * @param {string} [state] - * @param {string} [takenAfter] - * @param {string} [takenBefore] - * @param {string} [trashedAfter] - * @param {string} [trashedBefore] - * @param {AssetTypeEnum} [type] - * @param {string} [updatedAfter] - * @param {string} [updatedBefore] - * @param {boolean} [withArchived] - * @param {boolean} [withDeleted] - * @param {boolean} [withExif] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async searchSmart(query: string, city?: string, country?: string, createdAfter?: string, createdBefore?: string, deviceId?: string, isArchived?: boolean, isEncoded?: boolean, isExternal?: boolean, isFavorite?: boolean, isMotion?: boolean, isOffline?: boolean, isReadOnly?: boolean, isVisible?: boolean, lensModel?: string, libraryId?: string, make?: string, model?: string, page?: number, size?: number, state?: string, takenAfter?: string, takenBefore?: string, trashedAfter?: string, trashedBefore?: string, type?: AssetTypeEnum, updatedAfter?: string, updatedBefore?: string, withArchived?: boolean, withDeleted?: boolean, withExif?: boolean, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.searchSmart(query, city, country, createdAfter, createdBefore, deviceId, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, page, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, withArchived, withDeleted, withExif, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['SearchApi.searchSmart']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - } -}; - -/** - * SearchApi - factory interface - * @export - */ -export const SearchApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { - const localVarFp = SearchApiFp(configuration) - return { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getExploreData(options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getExploreData(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {SearchApiGetSearchSuggestionsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getSearchSuggestions(requestParameters: SearchApiGetSearchSuggestionsRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getSearchSuggestions(requestParameters.type, requestParameters.country, requestParameters.make, requestParameters.model, requestParameters.state, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {SearchApiSearchRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @deprecated - * @throws {RequiredError} - */ - search(requestParameters: SearchApiSearchRequest = {}, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.search(requestParameters.clip, requestParameters.motion, requestParameters.page, requestParameters.q, requestParameters.query, requestParameters.recent, requestParameters.size, requestParameters.smart, requestParameters.type, requestParameters.withArchived, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {SearchApiSearchMetadataRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - searchMetadata(requestParameters: SearchApiSearchMetadataRequest = {}, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.searchMetadata(requestParameters.checksum, requestParameters.city, requestParameters.country, requestParameters.createdAfter, requestParameters.createdBefore, requestParameters.deviceAssetId, requestParameters.deviceId, requestParameters.encodedVideoPath, requestParameters.id, requestParameters.isArchived, requestParameters.isEncoded, requestParameters.isExternal, requestParameters.isFavorite, requestParameters.isMotion, requestParameters.isOffline, requestParameters.isReadOnly, requestParameters.isVisible, requestParameters.lensModel, requestParameters.libraryId, requestParameters.make, requestParameters.model, requestParameters.order, requestParameters.originalFileName, requestParameters.originalPath, requestParameters.page, requestParameters.resizePath, requestParameters.size, requestParameters.state, requestParameters.takenAfter, requestParameters.takenBefore, requestParameters.trashedAfter, requestParameters.trashedBefore, requestParameters.type, requestParameters.updatedAfter, requestParameters.updatedBefore, requestParameters.webpPath, requestParameters.withArchived, requestParameters.withDeleted, requestParameters.withExif, requestParameters.withPeople, requestParameters.withStacked, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {SearchApiSearchPersonRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - searchPerson(requestParameters: SearchApiSearchPersonRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.searchPerson(requestParameters.name, requestParameters.withHidden, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {SearchApiSearchSmartRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - searchSmart(requestParameters: SearchApiSearchSmartRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.searchSmart(requestParameters.query, requestParameters.city, requestParameters.country, requestParameters.createdAfter, requestParameters.createdBefore, requestParameters.deviceId, requestParameters.isArchived, requestParameters.isEncoded, requestParameters.isExternal, requestParameters.isFavorite, requestParameters.isMotion, requestParameters.isOffline, requestParameters.isReadOnly, requestParameters.isVisible, requestParameters.lensModel, requestParameters.libraryId, requestParameters.make, requestParameters.model, requestParameters.page, requestParameters.size, requestParameters.state, requestParameters.takenAfter, requestParameters.takenBefore, requestParameters.trashedAfter, requestParameters.trashedBefore, requestParameters.type, requestParameters.updatedAfter, requestParameters.updatedBefore, requestParameters.withArchived, requestParameters.withDeleted, requestParameters.withExif, options).then((request) => request(axios, basePath)); - }, - }; -}; - -/** - * Request parameters for getSearchSuggestions operation in SearchApi. - * @export - * @interface SearchApiGetSearchSuggestionsRequest - */ -export interface SearchApiGetSearchSuggestionsRequest { - /** - * - * @type {SearchSuggestionType} - * @memberof SearchApiGetSearchSuggestions - */ - readonly type: SearchSuggestionType - - /** - * - * @type {string} - * @memberof SearchApiGetSearchSuggestions - */ - readonly country?: string - - /** - * - * @type {string} - * @memberof SearchApiGetSearchSuggestions - */ - readonly make?: string - - /** - * - * @type {string} - * @memberof SearchApiGetSearchSuggestions - */ - readonly model?: string - - /** - * - * @type {string} - * @memberof SearchApiGetSearchSuggestions - */ - readonly state?: string -} - -/** - * Request parameters for search operation in SearchApi. - * @export - * @interface SearchApiSearchRequest - */ -export interface SearchApiSearchRequest { - /** - * - * @type {boolean} - * @memberof SearchApiSearch - */ - readonly clip?: boolean - - /** - * - * @type {boolean} - * @memberof SearchApiSearch - */ - readonly motion?: boolean - - /** - * - * @type {number} - * @memberof SearchApiSearch - */ - readonly page?: number - - /** - * - * @type {string} - * @memberof SearchApiSearch - */ - readonly q?: string - - /** - * - * @type {string} - * @memberof SearchApiSearch - */ - readonly query?: string - - /** - * - * @type {boolean} - * @memberof SearchApiSearch - */ - readonly recent?: boolean - - /** - * - * @type {number} - * @memberof SearchApiSearch - */ - readonly size?: number - - /** - * - * @type {boolean} - * @memberof SearchApiSearch - */ - readonly smart?: boolean - - /** - * - * @type {'IMAGE' | 'VIDEO' | 'AUDIO' | 'OTHER'} - * @memberof SearchApiSearch - */ - readonly type?: SearchTypeEnum - - /** - * - * @type {boolean} - * @memberof SearchApiSearch - */ - readonly withArchived?: boolean -} - -/** - * Request parameters for searchMetadata operation in SearchApi. - * @export - * @interface SearchApiSearchMetadataRequest - */ -export interface SearchApiSearchMetadataRequest { - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly checksum?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly city?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly country?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly createdAfter?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly createdBefore?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly deviceAssetId?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly deviceId?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly encodedVideoPath?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly id?: string - - /** - * - * @type {boolean} - * @memberof SearchApiSearchMetadata - */ - readonly isArchived?: boolean - - /** - * - * @type {boolean} - * @memberof SearchApiSearchMetadata - */ - readonly isEncoded?: boolean - - /** - * - * @type {boolean} - * @memberof SearchApiSearchMetadata - */ - readonly isExternal?: boolean - - /** - * - * @type {boolean} - * @memberof SearchApiSearchMetadata - */ - readonly isFavorite?: boolean - - /** - * - * @type {boolean} - * @memberof SearchApiSearchMetadata - */ - readonly isMotion?: boolean - - /** - * - * @type {boolean} - * @memberof SearchApiSearchMetadata - */ - readonly isOffline?: boolean - - /** - * - * @type {boolean} - * @memberof SearchApiSearchMetadata - */ - readonly isReadOnly?: boolean - - /** - * - * @type {boolean} - * @memberof SearchApiSearchMetadata - */ - readonly isVisible?: boolean - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly lensModel?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly libraryId?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly make?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly model?: string - - /** - * - * @type {AssetOrder} - * @memberof SearchApiSearchMetadata - */ - readonly order?: AssetOrder - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly originalFileName?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly originalPath?: string - - /** - * - * @type {number} - * @memberof SearchApiSearchMetadata - */ - readonly page?: number - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly resizePath?: string - - /** - * - * @type {number} - * @memberof SearchApiSearchMetadata - */ - readonly size?: number - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly state?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly takenAfter?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly takenBefore?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly trashedAfter?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly trashedBefore?: string - - /** - * - * @type {AssetTypeEnum} - * @memberof SearchApiSearchMetadata - */ - readonly type?: AssetTypeEnum - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly updatedAfter?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly updatedBefore?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchMetadata - */ - readonly webpPath?: string - - /** - * - * @type {boolean} - * @memberof SearchApiSearchMetadata - */ - readonly withArchived?: boolean - - /** - * - * @type {boolean} - * @memberof SearchApiSearchMetadata - */ - readonly withDeleted?: boolean - - /** - * - * @type {boolean} - * @memberof SearchApiSearchMetadata - */ - readonly withExif?: boolean - - /** - * - * @type {boolean} - * @memberof SearchApiSearchMetadata - */ - readonly withPeople?: boolean - - /** - * - * @type {boolean} - * @memberof SearchApiSearchMetadata - */ - readonly withStacked?: boolean -} - -/** - * Request parameters for searchPerson operation in SearchApi. - * @export - * @interface SearchApiSearchPersonRequest - */ -export interface SearchApiSearchPersonRequest { - /** - * - * @type {string} - * @memberof SearchApiSearchPerson - */ - readonly name: string - - /** - * - * @type {boolean} - * @memberof SearchApiSearchPerson - */ - readonly withHidden?: boolean -} - -/** - * Request parameters for searchSmart operation in SearchApi. - * @export - * @interface SearchApiSearchSmartRequest - */ -export interface SearchApiSearchSmartRequest { - /** - * - * @type {string} - * @memberof SearchApiSearchSmart - */ - readonly query: string - - /** - * - * @type {string} - * @memberof SearchApiSearchSmart - */ - readonly city?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchSmart - */ - readonly country?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchSmart - */ - readonly createdAfter?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchSmart - */ - readonly createdBefore?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchSmart - */ - readonly deviceId?: string - - /** - * - * @type {boolean} - * @memberof SearchApiSearchSmart - */ - readonly isArchived?: boolean - - /** - * - * @type {boolean} - * @memberof SearchApiSearchSmart - */ - readonly isEncoded?: boolean - - /** - * - * @type {boolean} - * @memberof SearchApiSearchSmart - */ - readonly isExternal?: boolean - - /** - * - * @type {boolean} - * @memberof SearchApiSearchSmart - */ - readonly isFavorite?: boolean - - /** - * - * @type {boolean} - * @memberof SearchApiSearchSmart - */ - readonly isMotion?: boolean - - /** - * - * @type {boolean} - * @memberof SearchApiSearchSmart - */ - readonly isOffline?: boolean - - /** - * - * @type {boolean} - * @memberof SearchApiSearchSmart - */ - readonly isReadOnly?: boolean - - /** - * - * @type {boolean} - * @memberof SearchApiSearchSmart - */ - readonly isVisible?: boolean - - /** - * - * @type {string} - * @memberof SearchApiSearchSmart - */ - readonly lensModel?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchSmart - */ - readonly libraryId?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchSmart - */ - readonly make?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchSmart - */ - readonly model?: string - - /** - * - * @type {number} - * @memberof SearchApiSearchSmart - */ - readonly page?: number - - /** - * - * @type {number} - * @memberof SearchApiSearchSmart - */ - readonly size?: number - - /** - * - * @type {string} - * @memberof SearchApiSearchSmart - */ - readonly state?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchSmart - */ - readonly takenAfter?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchSmart - */ - readonly takenBefore?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchSmart - */ - readonly trashedAfter?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchSmart - */ - readonly trashedBefore?: string - - /** - * - * @type {AssetTypeEnum} - * @memberof SearchApiSearchSmart - */ - readonly type?: AssetTypeEnum - - /** - * - * @type {string} - * @memberof SearchApiSearchSmart - */ - readonly updatedAfter?: string - - /** - * - * @type {string} - * @memberof SearchApiSearchSmart - */ - readonly updatedBefore?: string - - /** - * - * @type {boolean} - * @memberof SearchApiSearchSmart - */ - readonly withArchived?: boolean - - /** - * - * @type {boolean} - * @memberof SearchApiSearchSmart - */ - readonly withDeleted?: boolean - - /** - * - * @type {boolean} - * @memberof SearchApiSearchSmart - */ - readonly withExif?: boolean -} - -/** - * SearchApi - object-oriented interface - * @export - * @class SearchApi - * @extends {BaseAPI} - */ -export class SearchApi extends BaseAPI { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof SearchApi - */ - public getExploreData(options?: RawAxiosRequestConfig) { - return SearchApiFp(this.configuration).getExploreData(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {SearchApiGetSearchSuggestionsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof SearchApi - */ - public getSearchSuggestions(requestParameters: SearchApiGetSearchSuggestionsRequest, options?: RawAxiosRequestConfig) { - return SearchApiFp(this.configuration).getSearchSuggestions(requestParameters.type, requestParameters.country, requestParameters.make, requestParameters.model, requestParameters.state, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {SearchApiSearchRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @deprecated - * @throws {RequiredError} - * @memberof SearchApi - */ - public search(requestParameters: SearchApiSearchRequest = {}, options?: RawAxiosRequestConfig) { - return SearchApiFp(this.configuration).search(requestParameters.clip, requestParameters.motion, requestParameters.page, requestParameters.q, requestParameters.query, requestParameters.recent, requestParameters.size, requestParameters.smart, requestParameters.type, requestParameters.withArchived, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {SearchApiSearchMetadataRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof SearchApi - */ - public searchMetadata(requestParameters: SearchApiSearchMetadataRequest = {}, options?: RawAxiosRequestConfig) { - return SearchApiFp(this.configuration).searchMetadata(requestParameters.checksum, requestParameters.city, requestParameters.country, requestParameters.createdAfter, requestParameters.createdBefore, requestParameters.deviceAssetId, requestParameters.deviceId, requestParameters.encodedVideoPath, requestParameters.id, requestParameters.isArchived, requestParameters.isEncoded, requestParameters.isExternal, requestParameters.isFavorite, requestParameters.isMotion, requestParameters.isOffline, requestParameters.isReadOnly, requestParameters.isVisible, requestParameters.lensModel, requestParameters.libraryId, requestParameters.make, requestParameters.model, requestParameters.order, requestParameters.originalFileName, requestParameters.originalPath, requestParameters.page, requestParameters.resizePath, requestParameters.size, requestParameters.state, requestParameters.takenAfter, requestParameters.takenBefore, requestParameters.trashedAfter, requestParameters.trashedBefore, requestParameters.type, requestParameters.updatedAfter, requestParameters.updatedBefore, requestParameters.webpPath, requestParameters.withArchived, requestParameters.withDeleted, requestParameters.withExif, requestParameters.withPeople, requestParameters.withStacked, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {SearchApiSearchPersonRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof SearchApi - */ - public searchPerson(requestParameters: SearchApiSearchPersonRequest, options?: RawAxiosRequestConfig) { - return SearchApiFp(this.configuration).searchPerson(requestParameters.name, requestParameters.withHidden, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {SearchApiSearchSmartRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof SearchApi - */ - public searchSmart(requestParameters: SearchApiSearchSmartRequest, options?: RawAxiosRequestConfig) { - return SearchApiFp(this.configuration).searchSmart(requestParameters.query, requestParameters.city, requestParameters.country, requestParameters.createdAfter, requestParameters.createdBefore, requestParameters.deviceId, requestParameters.isArchived, requestParameters.isEncoded, requestParameters.isExternal, requestParameters.isFavorite, requestParameters.isMotion, requestParameters.isOffline, requestParameters.isReadOnly, requestParameters.isVisible, requestParameters.lensModel, requestParameters.libraryId, requestParameters.make, requestParameters.model, requestParameters.page, requestParameters.size, requestParameters.state, requestParameters.takenAfter, requestParameters.takenBefore, requestParameters.trashedAfter, requestParameters.trashedBefore, requestParameters.type, requestParameters.updatedAfter, requestParameters.updatedBefore, requestParameters.withArchived, requestParameters.withDeleted, requestParameters.withExif, options).then((request) => request(this.axios, this.basePath)); - } -} - -/** - * @export - */ -export const SearchTypeEnum = { - Image: 'IMAGE', - Video: 'VIDEO', - Audio: 'AUDIO', - Other: 'OTHER' -} as const; -export type SearchTypeEnum = typeof SearchTypeEnum[keyof typeof SearchTypeEnum]; - - -/** - * ServerInfoApi - axios parameter creator - * @export - */ -export const ServerInfoApiAxiosParamCreator = function (configuration?: Configuration) { - return { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getServerConfig: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/server-info/config`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getServerFeatures: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/server-info/features`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getServerInfo: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/server-info`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getServerStatistics: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/server-info/statistics`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getServerVersion: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/server-info/version`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getSupportedMediaTypes: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/server-info/media-types`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getTheme: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/server-info/theme`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - pingServer: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/server-info/ping`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - setAdminOnboarding: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/server-info/admin-onboarding`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - } -}; - -/** - * ServerInfoApi - functional programming interface - * @export - */ -export const ServerInfoApiFp = function(configuration?: Configuration) { - const localVarAxiosParamCreator = ServerInfoApiAxiosParamCreator(configuration) - return { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getServerConfig(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getServerConfig(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['ServerInfoApi.getServerConfig']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getServerFeatures(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getServerFeatures(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['ServerInfoApi.getServerFeatures']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getServerInfo(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getServerInfo(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['ServerInfoApi.getServerInfo']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getServerStatistics(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getServerStatistics(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['ServerInfoApi.getServerStatistics']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getServerVersion(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getServerVersion(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['ServerInfoApi.getServerVersion']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getSupportedMediaTypes(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getSupportedMediaTypes(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['ServerInfoApi.getSupportedMediaTypes']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getTheme(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getTheme(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['ServerInfoApi.getTheme']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async pingServer(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.pingServer(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['ServerInfoApi.pingServer']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async setAdminOnboarding(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.setAdminOnboarding(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['ServerInfoApi.setAdminOnboarding']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - } -}; - -/** - * ServerInfoApi - factory interface - * @export - */ -export const ServerInfoApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { - const localVarFp = ServerInfoApiFp(configuration) - return { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getServerConfig(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getServerConfig(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getServerFeatures(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getServerFeatures(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getServerInfo(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getServerInfo(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getServerStatistics(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getServerStatistics(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getServerVersion(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getServerVersion(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getSupportedMediaTypes(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getSupportedMediaTypes(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getTheme(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getTheme(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - pingServer(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.pingServer(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - setAdminOnboarding(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.setAdminOnboarding(options).then((request) => request(axios, basePath)); - }, - }; -}; - -/** - * ServerInfoApi - object-oriented interface - * @export - * @class ServerInfoApi - * @extends {BaseAPI} - */ -export class ServerInfoApi extends BaseAPI { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof ServerInfoApi - */ - public getServerConfig(options?: RawAxiosRequestConfig) { - return ServerInfoApiFp(this.configuration).getServerConfig(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof ServerInfoApi - */ - public getServerFeatures(options?: RawAxiosRequestConfig) { - return ServerInfoApiFp(this.configuration).getServerFeatures(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof ServerInfoApi - */ - public getServerInfo(options?: RawAxiosRequestConfig) { - return ServerInfoApiFp(this.configuration).getServerInfo(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof ServerInfoApi - */ - public getServerStatistics(options?: RawAxiosRequestConfig) { - return ServerInfoApiFp(this.configuration).getServerStatistics(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof ServerInfoApi - */ - public getServerVersion(options?: RawAxiosRequestConfig) { - return ServerInfoApiFp(this.configuration).getServerVersion(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof ServerInfoApi - */ - public getSupportedMediaTypes(options?: RawAxiosRequestConfig) { - return ServerInfoApiFp(this.configuration).getSupportedMediaTypes(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof ServerInfoApi - */ - public getTheme(options?: RawAxiosRequestConfig) { - return ServerInfoApiFp(this.configuration).getTheme(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof ServerInfoApi - */ - public pingServer(options?: RawAxiosRequestConfig) { - return ServerInfoApiFp(this.configuration).pingServer(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof ServerInfoApi - */ - public setAdminOnboarding(options?: RawAxiosRequestConfig) { - return ServerInfoApiFp(this.configuration).setAdminOnboarding(options).then((request) => request(this.axios, this.basePath)); - } -} - - - -/** - * SharedLinkApi - axios parameter creator - * @export - */ -export const SharedLinkApiAxiosParamCreator = function (configuration?: Configuration) { - return { - /** - * - * @param {string} id - * @param {AssetIdsDto} assetIdsDto - * @param {string} [key] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - addSharedLinkAssets: async (id: string, assetIdsDto: AssetIdsDto, key?: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('addSharedLinkAssets', 'id', id) - // verify required parameter 'assetIdsDto' is not null or undefined - assertParamExists('addSharedLinkAssets', 'assetIdsDto', assetIdsDto) - const localVarPath = `/shared-link/{id}/assets` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (key !== undefined) { - localVarQueryParameter['key'] = key; - } - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(assetIdsDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {SharedLinkCreateDto} sharedLinkCreateDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - createSharedLink: async (sharedLinkCreateDto: SharedLinkCreateDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'sharedLinkCreateDto' is not null or undefined - assertParamExists('createSharedLink', 'sharedLinkCreateDto', sharedLinkCreateDto) - const localVarPath = `/shared-link`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(sharedLinkCreateDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAllSharedLinks: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/shared-link`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} [key] - * @param {string} [password] - * @param {string} [token] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getMySharedLink: async (key?: string, password?: string, token?: string, options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/shared-link/me`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (key !== undefined) { - localVarQueryParameter['key'] = key; - } - - if (password !== undefined) { - localVarQueryParameter['password'] = password; - } - - if (token !== undefined) { - localVarQueryParameter['token'] = token; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getSharedLinkById: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('getSharedLinkById', 'id', id) - const localVarPath = `/shared-link/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - removeSharedLink: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('removeSharedLink', 'id', id) - const localVarPath = `/shared-link/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {AssetIdsDto} assetIdsDto - * @param {string} [key] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - removeSharedLinkAssets: async (id: string, assetIdsDto: AssetIdsDto, key?: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('removeSharedLinkAssets', 'id', id) - // verify required parameter 'assetIdsDto' is not null or undefined - assertParamExists('removeSharedLinkAssets', 'assetIdsDto', assetIdsDto) - const localVarPath = `/shared-link/{id}/assets` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (key !== undefined) { - localVarQueryParameter['key'] = key; - } - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(assetIdsDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {SharedLinkEditDto} sharedLinkEditDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updateSharedLink: async (id: string, sharedLinkEditDto: SharedLinkEditDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('updateSharedLink', 'id', id) - // verify required parameter 'sharedLinkEditDto' is not null or undefined - assertParamExists('updateSharedLink', 'sharedLinkEditDto', sharedLinkEditDto) - const localVarPath = `/shared-link/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'PATCH', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(sharedLinkEditDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - } -}; - -/** - * SharedLinkApi - functional programming interface - * @export - */ -export const SharedLinkApiFp = function(configuration?: Configuration) { - const localVarAxiosParamCreator = SharedLinkApiAxiosParamCreator(configuration) - return { - /** - * - * @param {string} id - * @param {AssetIdsDto} assetIdsDto - * @param {string} [key] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async addSharedLinkAssets(id: string, assetIdsDto: AssetIdsDto, key?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.addSharedLinkAssets(id, assetIdsDto, key, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['SharedLinkApi.addSharedLinkAssets']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {SharedLinkCreateDto} sharedLinkCreateDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async createSharedLink(sharedLinkCreateDto: SharedLinkCreateDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.createSharedLink(sharedLinkCreateDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['SharedLinkApi.createSharedLink']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getAllSharedLinks(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAllSharedLinks(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['SharedLinkApi.getAllSharedLinks']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} [key] - * @param {string} [password] - * @param {string} [token] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getMySharedLink(key?: string, password?: string, token?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getMySharedLink(key, password, token, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['SharedLinkApi.getMySharedLink']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getSharedLinkById(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getSharedLinkById(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['SharedLinkApi.getSharedLinkById']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async removeSharedLink(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.removeSharedLink(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['SharedLinkApi.removeSharedLink']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {AssetIdsDto} assetIdsDto - * @param {string} [key] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async removeSharedLinkAssets(id: string, assetIdsDto: AssetIdsDto, key?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.removeSharedLinkAssets(id, assetIdsDto, key, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['SharedLinkApi.removeSharedLinkAssets']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {SharedLinkEditDto} sharedLinkEditDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async updateSharedLink(id: string, sharedLinkEditDto: SharedLinkEditDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.updateSharedLink(id, sharedLinkEditDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['SharedLinkApi.updateSharedLink']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - } -}; - -/** - * SharedLinkApi - factory interface - * @export - */ -export const SharedLinkApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { - const localVarFp = SharedLinkApiFp(configuration) - return { - /** - * - * @param {SharedLinkApiAddSharedLinkAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - addSharedLinkAssets(requestParameters: SharedLinkApiAddSharedLinkAssetsRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.addSharedLinkAssets(requestParameters.id, requestParameters.assetIdsDto, requestParameters.key, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {SharedLinkApiCreateSharedLinkRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - createSharedLink(requestParameters: SharedLinkApiCreateSharedLinkRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.createSharedLink(requestParameters.sharedLinkCreateDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAllSharedLinks(options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getAllSharedLinks(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {SharedLinkApiGetMySharedLinkRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getMySharedLink(requestParameters: SharedLinkApiGetMySharedLinkRequest = {}, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getMySharedLink(requestParameters.key, requestParameters.password, requestParameters.token, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {SharedLinkApiGetSharedLinkByIdRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getSharedLinkById(requestParameters: SharedLinkApiGetSharedLinkByIdRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getSharedLinkById(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {SharedLinkApiRemoveSharedLinkRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - removeSharedLink(requestParameters: SharedLinkApiRemoveSharedLinkRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.removeSharedLink(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {SharedLinkApiRemoveSharedLinkAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - removeSharedLinkAssets(requestParameters: SharedLinkApiRemoveSharedLinkAssetsRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.removeSharedLinkAssets(requestParameters.id, requestParameters.assetIdsDto, requestParameters.key, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {SharedLinkApiUpdateSharedLinkRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updateSharedLink(requestParameters: SharedLinkApiUpdateSharedLinkRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.updateSharedLink(requestParameters.id, requestParameters.sharedLinkEditDto, options).then((request) => request(axios, basePath)); - }, - }; -}; - -/** - * Request parameters for addSharedLinkAssets operation in SharedLinkApi. - * @export - * @interface SharedLinkApiAddSharedLinkAssetsRequest - */ -export interface SharedLinkApiAddSharedLinkAssetsRequest { - /** - * - * @type {string} - * @memberof SharedLinkApiAddSharedLinkAssets - */ - readonly id: string - - /** - * - * @type {AssetIdsDto} - * @memberof SharedLinkApiAddSharedLinkAssets - */ - readonly assetIdsDto: AssetIdsDto - - /** - * - * @type {string} - * @memberof SharedLinkApiAddSharedLinkAssets - */ - readonly key?: string -} - -/** - * Request parameters for createSharedLink operation in SharedLinkApi. - * @export - * @interface SharedLinkApiCreateSharedLinkRequest - */ -export interface SharedLinkApiCreateSharedLinkRequest { - /** - * - * @type {SharedLinkCreateDto} - * @memberof SharedLinkApiCreateSharedLink - */ - readonly sharedLinkCreateDto: SharedLinkCreateDto -} - -/** - * Request parameters for getMySharedLink operation in SharedLinkApi. - * @export - * @interface SharedLinkApiGetMySharedLinkRequest - */ -export interface SharedLinkApiGetMySharedLinkRequest { - /** - * - * @type {string} - * @memberof SharedLinkApiGetMySharedLink - */ - readonly key?: string - - /** - * - * @type {string} - * @memberof SharedLinkApiGetMySharedLink - */ - readonly password?: string - - /** - * - * @type {string} - * @memberof SharedLinkApiGetMySharedLink - */ - readonly token?: string -} - -/** - * Request parameters for getSharedLinkById operation in SharedLinkApi. - * @export - * @interface SharedLinkApiGetSharedLinkByIdRequest - */ -export interface SharedLinkApiGetSharedLinkByIdRequest { - /** - * - * @type {string} - * @memberof SharedLinkApiGetSharedLinkById - */ - readonly id: string -} - -/** - * Request parameters for removeSharedLink operation in SharedLinkApi. - * @export - * @interface SharedLinkApiRemoveSharedLinkRequest - */ -export interface SharedLinkApiRemoveSharedLinkRequest { - /** - * - * @type {string} - * @memberof SharedLinkApiRemoveSharedLink - */ - readonly id: string -} - -/** - * Request parameters for removeSharedLinkAssets operation in SharedLinkApi. - * @export - * @interface SharedLinkApiRemoveSharedLinkAssetsRequest - */ -export interface SharedLinkApiRemoveSharedLinkAssetsRequest { - /** - * - * @type {string} - * @memberof SharedLinkApiRemoveSharedLinkAssets - */ - readonly id: string - - /** - * - * @type {AssetIdsDto} - * @memberof SharedLinkApiRemoveSharedLinkAssets - */ - readonly assetIdsDto: AssetIdsDto - - /** - * - * @type {string} - * @memberof SharedLinkApiRemoveSharedLinkAssets - */ - readonly key?: string -} - -/** - * Request parameters for updateSharedLink operation in SharedLinkApi. - * @export - * @interface SharedLinkApiUpdateSharedLinkRequest - */ -export interface SharedLinkApiUpdateSharedLinkRequest { - /** - * - * @type {string} - * @memberof SharedLinkApiUpdateSharedLink - */ - readonly id: string - - /** - * - * @type {SharedLinkEditDto} - * @memberof SharedLinkApiUpdateSharedLink - */ - readonly sharedLinkEditDto: SharedLinkEditDto -} - -/** - * SharedLinkApi - object-oriented interface - * @export - * @class SharedLinkApi - * @extends {BaseAPI} - */ -export class SharedLinkApi extends BaseAPI { - /** - * - * @param {SharedLinkApiAddSharedLinkAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof SharedLinkApi - */ - public addSharedLinkAssets(requestParameters: SharedLinkApiAddSharedLinkAssetsRequest, options?: RawAxiosRequestConfig) { - return SharedLinkApiFp(this.configuration).addSharedLinkAssets(requestParameters.id, requestParameters.assetIdsDto, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {SharedLinkApiCreateSharedLinkRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof SharedLinkApi - */ - public createSharedLink(requestParameters: SharedLinkApiCreateSharedLinkRequest, options?: RawAxiosRequestConfig) { - return SharedLinkApiFp(this.configuration).createSharedLink(requestParameters.sharedLinkCreateDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof SharedLinkApi - */ - public getAllSharedLinks(options?: RawAxiosRequestConfig) { - return SharedLinkApiFp(this.configuration).getAllSharedLinks(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {SharedLinkApiGetMySharedLinkRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof SharedLinkApi - */ - public getMySharedLink(requestParameters: SharedLinkApiGetMySharedLinkRequest = {}, options?: RawAxiosRequestConfig) { - return SharedLinkApiFp(this.configuration).getMySharedLink(requestParameters.key, requestParameters.password, requestParameters.token, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {SharedLinkApiGetSharedLinkByIdRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof SharedLinkApi - */ - public getSharedLinkById(requestParameters: SharedLinkApiGetSharedLinkByIdRequest, options?: RawAxiosRequestConfig) { - return SharedLinkApiFp(this.configuration).getSharedLinkById(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {SharedLinkApiRemoveSharedLinkRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof SharedLinkApi - */ - public removeSharedLink(requestParameters: SharedLinkApiRemoveSharedLinkRequest, options?: RawAxiosRequestConfig) { - return SharedLinkApiFp(this.configuration).removeSharedLink(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {SharedLinkApiRemoveSharedLinkAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof SharedLinkApi - */ - public removeSharedLinkAssets(requestParameters: SharedLinkApiRemoveSharedLinkAssetsRequest, options?: RawAxiosRequestConfig) { - return SharedLinkApiFp(this.configuration).removeSharedLinkAssets(requestParameters.id, requestParameters.assetIdsDto, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {SharedLinkApiUpdateSharedLinkRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof SharedLinkApi - */ - public updateSharedLink(requestParameters: SharedLinkApiUpdateSharedLinkRequest, options?: RawAxiosRequestConfig) { - return SharedLinkApiFp(this.configuration).updateSharedLink(requestParameters.id, requestParameters.sharedLinkEditDto, options).then((request) => request(this.axios, this.basePath)); - } -} - - - -/** - * SystemConfigApi - axios parameter creator - * @export - */ -export const SystemConfigApiAxiosParamCreator = function (configuration?: Configuration) { - return { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getConfig: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/system-config`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getConfigDefaults: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/system-config/defaults`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {MapTheme} theme - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getMapStyle: async (theme: MapTheme, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'theme' is not null or undefined - assertParamExists('getMapStyle', 'theme', theme) - const localVarPath = `/system-config/map/style.json`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (theme !== undefined) { - localVarQueryParameter['theme'] = theme; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getStorageTemplateOptions: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/system-config/storage-template-options`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {SystemConfigDto} systemConfigDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updateConfig: async (systemConfigDto: SystemConfigDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'systemConfigDto' is not null or undefined - assertParamExists('updateConfig', 'systemConfigDto', systemConfigDto) - const localVarPath = `/system-config`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(systemConfigDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - } -}; - -/** - * SystemConfigApi - functional programming interface - * @export - */ -export const SystemConfigApiFp = function(configuration?: Configuration) { - const localVarAxiosParamCreator = SystemConfigApiAxiosParamCreator(configuration) - return { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getConfig(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getConfig(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['SystemConfigApi.getConfig']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getConfigDefaults(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getConfigDefaults(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['SystemConfigApi.getConfigDefaults']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {MapTheme} theme - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getMapStyle(theme: MapTheme, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getMapStyle(theme, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['SystemConfigApi.getMapStyle']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getStorageTemplateOptions(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getStorageTemplateOptions(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['SystemConfigApi.getStorageTemplateOptions']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {SystemConfigDto} systemConfigDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async updateConfig(systemConfigDto: SystemConfigDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.updateConfig(systemConfigDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['SystemConfigApi.updateConfig']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - } -}; - -/** - * SystemConfigApi - factory interface - * @export - */ -export const SystemConfigApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { - const localVarFp = SystemConfigApiFp(configuration) - return { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getConfig(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getConfig(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getConfigDefaults(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getConfigDefaults(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {SystemConfigApiGetMapStyleRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getMapStyle(requestParameters: SystemConfigApiGetMapStyleRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getMapStyle(requestParameters.theme, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getStorageTemplateOptions(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getStorageTemplateOptions(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {SystemConfigApiUpdateConfigRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updateConfig(requestParameters: SystemConfigApiUpdateConfigRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.updateConfig(requestParameters.systemConfigDto, options).then((request) => request(axios, basePath)); - }, - }; -}; - -/** - * Request parameters for getMapStyle operation in SystemConfigApi. - * @export - * @interface SystemConfigApiGetMapStyleRequest - */ -export interface SystemConfigApiGetMapStyleRequest { - /** - * - * @type {MapTheme} - * @memberof SystemConfigApiGetMapStyle - */ - readonly theme: MapTheme -} - -/** - * Request parameters for updateConfig operation in SystemConfigApi. - * @export - * @interface SystemConfigApiUpdateConfigRequest - */ -export interface SystemConfigApiUpdateConfigRequest { - /** - * - * @type {SystemConfigDto} - * @memberof SystemConfigApiUpdateConfig - */ - readonly systemConfigDto: SystemConfigDto -} - -/** - * SystemConfigApi - object-oriented interface - * @export - * @class SystemConfigApi - * @extends {BaseAPI} - */ -export class SystemConfigApi extends BaseAPI { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof SystemConfigApi - */ - public getConfig(options?: RawAxiosRequestConfig) { - return SystemConfigApiFp(this.configuration).getConfig(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof SystemConfigApi - */ - public getConfigDefaults(options?: RawAxiosRequestConfig) { - return SystemConfigApiFp(this.configuration).getConfigDefaults(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {SystemConfigApiGetMapStyleRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof SystemConfigApi - */ - public getMapStyle(requestParameters: SystemConfigApiGetMapStyleRequest, options?: RawAxiosRequestConfig) { - return SystemConfigApiFp(this.configuration).getMapStyle(requestParameters.theme, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof SystemConfigApi - */ - public getStorageTemplateOptions(options?: RawAxiosRequestConfig) { - return SystemConfigApiFp(this.configuration).getStorageTemplateOptions(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {SystemConfigApiUpdateConfigRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof SystemConfigApi - */ - public updateConfig(requestParameters: SystemConfigApiUpdateConfigRequest, options?: RawAxiosRequestConfig) { - return SystemConfigApiFp(this.configuration).updateConfig(requestParameters.systemConfigDto, options).then((request) => request(this.axios, this.basePath)); - } -} - - - -/** - * TagApi - axios parameter creator - * @export - */ -export const TagApiAxiosParamCreator = function (configuration?: Configuration) { - return { - /** - * - * @param {CreateTagDto} createTagDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - createTag: async (createTagDto: CreateTagDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'createTagDto' is not null or undefined - assertParamExists('createTag', 'createTagDto', createTagDto) - const localVarPath = `/tag`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(createTagDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - deleteTag: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('deleteTag', 'id', id) - const localVarPath = `/tag/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAllTags: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/tag`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getTagAssets: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('getTagAssets', 'id', id) - const localVarPath = `/tag/{id}/assets` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getTagById: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('getTagById', 'id', id) - const localVarPath = `/tag/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {AssetIdsDto} assetIdsDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - tagAssets: async (id: string, assetIdsDto: AssetIdsDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('tagAssets', 'id', id) - // verify required parameter 'assetIdsDto' is not null or undefined - assertParamExists('tagAssets', 'assetIdsDto', assetIdsDto) - const localVarPath = `/tag/{id}/assets` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(assetIdsDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {AssetIdsDto} assetIdsDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - untagAssets: async (id: string, assetIdsDto: AssetIdsDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('untagAssets', 'id', id) - // verify required parameter 'assetIdsDto' is not null or undefined - assertParamExists('untagAssets', 'assetIdsDto', assetIdsDto) - const localVarPath = `/tag/{id}/assets` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(assetIdsDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {UpdateTagDto} updateTagDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updateTag: async (id: string, updateTagDto: UpdateTagDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('updateTag', 'id', id) - // verify required parameter 'updateTagDto' is not null or undefined - assertParamExists('updateTag', 'updateTagDto', updateTagDto) - const localVarPath = `/tag/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'PATCH', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(updateTagDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - } -}; - -/** - * TagApi - functional programming interface - * @export - */ -export const TagApiFp = function(configuration?: Configuration) { - const localVarAxiosParamCreator = TagApiAxiosParamCreator(configuration) - return { - /** - * - * @param {CreateTagDto} createTagDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async createTag(createTagDto: CreateTagDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.createTag(createTagDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['TagApi.createTag']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async deleteTag(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.deleteTag(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['TagApi.deleteTag']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getAllTags(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAllTags(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['TagApi.getAllTags']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getTagAssets(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getTagAssets(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['TagApi.getTagAssets']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getTagById(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getTagById(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['TagApi.getTagById']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {AssetIdsDto} assetIdsDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async tagAssets(id: string, assetIdsDto: AssetIdsDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.tagAssets(id, assetIdsDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['TagApi.tagAssets']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {AssetIdsDto} assetIdsDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async untagAssets(id: string, assetIdsDto: AssetIdsDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.untagAssets(id, assetIdsDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['TagApi.untagAssets']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {UpdateTagDto} updateTagDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async updateTag(id: string, updateTagDto: UpdateTagDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.updateTag(id, updateTagDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['TagApi.updateTag']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - } -}; - -/** - * TagApi - factory interface - * @export - */ -export const TagApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { - const localVarFp = TagApiFp(configuration) - return { - /** - * - * @param {TagApiCreateTagRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - createTag(requestParameters: TagApiCreateTagRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.createTag(requestParameters.createTagDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {TagApiDeleteTagRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - deleteTag(requestParameters: TagApiDeleteTagRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.deleteTag(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAllTags(options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getAllTags(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {TagApiGetTagAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getTagAssets(requestParameters: TagApiGetTagAssetsRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getTagAssets(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {TagApiGetTagByIdRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getTagById(requestParameters: TagApiGetTagByIdRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getTagById(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {TagApiTagAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - tagAssets(requestParameters: TagApiTagAssetsRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.tagAssets(requestParameters.id, requestParameters.assetIdsDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {TagApiUntagAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - untagAssets(requestParameters: TagApiUntagAssetsRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.untagAssets(requestParameters.id, requestParameters.assetIdsDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {TagApiUpdateTagRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updateTag(requestParameters: TagApiUpdateTagRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.updateTag(requestParameters.id, requestParameters.updateTagDto, options).then((request) => request(axios, basePath)); - }, - }; -}; - -/** - * Request parameters for createTag operation in TagApi. - * @export - * @interface TagApiCreateTagRequest - */ -export interface TagApiCreateTagRequest { - /** - * - * @type {CreateTagDto} - * @memberof TagApiCreateTag - */ - readonly createTagDto: CreateTagDto -} - -/** - * Request parameters for deleteTag operation in TagApi. - * @export - * @interface TagApiDeleteTagRequest - */ -export interface TagApiDeleteTagRequest { - /** - * - * @type {string} - * @memberof TagApiDeleteTag - */ - readonly id: string -} - -/** - * Request parameters for getTagAssets operation in TagApi. - * @export - * @interface TagApiGetTagAssetsRequest - */ -export interface TagApiGetTagAssetsRequest { - /** - * - * @type {string} - * @memberof TagApiGetTagAssets - */ - readonly id: string -} - -/** - * Request parameters for getTagById operation in TagApi. - * @export - * @interface TagApiGetTagByIdRequest - */ -export interface TagApiGetTagByIdRequest { - /** - * - * @type {string} - * @memberof TagApiGetTagById - */ - readonly id: string -} - -/** - * Request parameters for tagAssets operation in TagApi. - * @export - * @interface TagApiTagAssetsRequest - */ -export interface TagApiTagAssetsRequest { - /** - * - * @type {string} - * @memberof TagApiTagAssets - */ - readonly id: string - - /** - * - * @type {AssetIdsDto} - * @memberof TagApiTagAssets - */ - readonly assetIdsDto: AssetIdsDto -} - -/** - * Request parameters for untagAssets operation in TagApi. - * @export - * @interface TagApiUntagAssetsRequest - */ -export interface TagApiUntagAssetsRequest { - /** - * - * @type {string} - * @memberof TagApiUntagAssets - */ - readonly id: string - - /** - * - * @type {AssetIdsDto} - * @memberof TagApiUntagAssets - */ - readonly assetIdsDto: AssetIdsDto -} - -/** - * Request parameters for updateTag operation in TagApi. - * @export - * @interface TagApiUpdateTagRequest - */ -export interface TagApiUpdateTagRequest { - /** - * - * @type {string} - * @memberof TagApiUpdateTag - */ - readonly id: string - - /** - * - * @type {UpdateTagDto} - * @memberof TagApiUpdateTag - */ - readonly updateTagDto: UpdateTagDto -} - -/** - * TagApi - object-oriented interface - * @export - * @class TagApi - * @extends {BaseAPI} - */ -export class TagApi extends BaseAPI { - /** - * - * @param {TagApiCreateTagRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof TagApi - */ - public createTag(requestParameters: TagApiCreateTagRequest, options?: RawAxiosRequestConfig) { - return TagApiFp(this.configuration).createTag(requestParameters.createTagDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {TagApiDeleteTagRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof TagApi - */ - public deleteTag(requestParameters: TagApiDeleteTagRequest, options?: RawAxiosRequestConfig) { - return TagApiFp(this.configuration).deleteTag(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof TagApi - */ - public getAllTags(options?: RawAxiosRequestConfig) { - return TagApiFp(this.configuration).getAllTags(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {TagApiGetTagAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof TagApi - */ - public getTagAssets(requestParameters: TagApiGetTagAssetsRequest, options?: RawAxiosRequestConfig) { - return TagApiFp(this.configuration).getTagAssets(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {TagApiGetTagByIdRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof TagApi - */ - public getTagById(requestParameters: TagApiGetTagByIdRequest, options?: RawAxiosRequestConfig) { - return TagApiFp(this.configuration).getTagById(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {TagApiTagAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof TagApi - */ - public tagAssets(requestParameters: TagApiTagAssetsRequest, options?: RawAxiosRequestConfig) { - return TagApiFp(this.configuration).tagAssets(requestParameters.id, requestParameters.assetIdsDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {TagApiUntagAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof TagApi - */ - public untagAssets(requestParameters: TagApiUntagAssetsRequest, options?: RawAxiosRequestConfig) { - return TagApiFp(this.configuration).untagAssets(requestParameters.id, requestParameters.assetIdsDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {TagApiUpdateTagRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof TagApi - */ - public updateTag(requestParameters: TagApiUpdateTagRequest, options?: RawAxiosRequestConfig) { - return TagApiFp(this.configuration).updateTag(requestParameters.id, requestParameters.updateTagDto, options).then((request) => request(this.axios, this.basePath)); - } -} - - - -/** - * TrashApi - axios parameter creator - * @export - */ -export const TrashApiAxiosParamCreator = function (configuration?: Configuration) { - return { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - emptyTrash: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/trash/empty`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {BulkIdsDto} bulkIdsDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - restoreAssets: async (bulkIdsDto: BulkIdsDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'bulkIdsDto' is not null or undefined - assertParamExists('restoreAssets', 'bulkIdsDto', bulkIdsDto) - const localVarPath = `/trash/restore/assets`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(bulkIdsDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - restoreTrash: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/trash/restore`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - } -}; - -/** - * TrashApi - functional programming interface - * @export - */ -export const TrashApiFp = function(configuration?: Configuration) { - const localVarAxiosParamCreator = TrashApiAxiosParamCreator(configuration) - return { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async emptyTrash(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.emptyTrash(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['TrashApi.emptyTrash']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {BulkIdsDto} bulkIdsDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async restoreAssets(bulkIdsDto: BulkIdsDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.restoreAssets(bulkIdsDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['TrashApi.restoreAssets']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async restoreTrash(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.restoreTrash(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['TrashApi.restoreTrash']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - } -}; - -/** - * TrashApi - factory interface - * @export - */ -export const TrashApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { - const localVarFp = TrashApiFp(configuration) - return { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - emptyTrash(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.emptyTrash(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {TrashApiRestoreAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - restoreAssets(requestParameters: TrashApiRestoreAssetsRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.restoreAssets(requestParameters.bulkIdsDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - restoreTrash(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.restoreTrash(options).then((request) => request(axios, basePath)); - }, - }; -}; - -/** - * Request parameters for restoreAssets operation in TrashApi. - * @export - * @interface TrashApiRestoreAssetsRequest - */ -export interface TrashApiRestoreAssetsRequest { - /** - * - * @type {BulkIdsDto} - * @memberof TrashApiRestoreAssets - */ - readonly bulkIdsDto: BulkIdsDto -} - -/** - * TrashApi - object-oriented interface - * @export - * @class TrashApi - * @extends {BaseAPI} - */ -export class TrashApi extends BaseAPI { - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof TrashApi - */ - public emptyTrash(options?: RawAxiosRequestConfig) { - return TrashApiFp(this.configuration).emptyTrash(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {TrashApiRestoreAssetsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof TrashApi - */ - public restoreAssets(requestParameters: TrashApiRestoreAssetsRequest, options?: RawAxiosRequestConfig) { - return TrashApiFp(this.configuration).restoreAssets(requestParameters.bulkIdsDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof TrashApi - */ - public restoreTrash(options?: RawAxiosRequestConfig) { - return TrashApiFp(this.configuration).restoreTrash(options).then((request) => request(this.axios, this.basePath)); - } -} - - - -/** - * UserApi - axios parameter creator - * @export - */ -export const UserApiAxiosParamCreator = function (configuration?: Configuration) { - return { - /** - * - * @param {File} file - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - createProfileImage: async (file: File, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'file' is not null or undefined - assertParamExists('createProfileImage', 'file', file) - const localVarPath = `/user/profile-image`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - const localVarFormParams = new ((configuration && configuration.formDataCtor) || FormData)(); - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - if (file !== undefined) { - localVarFormParams.append('file', file as any); - } - - - localVarHeaderParameter['Content-Type'] = 'multipart/form-data'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = localVarFormParams; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {CreateUserDto} createUserDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - createUser: async (createUserDto: CreateUserDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'createUserDto' is not null or undefined - assertParamExists('createUser', 'createUserDto', createUserDto) - const localVarPath = `/user`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(createUserDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - deleteProfileImage: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/user/profile-image`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - deleteUser: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('deleteUser', 'id', id) - const localVarPath = `/user/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {boolean} isAll - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAllUsers: async (isAll: boolean, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'isAll' is not null or undefined - assertParamExists('getAllUsers', 'isAll', isAll) - const localVarPath = `/user`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - if (isAll !== undefined) { - localVarQueryParameter['isAll'] = isAll; - } - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getMyUserInfo: async (options: RawAxiosRequestConfig = {}): Promise => { - const localVarPath = `/user/me`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getProfileImage: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('getProfileImage', 'id', id) - const localVarPath = `/user/profile-image/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getUserById: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('getUserById', 'id', id) - const localVarPath = `/user/info/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - restoreUser: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('restoreUser', 'id', id) - const localVarPath = `/user/{id}/restore` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {UpdateUserDto} updateUserDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updateUser: async (updateUserDto: UpdateUserDto, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'updateUserDto' is not null or undefined - assertParamExists('updateUser', 'updateUserDto', updateUserDto) - const localVarPath = `/user`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication cookie required - - // authentication api_key required - await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) - - // authentication bearer required - // http bearer authentication required - await setBearerAuthToObject(localVarHeaderParameter, configuration) - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(updateUserDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - } -}; - -/** - * UserApi - functional programming interface - * @export - */ -export const UserApiFp = function(configuration?: Configuration) { - const localVarAxiosParamCreator = UserApiAxiosParamCreator(configuration) - return { - /** - * - * @param {File} file - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async createProfileImage(file: File, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.createProfileImage(file, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['UserApi.createProfileImage']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {CreateUserDto} createUserDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async createUser(createUserDto: CreateUserDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.createUser(createUserDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['UserApi.createUser']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async deleteProfileImage(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.deleteProfileImage(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['UserApi.deleteProfileImage']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async deleteUser(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.deleteUser(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['UserApi.deleteUser']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {boolean} isAll - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getAllUsers(isAll: boolean, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAllUsers(isAll, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['UserApi.getAllUsers']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getMyUserInfo(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getMyUserInfo(options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['UserApi.getMyUserInfo']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getProfileImage(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getProfileImage(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['UserApi.getProfileImage']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getUserById(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getUserById(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['UserApi.getUserById']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async restoreUser(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.restoreUser(id, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['UserApi.restoreUser']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - /** - * - * @param {UpdateUserDto} updateUserDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async updateUser(updateUserDto: UpdateUserDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.updateUser(updateUserDto, options); - const index = configuration?.serverIndex ?? 0; - const operationBasePath = operationServerMap['UserApi.updateUser']?.[index]?.url; - return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); - }, - } -}; - -/** - * UserApi - factory interface - * @export - */ -export const UserApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { - const localVarFp = UserApiFp(configuration) - return { - /** - * - * @param {UserApiCreateProfileImageRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - createProfileImage(requestParameters: UserApiCreateProfileImageRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.createProfileImage(requestParameters.file, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {UserApiCreateUserRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - createUser(requestParameters: UserApiCreateUserRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.createUser(requestParameters.createUserDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - deleteProfileImage(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.deleteProfileImage(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {UserApiDeleteUserRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - deleteUser(requestParameters: UserApiDeleteUserRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.deleteUser(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {UserApiGetAllUsersRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAllUsers(requestParameters: UserApiGetAllUsersRequest, options?: RawAxiosRequestConfig): AxiosPromise> { - return localVarFp.getAllUsers(requestParameters.isAll, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getMyUserInfo(options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getMyUserInfo(options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {UserApiGetProfileImageRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getProfileImage(requestParameters: UserApiGetProfileImageRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getProfileImage(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {UserApiGetUserByIdRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getUserById(requestParameters: UserApiGetUserByIdRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.getUserById(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {UserApiRestoreUserRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - restoreUser(requestParameters: UserApiRestoreUserRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.restoreUser(requestParameters.id, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {UserApiUpdateUserRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - updateUser(requestParameters: UserApiUpdateUserRequest, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.updateUser(requestParameters.updateUserDto, options).then((request) => request(axios, basePath)); - }, - }; -}; - -/** - * Request parameters for createProfileImage operation in UserApi. - * @export - * @interface UserApiCreateProfileImageRequest - */ -export interface UserApiCreateProfileImageRequest { - /** - * - * @type {File} - * @memberof UserApiCreateProfileImage - */ - readonly file: File -} - -/** - * Request parameters for createUser operation in UserApi. - * @export - * @interface UserApiCreateUserRequest - */ -export interface UserApiCreateUserRequest { - /** - * - * @type {CreateUserDto} - * @memberof UserApiCreateUser - */ - readonly createUserDto: CreateUserDto -} - -/** - * Request parameters for deleteUser operation in UserApi. - * @export - * @interface UserApiDeleteUserRequest - */ -export interface UserApiDeleteUserRequest { - /** - * - * @type {string} - * @memberof UserApiDeleteUser - */ - readonly id: string -} - -/** - * Request parameters for getAllUsers operation in UserApi. - * @export - * @interface UserApiGetAllUsersRequest - */ -export interface UserApiGetAllUsersRequest { - /** - * - * @type {boolean} - * @memberof UserApiGetAllUsers - */ - readonly isAll: boolean -} - -/** - * Request parameters for getProfileImage operation in UserApi. - * @export - * @interface UserApiGetProfileImageRequest - */ -export interface UserApiGetProfileImageRequest { - /** - * - * @type {string} - * @memberof UserApiGetProfileImage - */ - readonly id: string -} - -/** - * Request parameters for getUserById operation in UserApi. - * @export - * @interface UserApiGetUserByIdRequest - */ -export interface UserApiGetUserByIdRequest { - /** - * - * @type {string} - * @memberof UserApiGetUserById - */ - readonly id: string -} - -/** - * Request parameters for restoreUser operation in UserApi. - * @export - * @interface UserApiRestoreUserRequest - */ -export interface UserApiRestoreUserRequest { - /** - * - * @type {string} - * @memberof UserApiRestoreUser - */ - readonly id: string -} - -/** - * Request parameters for updateUser operation in UserApi. - * @export - * @interface UserApiUpdateUserRequest - */ -export interface UserApiUpdateUserRequest { - /** - * - * @type {UpdateUserDto} - * @memberof UserApiUpdateUser - */ - readonly updateUserDto: UpdateUserDto -} - -/** - * UserApi - object-oriented interface - * @export - * @class UserApi - * @extends {BaseAPI} - */ -export class UserApi extends BaseAPI { - /** - * - * @param {UserApiCreateProfileImageRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof UserApi - */ - public createProfileImage(requestParameters: UserApiCreateProfileImageRequest, options?: RawAxiosRequestConfig) { - return UserApiFp(this.configuration).createProfileImage(requestParameters.file, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {UserApiCreateUserRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof UserApi - */ - public createUser(requestParameters: UserApiCreateUserRequest, options?: RawAxiosRequestConfig) { - return UserApiFp(this.configuration).createUser(requestParameters.createUserDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof UserApi - */ - public deleteProfileImage(options?: RawAxiosRequestConfig) { - return UserApiFp(this.configuration).deleteProfileImage(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {UserApiDeleteUserRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof UserApi - */ - public deleteUser(requestParameters: UserApiDeleteUserRequest, options?: RawAxiosRequestConfig) { - return UserApiFp(this.configuration).deleteUser(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {UserApiGetAllUsersRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof UserApi - */ - public getAllUsers(requestParameters: UserApiGetAllUsersRequest, options?: RawAxiosRequestConfig) { - return UserApiFp(this.configuration).getAllUsers(requestParameters.isAll, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof UserApi - */ - public getMyUserInfo(options?: RawAxiosRequestConfig) { - return UserApiFp(this.configuration).getMyUserInfo(options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {UserApiGetProfileImageRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof UserApi - */ - public getProfileImage(requestParameters: UserApiGetProfileImageRequest, options?: RawAxiosRequestConfig) { - return UserApiFp(this.configuration).getProfileImage(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {UserApiGetUserByIdRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof UserApi - */ - public getUserById(requestParameters: UserApiGetUserByIdRequest, options?: RawAxiosRequestConfig) { - return UserApiFp(this.configuration).getUserById(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {UserApiRestoreUserRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof UserApi - */ - public restoreUser(requestParameters: UserApiRestoreUserRequest, options?: RawAxiosRequestConfig) { - return UserApiFp(this.configuration).restoreUser(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {UserApiUpdateUserRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof UserApi - */ - public updateUser(requestParameters: UserApiUpdateUserRequest, options?: RawAxiosRequestConfig) { - return UserApiFp(this.configuration).updateUser(requestParameters.updateUserDto, options).then((request) => request(this.axios, this.basePath)); - } -} - - - diff --git a/open-api/typescript-sdk/axios-client/base.ts b/open-api/typescript-sdk/axios-client/base.ts deleted file mode 100644 index fd917465ad..0000000000 --- a/open-api/typescript-sdk/axios-client/base.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -/** - * Immich - * Immich API - * - * The version of the OpenAPI document: 1.94.1 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -import type { Configuration } from './configuration'; -// Some imports not used depending on template conditions -// @ts-ignore -import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios'; -import globalAxios from 'axios'; - -export const BASE_PATH = "/api".replace(/\/+$/, ""); - -/** - * - * @export - */ -export const COLLECTION_FORMATS = { - csv: ",", - ssv: " ", - tsv: "\t", - pipes: "|", -}; - -/** - * - * @export - * @interface RequestArgs - */ -export interface RequestArgs { - url: string; - options: RawAxiosRequestConfig; -} - -/** - * - * @export - * @class BaseAPI - */ -export class BaseAPI { - protected configuration: Configuration | undefined; - - constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) { - if (configuration) { - this.configuration = configuration; - this.basePath = configuration.basePath ?? basePath; - } - } -}; - -/** - * - * @export - * @class RequiredError - * @extends {Error} - */ -export class RequiredError extends Error { - constructor(public field: string, msg?: string) { - super(msg); - this.name = "RequiredError" - } -} - -interface ServerMap { - [key: string]: { - url: string, - description: string, - }[]; -} - -/** - * - * @export - */ -export const operationServerMap: ServerMap = { -} diff --git a/open-api/typescript-sdk/axios-client/common.ts b/open-api/typescript-sdk/axios-client/common.ts deleted file mode 100644 index 0cbeb646b5..0000000000 --- a/open-api/typescript-sdk/axios-client/common.ts +++ /dev/null @@ -1,150 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -/** - * Immich - * Immich API - * - * The version of the OpenAPI document: 1.94.1 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -import type { Configuration } from "./configuration"; -import type { RequestArgs } from "./base"; -import type { AxiosInstance, AxiosResponse } from 'axios'; -import { RequiredError } from "./base"; - -/** - * - * @export - */ -export const DUMMY_BASE_URL = 'https://example.com' - -/** - * - * @throws {RequiredError} - * @export - */ -export const assertParamExists = function (functionName: string, paramName: string, paramValue: unknown) { - if (paramValue === null || paramValue === undefined) { - throw new RequiredError(paramName, `Required parameter ${paramName} was null or undefined when calling ${functionName}.`); - } -} - -/** - * - * @export - */ -export const setApiKeyToObject = async function (object: any, keyParamName: string, configuration?: Configuration) { - if (configuration && configuration.apiKey) { - const localVarApiKeyValue = typeof configuration.apiKey === 'function' - ? await configuration.apiKey(keyParamName) - : await configuration.apiKey; - object[keyParamName] = localVarApiKeyValue; - } -} - -/** - * - * @export - */ -export const setBasicAuthToObject = function (object: any, configuration?: Configuration) { - if (configuration && (configuration.username || configuration.password)) { - object["auth"] = { username: configuration.username, password: configuration.password }; - } -} - -/** - * - * @export - */ -export const setBearerAuthToObject = async function (object: any, configuration?: Configuration) { - if (configuration && configuration.accessToken) { - const accessToken = typeof configuration.accessToken === 'function' - ? await configuration.accessToken() - : await configuration.accessToken; - object["Authorization"] = "Bearer " + accessToken; - } -} - -/** - * - * @export - */ -export const setOAuthToObject = async function (object: any, name: string, scopes: string[], configuration?: Configuration) { - if (configuration && configuration.accessToken) { - const localVarAccessTokenValue = typeof configuration.accessToken === 'function' - ? await configuration.accessToken(name, scopes) - : await configuration.accessToken; - object["Authorization"] = "Bearer " + localVarAccessTokenValue; - } -} - -function setFlattenedQueryParams(urlSearchParams: URLSearchParams, parameter: any, key: string = ""): void { - if (parameter == null) return; - if (typeof parameter === "object") { - if (Array.isArray(parameter)) { - (parameter as any[]).forEach(item => setFlattenedQueryParams(urlSearchParams, item, key)); - } - else { - Object.keys(parameter).forEach(currentKey => - setFlattenedQueryParams(urlSearchParams, parameter[currentKey], `${key}${key !== '' ? '.' : ''}${currentKey}`) - ); - } - } - else { - if (urlSearchParams.has(key)) { - urlSearchParams.append(key, parameter); - } - else { - urlSearchParams.set(key, parameter); - } - } -} - -/** - * - * @export - */ -export const setSearchParams = function (url: URL, ...objects: any[]) { - const searchParams = new URLSearchParams(url.search); - setFlattenedQueryParams(searchParams, objects); - url.search = searchParams.toString(); -} - -/** - * - * @export - */ -export const serializeDataIfNeeded = function (value: any, requestOptions: any, configuration?: Configuration) { - const nonString = typeof value !== 'string'; - const needsSerialization = nonString && configuration && configuration.isJsonMime - ? configuration.isJsonMime(requestOptions.headers['Content-Type']) - : nonString; - return needsSerialization - ? JSON.stringify(value !== undefined ? value : {}) - : (value || ""); -} - -/** - * - * @export - */ -export const toPathString = function (url: URL) { - return url.pathname + url.search + url.hash -} - -/** - * - * @export - */ -export const createRequestFunction = function (axiosArgs: RequestArgs, globalAxios: AxiosInstance, BASE_PATH: string, configuration?: Configuration) { - return >(axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => { - const axiosRequestArgs = {...axiosArgs.options, url: (axios.defaults.baseURL ? '' : configuration?.basePath ?? basePath) + axiosArgs.url}; - return axios.request(axiosRequestArgs); - }; -} diff --git a/open-api/typescript-sdk/axios-client/configuration.ts b/open-api/typescript-sdk/axios-client/configuration.ts deleted file mode 100644 index 6988962347..0000000000 --- a/open-api/typescript-sdk/axios-client/configuration.ts +++ /dev/null @@ -1,110 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -/** - * Immich - * Immich API - * - * The version of the OpenAPI document: 1.94.1 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface ConfigurationParameters { - apiKey?: string | Promise | ((name: string) => string) | ((name: string) => Promise); - username?: string; - password?: string; - accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); - basePath?: string; - serverIndex?: number; - baseOptions?: any; - formDataCtor?: new () => any; -} - -export class Configuration { - /** - * parameter for apiKey security - * @param name security name - * @memberof Configuration - */ - apiKey?: string | Promise | ((name: string) => string) | ((name: string) => Promise); - /** - * parameter for basic security - * - * @type {string} - * @memberof Configuration - */ - username?: string; - /** - * parameter for basic security - * - * @type {string} - * @memberof Configuration - */ - password?: string; - /** - * parameter for oauth2 security - * @param name security name - * @param scopes oauth2 scope - * @memberof Configuration - */ - accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); - /** - * override base path - * - * @type {string} - * @memberof Configuration - */ - basePath?: string; - /** - * override server index - * - * @type {number} - * @memberof Configuration - */ - serverIndex?: number; - /** - * base options for axios calls - * - * @type {any} - * @memberof Configuration - */ - baseOptions?: any; - /** - * The FormData constructor that will be used to create multipart form data - * requests. You can inject this here so that execution environments that - * do not support the FormData class can still run the generated client. - * - * @type {new () => FormData} - */ - formDataCtor?: new () => any; - - constructor(param: ConfigurationParameters = {}) { - this.apiKey = param.apiKey; - this.username = param.username; - this.password = param.password; - this.accessToken = param.accessToken; - this.basePath = param.basePath; - this.serverIndex = param.serverIndex; - this.baseOptions = param.baseOptions; - this.formDataCtor = param.formDataCtor; - } - - /** - * Check if the given MIME is a JSON MIME. - * JSON MIME examples: - * application/json - * application/json; charset=UTF8 - * APPLICATION/JSON - * application/vnd.company+json - * @param mime - MIME (Multipurpose Internet Mail Extensions) - * @return True if the given MIME is JSON, false otherwise. - */ - public isJsonMime(mime: string): boolean { - const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i'); - return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json'); - } -} diff --git a/open-api/typescript-sdk/axios-client/git_push.sh b/open-api/typescript-sdk/axios-client/git_push.sh deleted file mode 100644 index f53a75d4fa..0000000000 --- a/open-api/typescript-sdk/axios-client/git_push.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/sh -# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ -# -# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com" - -git_user_id=$1 -git_repo_id=$2 -release_note=$3 -git_host=$4 - -if [ "$git_host" = "" ]; then - git_host="github.com" - echo "[INFO] No command line input provided. Set \$git_host to $git_host" -fi - -if [ "$git_user_id" = "" ]; then - git_user_id="GIT_USER_ID" - echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" -fi - -if [ "$git_repo_id" = "" ]; then - git_repo_id="GIT_REPO_ID" - echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" -fi - -if [ "$release_note" = "" ]; then - release_note="Minor update" - echo "[INFO] No command line input provided. Set \$release_note to $release_note" -fi - -# Initialize the local directory as a Git repository -git init - -# Adds the files in the local repository and stages them for commit. -git add . - -# Commits the tracked changes and prepares them to be pushed to a remote repository. -git commit -m "$release_note" - -# Sets the new remote -git_remote=$(git remote) -if [ "$git_remote" = "" ]; then # git remote not defined - - if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." - git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git - else - git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git - fi - -fi - -git pull origin master - -# Pushes (Forces) the changes in the local repository up to the remote repository -echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" -git push origin master 2>&1 | grep -v 'To https' diff --git a/open-api/typescript-sdk/axios-client/index.ts b/open-api/typescript-sdk/axios-client/index.ts deleted file mode 100644 index bd60bb4169..0000000000 --- a/open-api/typescript-sdk/axios-client/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -/** - * Immich - * Immich API - * - * The version of the OpenAPI document: 1.94.1 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export * from "./api"; -export * from "./configuration"; - diff --git a/open-api/typescript-sdk/axios.ts b/open-api/typescript-sdk/axios.ts deleted file mode 100644 index 9cda702043..0000000000 --- a/open-api/typescript-sdk/axios.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './axios-client'; -export * as base from './axios-client/base'; -export * as configuration from './axios-client/configuration'; -export * as common from './axios-client/common'; diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index 5346a47086..f231662d05 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -1,150 +1,34 @@ { "name": "@immich/sdk", - "version": "1.92.1", + "version": "1.97.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/sdk", - "version": "1.92.1", + "version": "1.97.0", "license": "GNU Affero General Public License version 3", "devDependencies": { "@oazapfts/runtime": "^1.0.0", "@types/node": "^20.11.0", "typescript": "^5.3.3" - }, - "peerDependencies": { - "axios": "^1.6.7" - }, - "peerDependenciesMeta": { - "axios": { - "optional": true - } } }, "node_modules/@oazapfts/runtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@oazapfts/runtime/-/runtime-1.0.0.tgz", - "integrity": "sha512-1ovqeaeEvShbYge5/7ctJokpvqB0anBdfDNfU5jWstjV2/Gbe+vvcBM274Z0abM3IM0b9MmSNWYBXnJXYO8KCw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@oazapfts/runtime/-/runtime-1.0.1.tgz", + "integrity": "sha512-CMl7f1gXYpjIyEtDhg4YfXwr2MXfbadbvqwKbMsaHkVtSglmuz5A8jSyefTqaJlmh0MOA2ZNS9jnbfIdtcoDiw==", "dev": true }, "node_modules/@types/node": { - "version": "20.11.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", - "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", + "version": "20.11.20", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.20.tgz", + "integrity": "sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==", "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "optional": true, - "peer": true - }, - "node_modules/axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", - "optional": true, - "peer": true, - "dependencies": { - "follow-redirects": "^1.15.4", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "optional": true, - "peer": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "optional": true, - "peer": true, - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "optional": true, - "peer": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "optional": true, - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "optional": true, - "peer": true, - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "optional": true, - "peer": true - }, "node_modules/typescript": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index 05e7843f9a..ea1b085405 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -1,35 +1,23 @@ { "name": "@immich/sdk", - "version": "1.92.1", + "version": "1.97.0", "description": "", "type": "module", - "main": "./build/fetch/index.js", - "types": "./build/fetch/index.d.ts", + "main": "./build/index.js", + "types": "./build/index.d.ts", "exports": { - "./axios": { - "types": "./build/axios/axios.d.ts", - "default": "./build/axios/axios.js" - }, ".": { - "types": "./build/fetch/fetch.d.ts", - "default": "./build/fetch/fetch.js" + "types": "./build/index.d.ts", + "default": "./build/index.js" } }, "scripts": { - "build": "tsc -b ./tsconfig.axios.json ./tsconfig.fetch.json" + "build": "tsc" }, "license": "GNU Affero General Public License version 3", "devDependencies": { "@oazapfts/runtime": "^1.0.0", "@types/node": "^20.11.0", "typescript": "^5.3.3" - }, - "peerDependencies": { - "axios": "^1.6.7" - }, - "peerDependenciesMeta": { - "axios": { - "optional": true - } } } diff --git a/open-api/typescript-sdk/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts similarity index 91% rename from open-api/typescript-sdk/fetch-client.ts rename to open-api/typescript-sdk/src/fetch-client.ts index 00b8c68326..6f4937e54c 100644 --- a/open-api/typescript-sdk/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -1,6 +1,6 @@ /** * Immich - * 1.94.1 + * 1.97.0 * DO NOT MODIFY - This file has been generated using oazapfts. * See https://www.npmjs.com/package/oazapfts */ @@ -14,9 +14,6 @@ const oazapfts = Oazapfts.runtime(defaults); export const servers = { server1: "/api" }; -export type ReactionLevel = "album" | "asset"; -export type ReactionType = "comment" | "like"; -export type UserAvatarColor = "primary" | "pink" | "red" | "yellow" | "blue" | "green" | "purple" | "orange" | "gray" | "amber"; export type UserDto = { avatarColor: UserAvatarColor; email: string; @@ -29,7 +26,7 @@ export type ActivityResponseDto = { comment?: string | null; createdAt: string; id: string; - "type": "comment" | "like"; + "type": Type; user: UserDto; }; export type ActivityCreateDto = { @@ -69,7 +66,6 @@ export type UserResponseDto = { createdAt: string; deletedAt: string | null; email: string; - externalPath: string | null; id: string; isAdmin: boolean; memoriesEnabled?: boolean; @@ -103,14 +99,12 @@ export type SmartInfoResponseDto = { objects?: string[] | null; tags?: string[] | null; }; -export type TagTypeEnum = "OBJECT" | "FACE" | "CUSTOM"; export type TagResponseDto = { id: string; name: string; "type": TagTypeEnum; userId: string; }; -export type AssetTypeEnum = "IMAGE" | "VIDEO" | "AUDIO" | "OTHER"; export type AssetResponseDto = { /** base64 encoded sha1 hash */ checksum: string; @@ -186,7 +180,7 @@ export type BulkIdsDto = { ids: string[]; }; export type BulkIdResponseDto = { - error?: "duplicate" | "no_permission" | "not_found" | "unknown"; + error?: Error; id: string; success: boolean; }; @@ -232,10 +226,10 @@ export type AssetBulkUploadCheckDto = { assets: AssetBulkUploadCheckItem[]; }; export type AssetBulkUploadCheckResult = { - action: "accept" | "reject"; + action: Action; assetId?: string; id: string; - reason?: "duplicate" | "unsupported-format"; + reason?: Reason; }; export type AssetBulkUploadCheckResponseDto = { results: AssetBulkUploadCheckResult[]; @@ -261,7 +255,6 @@ export type CheckExistingAssetsDto = { export type CheckExistingAssetsResponseDto = { existingIds: string[]; }; -export type AssetJobName = "regenerate-thumbnail" | "refresh-metadata" | "transcode-video"; export type AssetJobsDto = { assetIds: string[]; name: AssetJobName; @@ -284,8 +277,6 @@ export type AssetStatsResponseDto = { total: number; videos: number; }; -export type ThumbnailFormat = "JPEG" | "WEBP"; -export type TimeBucketSize = "DAY" | "MONTH"; export type TimeBucketResponseDto = { count: number; timeBucket: string; @@ -298,7 +289,6 @@ export type CreateAssetDto = { fileCreatedAt: string; fileModifiedAt: string; isArchived?: boolean; - isExternal?: boolean; isFavorite?: boolean; isOffline?: boolean; isReadOnly?: boolean; @@ -319,14 +309,10 @@ export type UpdateAssetDto = { latitude?: number; longitude?: number; }; -export type AssetOrder = "asc" | "desc"; -export type EntityType = "ASSET" | "ALBUM"; export type AuditDeletesResponseDto = { ids: string[]; needsFullSync: boolean; }; -export type PathEntityType = "asset" | "person" | "user"; -export type PathType = "original" | "jpeg_thumbnail" | "webp_thumbnail" | "encoded_video" | "sidecar" | "face" | "profile"; export type FileReportItemDto = { checksum?: string; entityId: string; @@ -452,13 +438,10 @@ export type AllJobStatusResponseDto = { thumbnailGeneration: JobStatusDto; videoConversion: JobStatusDto; }; -export type JobName = "thumbnailGeneration" | "metadataExtraction" | "videoConversion" | "faceDetection" | "facialRecognition" | "smartSearch" | "backgroundTask" | "storageTemplateMigration" | "migration" | "search" | "sidecar" | "library"; -export type JobCommand = "start" | "pause" | "resume" | "empty" | "clear-failed"; export type JobCommandDto = { command: JobCommand; force: boolean; }; -export type LibraryType = "UPLOAD" | "EXTERNAL"; export type LibraryResponseDto = { assetCount: number; createdAt: string; @@ -477,6 +460,7 @@ export type CreateLibraryDto = { isVisible?: boolean; isWatched?: boolean; name?: string; + ownerId?: string; "type": LibraryType; }; export type UpdateLibraryDto = { @@ -495,6 +479,18 @@ export type LibraryStatsResponseDto = { usage: number; videos: number; }; +export type ValidateLibraryDto = { + exclusionPatterns?: string[]; + importPaths?: string[]; +}; +export type ValidateLibraryImportPathResponseDto = { + importPath: string; + isValid?: boolean; + message?: string; +}; +export type ValidateLibraryResponseDto = { + importPaths?: ValidateLibraryImportPathResponseDto[]; +}; export type OAuthConfigDto = { redirectUri: string; }; @@ -509,7 +505,6 @@ export type PartnerResponseDto = { createdAt: string; deletedAt: string | null; email: string; - externalPath: string | null; id: string; inTimeline?: boolean; isAdmin: boolean; @@ -527,6 +522,7 @@ export type UpdatePartnerDto = { inTimeline: boolean; }; export type PeopleResponseDto = { + hidden: number; people: PersonResponseDto[]; total: number; }; @@ -603,7 +599,93 @@ export type SearchExploreResponseDto = { fieldName: string; items: SearchExploreItem[]; }; -export type SearchSuggestionType = "country" | "state" | "city" | "camera-make" | "camera-model"; +export type MetadataSearchDto = { + checksum?: string; + city?: string; + country?: string; + createdAfter?: string; + createdBefore?: string; + deviceAssetId?: string; + deviceId?: string; + encodedVideoPath?: string; + id?: string; + isArchived?: boolean; + isEncoded?: boolean; + isExternal?: boolean; + isFavorite?: boolean; + isMotion?: boolean; + isNotInAlbum?: boolean; + isOffline?: boolean; + isReadOnly?: boolean; + isVisible?: boolean; + lensModel?: string; + libraryId?: string; + make?: string; + model?: string; + order?: AssetOrder; + originalFileName?: string; + originalPath?: string; + page?: number; + personIds?: string[]; + resizePath?: string; + size?: number; + state?: string; + takenAfter?: string; + takenBefore?: string; + trashedAfter?: string; + trashedBefore?: string; + "type"?: AssetTypeEnum; + updatedAfter?: string; + updatedBefore?: string; + webpPath?: string; + withArchived?: boolean; + withDeleted?: boolean; + withExif?: boolean; + withPeople?: boolean; + withStacked?: boolean; +}; +export type PlacesResponseDto = { + admin1name?: string; + admin2name?: string; + latitude: number; + longitude: number; + name: string; +}; +export type SmartSearchDto = { + city?: string; + country?: string; + createdAfter?: string; + createdBefore?: string; + deviceId?: string; + isArchived?: boolean; + isEncoded?: boolean; + isExternal?: boolean; + isFavorite?: boolean; + isMotion?: boolean; + isNotInAlbum?: boolean; + isOffline?: boolean; + isReadOnly?: boolean; + isVisible?: boolean; + lensModel?: string; + libraryId?: string; + make?: string; + model?: string; + page?: number; + personIds?: string[]; + query: string; + size?: number; + state?: string; + takenAfter?: string; + takenBefore?: string; + trashedAfter?: string; + trashedBefore?: string; + "type"?: AssetTypeEnum; + updatedAfter?: string; + updatedBefore?: string; + withArchived?: boolean; + withDeleted?: boolean; + withExif?: boolean; +}; export type ServerInfoResponseDto = { diskAvailable: string; diskAvailableRaw: number; @@ -665,7 +747,6 @@ export type ServerVersionResponseDto = { minor: number; patch: number; }; -export type SharedLinkType = "ALBUM" | "INDIVIDUAL"; export type SharedLinkResponseDto = { album?: AlbumResponseDto; allowDownload: boolean; @@ -707,21 +788,15 @@ export type SharedLinkEditDto = { }; export type AssetIdsResponseDto = { assetId: string; - error?: "duplicate" | "no_permission" | "not_found"; + error?: Error2; success: boolean; }; -export type TranscodeHwAccel = "nvenc" | "qsv" | "vaapi" | "rkmpp" | "disabled"; -export type AudioCodec = "mp3" | "aac" | "libopus"; -export type VideoCodec = "h264" | "hevc" | "vp9"; -export type CqMode = "auto" | "cqp" | "icq"; -export type ToneMapping = "hable" | "mobius" | "reinhard" | "disabled"; -export type TranscodePolicy = "all" | "optimal" | "bitrate" | "required" | "disabled"; export type SystemConfigFFmpegDto = { - accel: TranscodeHwAccel; + accel: TranscodeHWAccel; acceptedAudioCodecs: AudioCodec[]; acceptedVideoCodecs: VideoCodec[]; bframes: number; - cqMode: CqMode; + cqMode: CQMode; crf: number; gopSize: number; maxBitrate: string; @@ -759,23 +834,18 @@ export type SystemConfigLibraryScanDto = { }; export type SystemConfigLibraryWatchDto = { enabled: boolean; - interval: number; - usePolling: boolean; }; export type SystemConfigLibraryDto = { scan: SystemConfigLibraryScanDto; watch: SystemConfigLibraryWatchDto; }; -export type LogLevel = "verbose" | "debug" | "log" | "warn" | "error" | "fatal"; export type SystemConfigLoggingDto = { enabled: boolean; level: LogLevel; }; -export type ClipMode = "vision" | "text"; -export type ModelType = "facial-recognition" | "clip"; export type ClipConfig = { enabled: boolean; - mode?: ClipMode; + mode?: CLIPMode; modelName: string; modelType?: ModelType; }; @@ -807,6 +877,7 @@ export type SystemConfigOAuthDto = { buttonText: string; clientId: string; clientSecret: string; + defaultStorageQuota: number; enabled: boolean; issuerUrl: string; mobileOverrideEnabled: boolean; @@ -814,6 +885,7 @@ export type SystemConfigOAuthDto = { scope: string; signingAlgorithm: string; storageLabelClaim: string; + storageQuotaClaim: string; }; export type SystemConfigPasswordLoginDto = { enabled: boolean; @@ -833,7 +905,6 @@ export type SystemConfigStorageTemplateDto = { export type SystemConfigThemeDto = { customCss: string; }; -export type Colorspace = "srgb" | "p3"; export type SystemConfigThumbnailDto = { colorspace: Colorspace; jpegSize: number; @@ -861,7 +932,6 @@ export type SystemConfigDto = { thumbnail: SystemConfigThumbnailDto; trash: SystemConfigTrashDto; }; -export type MapTheme = "light" | "dark"; export type SystemConfigTemplateStorageOptionDto = { dayOptions: string[]; hourOptions: string[]; @@ -881,7 +951,6 @@ export type UpdateTagDto = { }; export type CreateUserDto = { email: string; - externalPath?: string | null; memoriesEnabled?: boolean; name: string; password: string; @@ -891,7 +960,6 @@ export type CreateUserDto = { export type UpdateUserDto = { avatarColor?: UserAvatarColor; email?: string; - externalPath?: string; id: string; isAdmin?: boolean; memoriesEnabled?: boolean; @@ -1268,11 +1336,12 @@ export function runAssetJobs({ assetJobsDto }: { body: assetJobsDto }))); } -export function getMapMarkers({ fileCreatedAfter, fileCreatedBefore, isArchived, isFavorite }: { +export function getMapMarkers({ fileCreatedAfter, fileCreatedBefore, isArchived, isFavorite, withPartners }: { fileCreatedAfter?: string; fileCreatedBefore?: string; isArchived?: boolean; isFavorite?: boolean; + withPartners?: boolean; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -1281,7 +1350,8 @@ export function getMapMarkers({ fileCreatedAfter, fileCreatedBefore, isArchived, fileCreatedAfter, fileCreatedBefore, isArchived, - isFavorite + isFavorite, + withPartners }))}`, { ...opts })); @@ -1463,7 +1533,7 @@ export function updateAsset({ id, updateAssetDto }: { body: updateAssetDto }))); } -export function searchAssets({ checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, $type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked }: { +export function searchAssets({ checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isNotInAlbum, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, personIds, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, $type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked }: { checksum?: string; city?: string; country?: string; @@ -1478,6 +1548,7 @@ export function searchAssets({ checksum, city, country, createdAfter, createdBef isExternal?: boolean; isFavorite?: boolean; isMotion?: boolean; + isNotInAlbum?: boolean; isOffline?: boolean; isReadOnly?: boolean; isVisible?: boolean; @@ -1489,6 +1560,7 @@ export function searchAssets({ checksum, city, country, createdAfter, createdBef originalFileName?: string; originalPath?: string; page?: number; + personIds?: string[]; resizePath?: string; size?: number; state?: string; @@ -1524,6 +1596,7 @@ export function searchAssets({ checksum, city, country, createdAfter, createdBef isExternal, isFavorite, isMotion, + isNotInAlbum, isOffline, isReadOnly, isVisible, @@ -1535,6 +1608,7 @@ export function searchAssets({ checksum, city, country, createdAfter, createdBef originalFileName, originalPath, page, + personIds, resizePath, size, state, @@ -1766,11 +1840,15 @@ export function sendJobCommand({ id, jobCommandDto }: { body: jobCommandDto }))); } -export function getLibraries(opts?: Oazapfts.RequestOpts) { +export function getAllLibraries({ $type }: { + $type?: LibraryType; +}, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: LibraryResponseDto[]; - }>("/library", { + }>(`/library${QS.query(QS.explode({ + "type": $type + }))}`, { ...opts })); } @@ -1794,7 +1872,7 @@ export function deleteLibrary({ id }: { method: "DELETE" })); } -export function getLibraryInfo({ id }: { +export function getLibrary({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ @@ -1845,6 +1923,19 @@ export function getLibraryStatistics({ id }: { ...opts })); } +export function validate({ id, validateLibraryDto }: { + id: string; + validateLibraryDto: ValidateLibraryDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: ValidateLibraryResponseDto; + }>(`/library/${encodeURIComponent(id)}/validate`, oazapfts.json({ + ...opts, + method: "POST", + body: validateLibraryDto + }))); +} export function startOAuth({ oAuthConfigDto }: { oAuthConfigDto: OAuthConfigDto; }, opts?: Oazapfts.RequestOpts) { @@ -2089,97 +2180,17 @@ export function getExploreData(opts?: Oazapfts.RequestOpts) { ...opts })); } -export function searchMetadata({ checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, $type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked }: { - checksum?: string; - city?: string; - country?: string; - createdAfter?: string; - createdBefore?: string; - deviceAssetId?: string; - deviceId?: string; - encodedVideoPath?: string; - id?: string; - isArchived?: boolean; - isEncoded?: boolean; - isExternal?: boolean; - isFavorite?: boolean; - isMotion?: boolean; - isOffline?: boolean; - isReadOnly?: boolean; - isVisible?: boolean; - lensModel?: string; - libraryId?: string; - make?: string; - model?: string; - order?: AssetOrder; - originalFileName?: string; - originalPath?: string; - page?: number; - resizePath?: string; - size?: number; - state?: string; - takenAfter?: string; - takenBefore?: string; - trashedAfter?: string; - trashedBefore?: string; - $type?: AssetTypeEnum; - updatedAfter?: string; - updatedBefore?: string; - webpPath?: string; - withArchived?: boolean; - withDeleted?: boolean; - withExif?: boolean; - withPeople?: boolean; - withStacked?: boolean; +export function searchMetadata({ metadataSearchDto }: { + metadataSearchDto: MetadataSearchDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ - status: 200; + status: 201; data: SearchResponseDto; - }>(`/search/metadata${QS.query(QS.explode({ - checksum, - city, - country, - createdAfter, - createdBefore, - deviceAssetId, - deviceId, - encodedVideoPath, - id, - isArchived, - isEncoded, - isExternal, - isFavorite, - isMotion, - isOffline, - isReadOnly, - isVisible, - lensModel, - libraryId, - make, - model, - order, - originalFileName, - originalPath, - page, - resizePath, - size, - state, - takenAfter, - takenBefore, - trashedAfter, - trashedBefore, - "type": $type, - updatedAfter, - updatedBefore, - webpPath, - withArchived, - withDeleted, - withExif, - withPeople, - withStacked - }))}`, { - ...opts - })); + }>("/search/metadata", oazapfts.json({ + ...opts, + method: "POST", + body: metadataSearchDto + }))); } export function searchPerson({ name, withHidden }: { name: string; @@ -2195,78 +2206,30 @@ export function searchPerson({ name, withHidden }: { ...opts })); } -export function searchSmart({ city, country, createdAfter, createdBefore, deviceId, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, page, query, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, $type, updatedAfter, updatedBefore, withArchived, withDeleted, withExif }: { - city?: string; - country?: string; - createdAfter?: string; - createdBefore?: string; - deviceId?: string; - isArchived?: boolean; - isEncoded?: boolean; - isExternal?: boolean; - isFavorite?: boolean; - isMotion?: boolean; - isOffline?: boolean; - isReadOnly?: boolean; - isVisible?: boolean; - lensModel?: string; - libraryId?: string; - make?: string; - model?: string; - page?: number; - query: string; - size?: number; - state?: string; - takenAfter?: string; - takenBefore?: string; - trashedAfter?: string; - trashedBefore?: string; - $type?: AssetTypeEnum; - updatedAfter?: string; - updatedBefore?: string; - withArchived?: boolean; - withDeleted?: boolean; - withExif?: boolean; +export function searchPlaces({ name }: { + name: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; - data: SearchResponseDto; - }>(`/search/smart${QS.query(QS.explode({ - city, - country, - createdAfter, - createdBefore, - deviceId, - isArchived, - isEncoded, - isExternal, - isFavorite, - isMotion, - isOffline, - isReadOnly, - isVisible, - lensModel, - libraryId, - make, - model, - page, - query, - size, - state, - takenAfter, - takenBefore, - trashedAfter, - trashedBefore, - "type": $type, - updatedAfter, - updatedBefore, - withArchived, - withDeleted, - withExif + data: PlacesResponseDto[]; + }>(`/search/places${QS.query(QS.explode({ + name }))}`, { ...opts })); } +export function searchSmart({ smartSearchDto }: { + smartSearchDto: SmartSearchDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 201; + data: SearchResponseDto; + }>("/search/smart", oazapfts.json({ + ...opts, + method: "POST", + body: smartSearchDto + }))); +} export function getSearchSuggestions({ country, make, model, state, $type }: { country?: string; make?: string; @@ -2716,3 +2679,187 @@ export function restoreUser({ id }: { method: "POST" })); } +export enum ReactionLevel { + Album = "album", + Asset = "asset" +} +export enum ReactionType { + Comment = "comment", + Like = "like" +} +export enum Type { + Comment = "comment", + Like = "like" +} +export enum UserAvatarColor { + Primary = "primary", + Pink = "pink", + Red = "red", + Yellow = "yellow", + Blue = "blue", + Green = "green", + Purple = "purple", + Orange = "orange", + Gray = "gray", + Amber = "amber" +} +export enum TagTypeEnum { + Object = "OBJECT", + Face = "FACE", + Custom = "CUSTOM" +} +export enum AssetTypeEnum { + Image = "IMAGE", + Video = "VIDEO", + Audio = "AUDIO", + Other = "OTHER" +} +export enum Error { + Duplicate = "duplicate", + NoPermission = "no_permission", + NotFound = "not_found", + Unknown = "unknown" +} +export enum Action { + Accept = "accept", + Reject = "reject" +} +export enum Reason { + Duplicate = "duplicate", + UnsupportedFormat = "unsupported-format" +} +export enum AssetJobName { + RegenerateThumbnail = "regenerate-thumbnail", + RefreshMetadata = "refresh-metadata", + TranscodeVideo = "transcode-video" +} +export enum ThumbnailFormat { + Jpeg = "JPEG", + Webp = "WEBP" +} +export enum TimeBucketSize { + Day = "DAY", + Month = "MONTH" +} +export enum AssetOrder { + Asc = "asc", + Desc = "desc" +} +export enum EntityType { + Asset = "ASSET", + Album = "ALBUM" +} +export enum PathEntityType { + Asset = "asset", + Person = "person", + User = "user" +} +export enum PathType { + Original = "original", + JpegThumbnail = "jpeg_thumbnail", + WebpThumbnail = "webp_thumbnail", + EncodedVideo = "encoded_video", + Sidecar = "sidecar", + Face = "face", + Profile = "profile" +} +export enum JobName { + ThumbnailGeneration = "thumbnailGeneration", + MetadataExtraction = "metadataExtraction", + VideoConversion = "videoConversion", + FaceDetection = "faceDetection", + FacialRecognition = "facialRecognition", + SmartSearch = "smartSearch", + BackgroundTask = "backgroundTask", + StorageTemplateMigration = "storageTemplateMigration", + Migration = "migration", + Search = "search", + Sidecar = "sidecar", + Library = "library" +} +export enum JobCommand { + Start = "start", + Pause = "pause", + Resume = "resume", + Empty = "empty", + ClearFailed = "clear-failed" +} +export enum LibraryType { + Upload = "UPLOAD", + External = "EXTERNAL" +} +export enum SearchSuggestionType { + Country = "country", + State = "state", + City = "city", + CameraMake = "camera-make", + CameraModel = "camera-model" +} +export enum SharedLinkType { + Album = "ALBUM", + Individual = "INDIVIDUAL" +} +export enum Error2 { + Duplicate = "duplicate", + NoPermission = "no_permission", + NotFound = "not_found" +} +export enum TranscodeHWAccel { + Nvenc = "nvenc", + Qsv = "qsv", + Vaapi = "vaapi", + Rkmpp = "rkmpp", + Disabled = "disabled" +} +export enum AudioCodec { + Mp3 = "mp3", + Aac = "aac", + Libopus = "libopus" +} +export enum VideoCodec { + H264 = "h264", + Hevc = "hevc", + Vp9 = "vp9" +} +export enum CQMode { + Auto = "auto", + Cqp = "cqp", + Icq = "icq" +} +export enum ToneMapping { + Hable = "hable", + Mobius = "mobius", + Reinhard = "reinhard", + Disabled = "disabled" +} +export enum TranscodePolicy { + All = "all", + Optimal = "optimal", + Bitrate = "bitrate", + Required = "required", + Disabled = "disabled" +} +export enum LogLevel { + Verbose = "verbose", + Debug = "debug", + Log = "log", + Warn = "warn", + Error = "error", + Fatal = "fatal" +} +export enum CLIPMode { + Vision = "vision", + Text = "text" +} +export enum ModelType { + FacialRecognition = "facial-recognition", + Clip = "clip" +} +export enum Colorspace { + Srgb = "srgb", + P3 = "p3" +} +export enum MapTheme { + Light = "light", + Dark = "dark" +} diff --git a/open-api/typescript-sdk/src/fetch-errors.ts b/open-api/typescript-sdk/src/fetch-errors.ts new file mode 100644 index 0000000000..f21f0ed1c4 --- /dev/null +++ b/open-api/typescript-sdk/src/fetch-errors.ts @@ -0,0 +1,15 @@ +import { HttpError } from '@oazapfts/runtime'; + +export interface ApiExceptionResponse { + message: string; + error?: string; + statusCode: number; +} + +export interface ApiHttpError extends HttpError { + data: ApiExceptionResponse; +} + +export function isHttpError(error: unknown): error is ApiHttpError { + return error instanceof HttpError; +} diff --git a/open-api/typescript-sdk/fetch.ts b/open-api/typescript-sdk/src/index.ts similarity index 50% rename from open-api/typescript-sdk/fetch.ts rename to open-api/typescript-sdk/src/index.ts index 5441cd8268..5759e66ad9 100644 --- a/open-api/typescript-sdk/fetch.ts +++ b/open-api/typescript-sdk/src/index.ts @@ -1 +1,2 @@ export * from './fetch-client'; +export * from './fetch-errors'; diff --git a/open-api/typescript-sdk/tsconfig.axios.json b/open-api/typescript-sdk/tsconfig.axios.json deleted file mode 100644 index 9f0d06bbff..0000000000 --- a/open-api/typescript-sdk/tsconfig.axios.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "include": ["axios.ts", "axios-client/**/*"], - "compilerOptions": { - "target": "esnext", - "strict": true, - "skipLibCheck": true, - "declaration": true, - "outDir": "build/axios", - "module": "esnext", - "moduleResolution": "Bundler", - "lib": ["esnext"] - } -} diff --git a/open-api/typescript-sdk/tsconfig.fetch.json b/open-api/typescript-sdk/tsconfig.json similarity index 77% rename from open-api/typescript-sdk/tsconfig.fetch.json rename to open-api/typescript-sdk/tsconfig.json index 58ef1ffa6b..251202774e 100644 --- a/open-api/typescript-sdk/tsconfig.fetch.json +++ b/open-api/typescript-sdk/tsconfig.json @@ -1,13 +1,13 @@ { - "include": ["fetch.ts"], "compilerOptions": { "target": "esnext", "strict": true, "skipLibCheck": true, "declaration": true, - "outDir": "build/fetch", + "outDir": "build", "module": "esnext", "moduleResolution": "Bundler", "lib": ["esnext", "dom"] - } + }, + "include": ["src/**/*.ts"] } diff --git a/server/Dockerfile b/server/Dockerfile index eb62b8812a..0ebd5c44cb 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,5 +1,5 @@ # dev build -FROM ghcr.io/immich-app/base-server-dev:20240213@sha256:16646a37bae065b51e68cb2ba7a63027b29504d43a30644625382afbe326114a as dev +FROM ghcr.io/immich-app/base-server-dev:20240227@sha256:b1e212c106ce2318a587e0b2ef377215c958e877f61993ed9310534e4589cce4 as dev RUN apt-get install --no-install-recommends -yqq tini WORKDIR /usr/src/app @@ -24,7 +24,7 @@ RUN npm prune --omit=dev --omit=optional COPY --from=dev /usr/src/app/node_modules/@img ./node_modules/@img # web build -FROM node:iron-alpine3.18 as web +FROM node:iron-alpine3.18@sha256:a02826c7340c37a29179152723190bcc3044f933c925f3c2d78abb20f794de3f as web WORKDIR /usr/src/open-api/typescript-sdk COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./ @@ -40,7 +40,7 @@ RUN npm run build # prod build -FROM ghcr.io/immich-app/base-server-prod:20240213@sha256:61d159d069c5b522f16de9733fb79feb0e82c0b099d16f026196f344d12a1e5e +FROM ghcr.io/immich-app/base-server-prod:20240227@sha256:d47f5f7f2b6c53957c6353352b2fa24f2845da50e6491a7c74eb779ace10628c WORKDIR /usr/src/app ENV NODE_ENV=production \ @@ -59,4 +59,4 @@ COPY LICENSE /LICENSE ENV PATH="${PATH}:/usr/src/app/bin" VOLUME /usr/src/app/upload EXPOSE 3001 -ENTRYPOINT ["tini", "--", "/bin/sh"] +ENTRYPOINT ["tini", "--", "/bin/bash"] diff --git a/server/bin/immich b/server/bin/immich index 053e87313b..6b7dc3aa3f 100755 --- a/server/bin/immich +++ b/server/bin/immich @@ -1,2 +1,3 @@ #!/usr/bin/env bash + node /usr/src/app/node_modules/.bin/immich "$@" diff --git a/server/bin/immich-admin b/server/bin/immich-admin index 0634eae4bc..30fd33a20a 100755 --- a/server/bin/immich-admin +++ b/server/bin/immich-admin @@ -1,2 +1,3 @@ #!/usr/bin/env sh -/usr/src/app/start.sh immich-admin $1 + +/usr/src/app/start.sh immich-admin "$@" diff --git a/server/bin/immich-dev b/server/bin/immich-dev index fcf064bf79..177455d037 100755 --- a/server/bin/immich-dev +++ b/server/bin/immich-dev @@ -1,2 +1,3 @@ #!/usr/bin/env bash + node /usr/src/app/node_modules/.bin/nest start --debug "0.0.0.0:9230" --watch -- "$@" diff --git a/server/bin/immich-test b/server/bin/immich-test index 302f6765c4..93b104f136 100755 --- a/server/bin/immich-test +++ b/server/bin/immich-test @@ -1,2 +1,3 @@ #!/usr/bin/env bash -node /usr/src/app/node_modules/.bin/jest --config e2e/$1/jest-e2e.json --runInBand + +node /usr/src/app/node_modules/.bin/jest --config e2e/"$1"/jest-e2e.json --runInBand diff --git a/server/e2e/api/setup.ts b/server/e2e/api/setup.ts index 8d44b07cbb..88f2f598bd 100644 --- a/server/e2e/api/setup.ts +++ b/server/e2e/api/setup.ts @@ -1,6 +1,16 @@ import { PostgreSqlContainer } from '@testcontainers/postgresql'; +import path from 'node:path'; export default async () => { + let IMMICH_TEST_ASSET_PATH: string = ''; + + if (process.env.IMMICH_TEST_ASSET_PATH === undefined) { + IMMICH_TEST_ASSET_PATH = path.normalize(`${__dirname}/../../test/assets/`); + process.env.IMMICH_TEST_ASSET_PATH = IMMICH_TEST_ASSET_PATH; + } else { + IMMICH_TEST_ASSET_PATH = process.env.IMMICH_TEST_ASSET_PATH; + } + const pg = await new PostgreSqlContainer('tensorchord/pgvecto-rs:pg14-v0.2.0') .withDatabase('immich') .withUsername('postgres') @@ -11,6 +21,9 @@ export default async () => { process.env.DB_URL = pg.getConnectionUri(); process.env.NODE_ENV = 'development'; - process.env.LOG_LEVEL = 'fatal'; process.env.TZ = 'Z'; + + if (process.env.LOG_LEVEL === undefined) { + process.env.LOG_LEVEL = 'fatal'; + } }; diff --git a/server/e2e/api/specs/asset.e2e-spec.ts b/server/e2e/api/specs/asset.e2e-spec.ts index 5993a70400..7484187182 100644 --- a/server/e2e/api/specs/asset.e2e-spec.ts +++ b/server/e2e/api/specs/asset.e2e-spec.ts @@ -41,6 +41,7 @@ describe(`${AssetController.name} (e2e)`, () => { let app: INestApplication; let server: any; let assetRepository: IAssetRepository; + let admin: LoginResponseDto; let user1: LoginResponseDto; let user2: LoginResponseDto; let userWithQuota: LoginResponseDto; @@ -50,6 +51,7 @@ describe(`${AssetController.name} (e2e)`, () => { let asset3: AssetResponseDto; let asset4: AssetResponseDto; let asset5: AssetResponseDto; + let asset6: AssetResponseDto; const createAsset = async ( loginResponse: LoginResponseDto, @@ -71,7 +73,7 @@ describe(`${AssetController.name} (e2e)`, () => { await testApp.reset(); await api.authApi.adminSignUp(server); - const admin = await api.authApi.adminLogin(server); + admin = await api.authApi.adminLogin(server); await Promise.all([ api.userApi.create(server, admin.accessToken, userDto.user1), @@ -85,23 +87,17 @@ describe(`${AssetController.name} (e2e)`, () => { api.authApi.login(server, userDto.userWithQuota), ]); - const [user1Libraries, user2Libraries] = await Promise.all([ - api.libraryApi.getAll(server, user1.accessToken), - api.libraryApi.getAll(server, user2.accessToken), - ]); - - libraries = [...user1Libraries, ...user2Libraries]; + libraries = await api.libraryApi.getAll(server, admin.accessToken); }); beforeEach(async () => { await testApp.reset({ entities: [AssetEntity, AssetStackEntity] }); - [asset1, asset2, asset3, asset4, asset5] = await Promise.all([ + [asset1, asset2, asset3, asset4, asset5, asset6] = await Promise.all([ createAsset(user1, new Date('1970-01-01')), createAsset(user1, new Date('1970-02-10')), createAsset(user1, new Date('1970-02-11'), { isFavorite: true, - isArchived: true, isExternal: true, isReadOnly: true, type: AssetType.VIDEO, @@ -118,6 +114,9 @@ describe(`${AssetController.name} (e2e)`, () => { createAsset(user1, new Date('1970-01-01'), { deletedAt: yesterday.toJSDate(), }), + createAsset(user1, new Date('1970-02-11'), { + isArchived: true, + }), ]); await assetRepository.upsertExif({ @@ -275,14 +274,14 @@ describe(`${AssetController.name} (e2e)`, () => { should: 'should search by isArchived (true)', deferred: () => ({ query: { isArchived: true }, - assets: [asset3], + assets: [asset6], }), }, { should: 'should search by isArchived (false)', deferred: () => ({ query: { isArchived: false }, - assets: [asset2, asset1], + assets: [asset3, asset2, asset1], }), }, { @@ -313,6 +312,20 @@ describe(`${AssetController.name} (e2e)`, () => { assets: [asset3], }), }, + { + should: 'should search by withArchived (true)', + deferred: () => ({ + query: { withArchived: true }, + assets: [asset3, asset6, asset2, asset1], + }), + }, + { + should: 'should search by withArchived (false)', + deferred: () => ({ + query: { withArchived: false }, + assets: [asset3, asset2, asset1], + }), + }, { should: 'should search by createdBefore', deferred: () => ({ @@ -514,97 +527,13 @@ describe(`${AssetController.name} (e2e)`, () => { expect(status).toBe(200); expect(body.length).toBe(assets.length); - for (let i = 0; i < assets.length; i++) { - expect(body[i]).toEqual(expect.objectContaining({ id: assets[i].id })); + for (const [i, asset] of assets.entries()) { + expect(body[i]).toEqual(expect.objectContaining({ id: asset.id })); } }); } }); - describe('GET /asset/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).get(`/asset/${uuidStub.notFound}`); - expect(body).toEqual(errorStub.unauthorized); - expect(status).toBe(401); - }); - - it('should require a valid id', async () => { - const { status, body } = await request(server) - .get(`/asset/${uuidStub.invalid}`) - .set('Authorization', `Bearer ${user1.accessToken}`); - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest(['id must be a UUID'])); - }); - - it('should require access', async () => { - const { status, body } = await request(server) - .get(`/asset/${asset4.id}`) - .set('Authorization', `Bearer ${user1.accessToken}`); - expect(status).toBe(400); - expect(body).toEqual(errorStub.noPermission); - }); - - it('should get the asset info', async () => { - const { status, body } = await request(server) - .get(`/asset/${asset1.id}`) - .set('Authorization', `Bearer ${user1.accessToken}`); - expect(status).toBe(200); - expect(body).toMatchObject({ id: asset1.id }); - }); - - it('should work with a shared link', async () => { - const sharedLink = await api.sharedLinkApi.create(server, user1.accessToken, { - type: SharedLinkType.INDIVIDUAL, - assetIds: [asset1.id], - }); - - const { status, body } = await request(server).get(`/asset/${asset1.id}?key=${sharedLink.key}`); - expect(status).toBe(200); - expect(body).toMatchObject({ id: asset1.id }); - }); - - it('should not send people data for shared links for un-authenticated users', async () => { - const personRepository = app.get(IPersonRepository); - const person = await personRepository.create({ ownerId: asset1.ownerId, name: 'Test Person' }); - - await personRepository.createFaces([ - { - assetId: asset1.id, - personId: person.id, - embedding: Array.from({ length: 512 }, Math.random), - }, - ]); - - const { status, body } = await request(server) - .put(`/asset/${asset1.id}`) - .set('Authorization', `Bearer ${user1.accessToken}`) - .send({ isFavorite: true }); - expect(status).toEqual(200); - expect(body).toMatchObject({ - id: asset1.id, - isFavorite: true, - people: [ - { - birthDate: null, - id: expect.any(String), - isHidden: false, - name: 'Test Person', - thumbnailPath: '', - }, - ], - }); - - const sharedLink = await api.sharedLinkApi.create(server, user1.accessToken, { - type: SharedLinkType.INDIVIDUAL, - assetIds: [asset1.id], - }); - - const data = await request(server).get(`/asset/${asset1.id}?key=${sharedLink.key}`); - expect(data.status).toBe(200); - expect(data.body).toMatchObject({ people: [] }); - }); - }); - describe('POST /asset/upload', () => { it('should require authentication', async () => { const { status, body } = await request(server) @@ -682,7 +611,7 @@ describe(`${AssetController.name} (e2e)`, () => { it("should not upload to another user's library", async () => { const content = randomBytes(32); - const library = (await api.libraryApi.getAll(server, user2.accessToken))[0]; + const [library] = await api.libraryApi.getAll(server, admin.accessToken); await api.assetApi.upload(server, user1.accessToken, 'example-image', { content }); const { body, status } = await request(server) @@ -742,286 +671,6 @@ describe(`${AssetController.name} (e2e)`, () => { }); }); - describe('PUT /asset/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).put(`/asset/:${uuidStub.notFound}`); - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it('should require a valid id', async () => { - const { status, body } = await request(server) - .put(`/asset/${uuidStub.invalid}`) - .set('Authorization', `Bearer ${user1.accessToken}`); - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest(['id must be a UUID'])); - }); - - it('should require access', async () => { - const { status, body } = await request(server) - .put(`/asset/${asset4.id}`) - .set('Authorization', `Bearer ${user1.accessToken}`); - expect(status).toBe(400); - expect(body).toEqual(errorStub.noPermission); - }); - - it('should favorite an asset', async () => { - expect(asset1).toMatchObject({ isFavorite: false }); - - const { status, body } = await request(server) - .put(`/asset/${asset1.id}`) - .set('Authorization', `Bearer ${user1.accessToken}`) - .send({ isFavorite: true }); - expect(body).toMatchObject({ id: asset1.id, isFavorite: true }); - expect(status).toEqual(200); - }); - - it('should archive an asset', async () => { - expect(asset1).toMatchObject({ isArchived: false }); - - const { status, body } = await request(server) - .put(`/asset/${asset1.id}`) - .set('Authorization', `Bearer ${user1.accessToken}`) - .send({ isArchived: true }); - expect(body).toMatchObject({ id: asset1.id, isArchived: true }); - expect(status).toEqual(200); - }); - - it('should update date time original', async () => { - const { status, body } = await request(server) - .put(`/asset/${asset1.id}`) - .set('Authorization', `Bearer ${user1.accessToken}`) - .send({ dateTimeOriginal: '2023-11-19T18:11:00.000-07:00' }); - - expect(body).toMatchObject({ - id: asset1.id, - exifInfo: expect.objectContaining({ dateTimeOriginal: '2023-11-20T01:11:00.000Z' }), - }); - expect(status).toEqual(200); - }); - - it('should reject invalid gps coordinates', async () => { - for (const test of [ - { latitude: 12 }, - { longitude: 12 }, - { latitude: 12, longitude: 'abc' }, - { latitude: 'abc', longitude: 12 }, - { latitude: null, longitude: 12 }, - { latitude: 12, longitude: null }, - { latitude: 91, longitude: 12 }, - { latitude: -91, longitude: 12 }, - { latitude: 12, longitude: -181 }, - { latitude: 12, longitude: 181 }, - ]) { - const { status, body } = await request(server) - .put(`/asset/${asset1.id}`) - .send(test) - .set('Authorization', `Bearer ${user1.accessToken}`); - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest()); - } - }); - - it('should update gps data', async () => { - const { status, body } = await request(server) - .put(`/asset/${asset1.id}`) - .set('Authorization', `Bearer ${user1.accessToken}`) - .send({ latitude: 12, longitude: 12 }); - - expect(body).toMatchObject({ - id: asset1.id, - exifInfo: expect.objectContaining({ latitude: 12, longitude: 12 }), - }); - expect(status).toEqual(200); - }); - - it('should set the description', async () => { - const { status, body } = await request(server) - .put(`/asset/${asset1.id}`) - .set('Authorization', `Bearer ${user1.accessToken}`) - .send({ description: 'Test asset description' }); - expect(body).toMatchObject({ - id: asset1.id, - exifInfo: expect.objectContaining({ description: 'Test asset description' }), - }); - expect(status).toEqual(200); - }); - - it('should return tagged people', async () => { - const personRepository = app.get(IPersonRepository); - const person = await personRepository.create({ ownerId: asset1.ownerId, name: 'Test Person' }); - - await personRepository.createFaces([ - { - assetId: asset1.id, - personId: person.id, - embedding: Array.from({ length: 512 }, Math.random), - }, - ]); - - const { status, body } = await request(server) - .put(`/asset/${asset1.id}`) - .set('Authorization', `Bearer ${user1.accessToken}`) - .send({ isFavorite: true }); - expect(status).toEqual(200); - expect(body).toMatchObject({ - id: asset1.id, - isFavorite: true, - people: [ - { - birthDate: null, - id: expect.any(String), - isHidden: false, - name: 'Test Person', - thumbnailPath: '', - }, - ], - }); - }); - }); - - describe('GET /asset/statistics', () => { - beforeEach(async () => { - await api.assetApi.upload(server, user1.accessToken, 'favored_asset', { isFavorite: true }); - await api.assetApi.upload(server, user1.accessToken, 'archived_asset', { isArchived: true }); - await api.assetApi.upload(server, user1.accessToken, 'favored_archived_asset', { - isFavorite: true, - isArchived: true, - }); - }); - - it('should require authentication', async () => { - const { status, body } = await request(server).get('/asset/statistics'); - - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it('should return stats of all assets', async () => { - const { status, body } = await request(server) - .get('/asset/statistics') - .set('Authorization', `Bearer ${user1.accessToken}`); - - expect(body).toEqual({ images: 5, videos: 1, total: 6 }); - expect(status).toBe(200); - }); - - it('should return stats of all favored assets', async () => { - const { status, body } = await request(server) - .get('/asset/statistics') - .set('Authorization', `Bearer ${user1.accessToken}`) - .query({ isFavorite: true }); - - expect(status).toBe(200); - expect(body).toEqual({ images: 2, videos: 1, total: 3 }); - }); - - it('should return stats of all archived assets', async () => { - const { status, body } = await request(server) - .get('/asset/statistics') - .set('Authorization', `Bearer ${user1.accessToken}`) - .query({ isArchived: true }); - - expect(status).toBe(200); - expect(body).toEqual({ images: 2, videos: 1, total: 3 }); - }); - - it('should return stats of all favored and archived assets', async () => { - const { status, body } = await request(server) - .get('/asset/statistics') - .set('Authorization', `Bearer ${user1.accessToken}`) - .query({ isFavorite: true, isArchived: true }); - - expect(status).toBe(200); - expect(body).toEqual({ images: 1, videos: 1, total: 2 }); - }); - - it('should return stats of all assets neither favored nor archived', async () => { - const { status, body } = await request(server) - .get('/asset/statistics') - .set('Authorization', `Bearer ${user1.accessToken}`) - .query({ isFavorite: false, isArchived: false }); - - expect(status).toBe(200); - expect(body).toEqual({ images: 2, videos: 0, total: 2 }); - }); - }); - - describe('GET /asset/random', () => { - beforeAll(async () => { - await Promise.all([ - createAsset(user1, new Date('1970-02-01')), - createAsset(user1, new Date('1970-02-01')), - createAsset(user1, new Date('1970-02-01')), - createAsset(user1, new Date('1970-02-01')), - createAsset(user1, new Date('1970-02-01')), - createAsset(user1, new Date('1970-02-01')), - ]); - }); - it('should require authentication', async () => { - const { status, body } = await request(server).get('/asset/random'); - - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it.each(Array(10))('should return 1 random assets', async () => { - const { status, body } = await request(server) - .get('/asset/random') - .set('Authorization', `Bearer ${user1.accessToken}`); - - expect(status).toBe(200); - - const assets: AssetResponseDto[] = body; - expect(assets.length).toBe(1); - expect(assets[0].ownerId).toBe(user1.userId); - // - // assets owned by user2 - expect(assets[0].id).not.toBe(asset4.id); - // assets owned by user1 - expect([asset1.id, asset2.id, asset3.id]).toContain(assets[0].id); - }); - - it.each(Array(10))('should return 2 random assets', async () => { - const { status, body } = await request(server) - .get('/asset/random?count=2') - .set('Authorization', `Bearer ${user1.accessToken}`); - - expect(status).toBe(200); - - const assets: AssetResponseDto[] = body; - expect(assets.length).toBe(2); - - for (const asset of assets) { - expect(asset.ownerId).toBe(user1.userId); - // assets owned by user1 - expect([asset1.id, asset2.id, asset3.id]).toContain(asset.id); - // assets owned by user2 - expect(asset.id).not.toBe(asset4.id); - } - }); - - it.each(Array(10))( - 'should return 1 asset if there are 10 assets in the database but user 2 only has 1', - async () => { - const { status, body } = await request(server) - .get('/[]asset/random') - .set('Authorization', `Bearer ${user2.accessToken}`); - - expect(status).toBe(200); - expect(body).toEqual([expect.objectContaining({ id: asset4.id })]); - }, - ); - - it('should return error', async () => { - const { status } = await request(server) - .get('/asset/random?count=ABC') - .set('Authorization', `Bearer ${user1.accessToken}`); - - expect(status).toBe(400); - }); - }); - describe('GET /asset/time-buckets', () => { it('should require authentication', async () => { const { status, body } = await request(server).get('/asset/time-buckets').query({ size: TimeBucketSize.MONTH }); @@ -1041,7 +690,7 @@ describe(`${AssetController.name} (e2e)`, () => { expect.arrayContaining([ { count: 1, timeBucket: '2023-11-01T00:00:00.000Z' }, { count: 1, timeBucket: '1970-01-01T00:00:00.000Z' }, - { count: 1, timeBucket: '1970-02-01T00:00:00.000Z' }, + { count: 2, timeBucket: '1970-02-01T00:00:00.000Z' }, ]), ); }); @@ -1198,8 +847,13 @@ describe(`${AssetController.name} (e2e)`, () => { .set('Authorization', `Bearer ${user1.accessToken}`); expect(status).toBe(200); - expect(body).toHaveLength(1); - expect(body).toEqual(expect.arrayContaining([expect.objectContaining({ id: asset2.id })])); + expect(body).toHaveLength(2); + expect(body).toEqual( + expect.arrayContaining([ + expect.objectContaining({ id: asset2.id }), + expect.objectContaining({ id: asset3.id }), + ]), + ); }); it('should get all map markers', async () => { @@ -1209,8 +863,13 @@ describe(`${AssetController.name} (e2e)`, () => { .query({ isArchived: false }); expect(status).toBe(200); - expect(body).toHaveLength(1); - expect(body).toEqual([expect.objectContaining({ id: asset2.id })]); + expect(body).toHaveLength(2); + expect(body).toEqual( + expect.arrayContaining([ + expect.objectContaining({ id: asset2.id }), + expect.objectContaining({ id: asset3.id }), + ]), + ); }); }); diff --git a/server/e2e/api/specs/auth.e2e-spec.ts b/server/e2e/api/specs/auth.e2e-spec.ts deleted file mode 100644 index e514d2b803..0000000000 --- a/server/e2e/api/specs/auth.e2e-spec.ts +++ /dev/null @@ -1,294 +0,0 @@ -import { AuthController } from '@app/immich'; -import { - adminSignupStub, - changePasswordStub, - deviceStub, - errorStub, - loginResponseStub, - loginStub, - uuidStub, -} from '@test/fixtures'; -import request from 'supertest'; -import { api } from '../../client'; -import { testApp } from '../utils'; - -const name = 'Immich Admin'; -const password = 'Password123'; -const email = 'admin@immich.app'; - -const adminSignupResponse = { - avatarColor: expect.any(String), - id: expect.any(String), - name: 'Immich Admin', - email: 'admin@immich.app', - storageLabel: 'admin', - externalPath: null, - profileImagePath: '', - // why? lol - shouldChangePassword: true, - isAdmin: true, - createdAt: expect.any(String), - updatedAt: expect.any(String), - deletedAt: null, - oauthId: '', - memoriesEnabled: true, - quotaUsageInBytes: 0, - quotaSizeInBytes: null, -}; - -describe(`${AuthController.name} (e2e)`, () => { - let server: any; - let accessToken: string; - - beforeAll(async () => { - server = (await testApp.create()).getHttpServer(); - }); - - afterAll(async () => { - await testApp.teardown(); - }); - - beforeEach(async () => { - await testApp.reset(); - await api.authApi.adminSignUp(server); - const response = await api.authApi.adminLogin(server); - accessToken = response.accessToken; - }); - - describe('POST /auth/admin-sign-up', () => { - beforeEach(async () => { - await testApp.reset(); - }); - - const invalid = [ - { - should: 'require an email address', - data: { name, password }, - }, - { - should: 'require a password', - data: { name, email }, - }, - { - should: 'require a name', - data: { email, password }, - }, - { - should: 'require a valid email', - data: { name, email: 'immich', password }, - }, - ]; - - for (const { should, data } of invalid) { - it(`should ${should}`, async () => { - const { status, body } = await request(server).post('/auth/admin-sign-up').send(data); - expect(status).toEqual(400); - expect(body).toEqual(errorStub.badRequest()); - }); - } - - it(`should sign up the admin`, async () => { - await api.authApi.adminSignUp(server); - }); - - it('should sign up the admin with a local domain', async () => { - const { status, body } = await request(server) - .post('/auth/admin-sign-up') - .send({ ...adminSignupStub, email: 'admin@local' }); - expect(status).toEqual(201); - expect(body).toEqual({ ...adminSignupResponse, email: 'admin@local' }); - }); - - it('should transform email to lower case', async () => { - const { status, body } = await request(server) - .post('/auth/admin-sign-up') - .send({ ...adminSignupStub, email: 'aDmIn@IMMICH.app' }); - expect(status).toEqual(201); - expect(body).toEqual(adminSignupResponse); - }); - - it('should not allow a second admin to sign up', async () => { - await api.authApi.adminSignUp(server); - - const { status, body } = await request(server).post('/auth/admin-sign-up').send(adminSignupStub); - - expect(status).toBe(400); - expect(body).toEqual(errorStub.alreadyHasAdmin); - }); - - for (const key of Object.keys(adminSignupStub)) { - it(`should not allow null ${key}`, async () => { - const { status, body } = await request(server) - .post('/auth/admin-sign-up') - .send({ ...adminSignupStub, [key]: null }); - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest()); - }); - } - }); - - describe(`POST /auth/login`, () => { - it('should reject an incorrect password', async () => { - const { status, body } = await request(server).post('/auth/login').send({ email, password: 'incorrect' }); - expect(body).toEqual(errorStub.incorrectLogin); - expect(status).toBe(401); - }); - - for (const key of Object.keys(loginStub.admin)) { - it(`should not allow null ${key}`, async () => { - const { status, body } = await request(server) - .post('/auth/login') - .send({ ...loginStub.admin, [key]: null }); - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest()); - }); - } - - it('should accept a correct password', async () => { - const { status, body, headers } = await request(server).post('/auth/login').send({ email, password }); - expect(status).toBe(201); - expect(body).toEqual(loginResponseStub.admin.response); - - const token = body.accessToken; - expect(token).toBeDefined(); - - const cookies = headers['set-cookie']; - expect(cookies).toHaveLength(3); - expect(cookies[0]).toEqual(`immich_access_token=${token}; HttpOnly; Path=/; Max-Age=34560000; SameSite=Lax;`); - expect(cookies[1]).toEqual('immich_auth_type=password; HttpOnly; Path=/; Max-Age=34560000; SameSite=Lax;'); - expect(cookies[2]).toEqual('immich_is_authenticated=true; Path=/; Max-Age=34560000; SameSite=Lax;'); - }); - }); - - describe('GET /auth/devices', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).get('/auth/devices'); - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it('should get a list of authorized devices', async () => { - const { status, body } = await request(server).get('/auth/devices').set('Authorization', `Bearer ${accessToken}`); - expect(status).toBe(200); - expect(body).toEqual([deviceStub.current]); - }); - }); - - describe('DELETE /auth/devices', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).delete(`/auth/devices`); - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it('should logout all devices (except the current one)', async () => { - for (let i = 0; i < 5; i++) { - await api.authApi.adminLogin(server); - } - - await expect(api.authApi.getAuthDevices(server, accessToken)).resolves.toHaveLength(6); - - const { status } = await request(server).delete(`/auth/devices`).set('Authorization', `Bearer ${accessToken}`); - expect(status).toBe(204); - - await api.authApi.validateToken(server, accessToken); - }); - }); - - describe('DELETE /auth/devices/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).delete(`/auth/devices/${uuidStub.notFound}`); - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it('should throw an error for a non-existent device id', async () => { - const { status, body } = await request(server) - .delete(`/auth/devices/${uuidStub.notFound}`) - .set('Authorization', `Bearer ${accessToken}`); - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest('Not found or no authDevice.delete access')); - }); - - it('should logout a device', async () => { - const [device] = await api.authApi.getAuthDevices(server, accessToken); - const { status } = await request(server) - .delete(`/auth/devices/${device.id}`) - .set('Authorization', `Bearer ${accessToken}`); - expect(status).toBe(204); - - const response = await request(server).post('/auth/validateToken').set('Authorization', `Bearer ${accessToken}`); - expect(response.body).toEqual(errorStub.invalidToken); - expect(response.status).toBe(401); - }); - }); - - describe('POST /auth/validateToken', () => { - it('should reject an invalid token', async () => { - const { status, body } = await request(server).post(`/auth/validateToken`).set('Authorization', 'Bearer 123'); - expect(status).toBe(401); - expect(body).toEqual(errorStub.invalidToken); - }); - - it('should accept a valid token', async () => { - const { status, body } = await request(server) - .post(`/auth/validateToken`) - .send({}) - .set('Authorization', `Bearer ${accessToken}`); - expect(status).toBe(200); - expect(body).toEqual({ authStatus: true }); - }); - }); - - describe('POST /auth/change-password', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).post(`/auth/change-password`).send(changePasswordStub); - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - for (const key of Object.keys(changePasswordStub)) { - it(`should not allow null ${key}`, async () => { - const { status, body } = await request(server) - .post('/auth/change-password') - .send({ ...changePasswordStub, [key]: null }) - .set('Authorization', `Bearer ${accessToken}`); - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest()); - }); - } - - it('should require the current password', async () => { - const { status, body } = await request(server) - .post(`/auth/change-password`) - .send({ ...changePasswordStub, password: 'wrong-password' }) - .set('Authorization', `Bearer ${accessToken}`); - expect(status).toBe(400); - expect(body).toEqual(errorStub.wrongPassword); - }); - - it('should change the password', async () => { - const { status } = await request(server) - .post(`/auth/change-password`) - .send(changePasswordStub) - .set('Authorization', `Bearer ${accessToken}`); - expect(status).toBe(200); - - await api.authApi.login(server, { email: 'admin@immich.app', password: 'Password1234' }); - }); - }); - - describe('POST /auth/logout', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).post(`/auth/logout`); - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it('should logout the user', async () => { - const { status, body } = await request(server).post(`/auth/logout`).set('Authorization', `Bearer ${accessToken}`); - expect(status).toBe(200); - expect(body).toEqual({ successful: true, redirectUri: '/auth/login?autoLaunch=0' }); - }); - }); -}); diff --git a/server/e2e/api/specs/download.e2e-spec.ts b/server/e2e/api/specs/download.e2e-spec.ts deleted file mode 100644 index 9f8c477df9..0000000000 --- a/server/e2e/api/specs/download.e2e-spec.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { AssetResponseDto, IAssetRepository, LibraryResponseDto, LoginResponseDto, mapAsset } from '@app/domain'; -import { AssetController } from '@app/immich'; -import { AssetEntity } from '@app/infra/entities'; -import { INestApplication } from '@nestjs/common'; -import { errorStub, userDto } from '@test/fixtures'; -import request from 'supertest'; -import { api } from '../../client'; -import { generateAsset, testApp } from '../utils'; - -describe(`${AssetController.name} (e2e)`, () => { - let app: INestApplication; - let server: any; - let assetRepository: IAssetRepository; - let user1: LoginResponseDto; - let libraries: LibraryResponseDto[]; - let asset1: AssetResponseDto; - - const createAsset = async ( - loginResponse: LoginResponseDto, - fileCreatedAt: Date, - other: Partial = {}, - ) => { - const asset = await assetRepository.create( - generateAsset(loginResponse.userId, libraries, { fileCreatedAt, ...other }), - ); - - return mapAsset(asset); - }; - - beforeAll(async () => { - app = await testApp.create(); - server = app.getHttpServer(); - assetRepository = app.get(IAssetRepository); - - await testApp.reset(); - - await api.authApi.adminSignUp(server); - const admin = await api.authApi.adminLogin(server); - - await api.userApi.create(server, admin.accessToken, userDto.user1); - user1 = await api.authApi.login(server, userDto.user1); - libraries = await api.libraryApi.getAll(server, user1.accessToken); - asset1 = await createAsset(user1, new Date('1970-01-01')); - }); - - afterAll(async () => { - await testApp.teardown(); - }); - - describe('POST /download/info', () => { - it('should require authentication', async () => { - const { status, body } = await request(server) - .post(`/download/info`) - .send({ assetIds: [asset1.id] }); - - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it('should download info', async () => { - const { status, body } = await request(server) - .post('/download/info') - .set('Authorization', `Bearer ${user1.accessToken}`) - .send({ assetIds: [asset1.id] }); - - expect(status).toBe(201); - expect(body).toEqual(expect.objectContaining({ archives: [expect.objectContaining({ assetIds: [asset1.id] })] })); - }); - }); - - describe('POST /download/asset/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).post(`/download/asset/${asset1.id}`); - - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it('should download file', async () => { - const asset = await api.assetApi.upload(server, user1.accessToken, 'example'); - const response = await request(server) - .post(`/download/asset/${asset.id}`) - .set('Authorization', `Bearer ${user1.accessToken}`); - - expect(response.status).toBe(200); - expect(response.headers['content-type']).toEqual('image/jpeg'); - }); - }); -}); diff --git a/server/e2e/api/specs/library.e2e-spec.ts b/server/e2e/api/specs/library.e2e-spec.ts deleted file mode 100644 index 75c973466e..0000000000 --- a/server/e2e/api/specs/library.e2e-spec.ts +++ /dev/null @@ -1,438 +0,0 @@ -import { LibraryResponseDto, LoginResponseDto } from '@app/domain'; -import { LibraryController } from '@app/immich'; -import { LibraryType } from '@app/infra/entities'; -import { errorStub, userDto, uuidStub } from '@test/fixtures'; -import request from 'supertest'; -import { api } from '../../client'; -import { testApp } from '../utils'; - -describe(`${LibraryController.name} (e2e)`, () => { - let server: any; - let admin: LoginResponseDto; - - beforeAll(async () => { - const app = await testApp.create(); - server = app.getHttpServer(); - }); - - afterAll(async () => { - await testApp.teardown(); - }); - - beforeEach(async () => { - await testApp.reset(); - await api.authApi.adminSignUp(server); - admin = await api.authApi.adminLogin(server); - }); - - describe('GET /library', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).get('/library'); - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it('should start with a default upload library', async () => { - const { status, body } = await request(server) - .get('/library') - .set('Authorization', `Bearer ${admin.accessToken}`); - expect(status).toBe(200); - expect(body).toHaveLength(1); - expect(body).toEqual([ - expect.objectContaining({ - ownerId: admin.userId, - type: LibraryType.UPLOAD, - name: 'Default Library', - refreshedAt: null, - assetCount: 0, - importPaths: [], - exclusionPatterns: [], - }), - ]); - }); - }); - - describe('POST /library', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).post('/library').send({}); - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it('should create an external library with defaults', async () => { - const { status, body } = await request(server) - .post('/library') - .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ type: LibraryType.EXTERNAL }); - - expect(status).toBe(201); - expect(body).toEqual( - expect.objectContaining({ - ownerId: admin.userId, - type: LibraryType.EXTERNAL, - name: 'New External Library', - refreshedAt: null, - assetCount: 0, - importPaths: [], - exclusionPatterns: [], - }), - ); - }); - - it('should create an external library with options', async () => { - const { status, body } = await request(server) - .post('/library') - .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ - type: LibraryType.EXTERNAL, - name: 'My Awesome Library', - importPaths: ['/path/to/import'], - exclusionPatterns: ['**/Raw/**'], - }); - - expect(status).toBe(201); - expect(body).toEqual( - expect.objectContaining({ - name: 'My Awesome Library', - importPaths: ['/path/to/import'], - }), - ); - }); - - it('should not create an external library with duplicate import paths', async () => { - const { status, body } = await request(server) - .post('/library') - .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ - type: LibraryType.EXTERNAL, - name: 'My Awesome Library', - importPaths: ['/path', '/path'], - exclusionPatterns: ['**/Raw/**'], - }); - - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest(["All importPaths's elements must be unique"])); - }); - - it('should not create an external library with duplicate exclusion patterns', async () => { - const { status, body } = await request(server) - .post('/library') - .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ - type: LibraryType.EXTERNAL, - name: 'My Awesome Library', - importPaths: ['/path/to/import'], - exclusionPatterns: ['**/Raw/**', '**/Raw/**'], - }); - - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest(["All exclusionPatterns's elements must be unique"])); - }); - - it('should create an upload library with defaults', async () => { - const { status, body } = await request(server) - .post('/library') - .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ type: LibraryType.UPLOAD }); - - expect(status).toBe(201); - expect(body).toEqual( - expect.objectContaining({ - ownerId: admin.userId, - type: LibraryType.UPLOAD, - name: 'New Upload Library', - refreshedAt: null, - assetCount: 0, - importPaths: [], - exclusionPatterns: [], - }), - ); - }); - - it('should create an upload library with options', async () => { - const { status, body } = await request(server) - .post('/library') - .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ type: LibraryType.UPLOAD, name: 'My Awesome Library' }); - - expect(status).toBe(201); - expect(body).toEqual( - expect.objectContaining({ - name: 'My Awesome Library', - }), - ); - }); - - it('should not allow upload libraries to have import paths', async () => { - const { status, body } = await request(server) - .post('/library') - .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ type: LibraryType.UPLOAD, importPaths: ['/path/to/import'] }); - - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest('Upload libraries cannot have import paths')); - }); - - it('should not allow upload libraries to have exclusion patterns', async () => { - const { status, body } = await request(server) - .post('/library') - .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ type: LibraryType.UPLOAD, exclusionPatterns: ['**/Raw/**'] }); - - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest('Upload libraries cannot have exclusion patterns')); - }); - - it('should allow a non-admin to create a library', async () => { - await api.userApi.create(server, admin.accessToken, userDto.user1); - const user1 = await api.authApi.login(server, userDto.user1); - - const { status, body } = await request(server) - .post('/library') - .set('Authorization', `Bearer ${user1.accessToken}`) - .send({ type: LibraryType.EXTERNAL }); - - expect(status).toBe(201); - expect(body).toEqual( - expect.objectContaining({ - ownerId: user1.userId, - type: LibraryType.EXTERNAL, - name: 'New External Library', - refreshedAt: null, - assetCount: 0, - importPaths: [], - exclusionPatterns: [], - }), - ); - }); - }); - - describe('PUT /library/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).put(`/library/${uuidStub.notFound}`).send({}); - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - describe('external library', () => { - let library: LibraryResponseDto; - - beforeEach(async () => { - // Create an external library with default settings - library = await api.libraryApi.create(server, admin.accessToken, { type: LibraryType.EXTERNAL }); - }); - - it('should change the library name', async () => { - const { status, body } = await request(server) - .put(`/library/${library.id}`) - .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ name: 'New Library Name' }); - - expect(status).toBe(200); - expect(body).toEqual( - expect.objectContaining({ - name: 'New Library Name', - }), - ); - }); - - it('should not set an empty name', async () => { - const { status, body } = await request(server) - .put(`/library/${library.id}`) - .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ name: '' }); - - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest(['name should not be empty'])); - }); - - it('should change the import paths', async () => { - const { status, body } = await request(server) - .put(`/library/${library.id}`) - .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ importPaths: ['/path/to/import'] }); - - expect(status).toBe(200); - expect(body).toEqual( - expect.objectContaining({ - importPaths: ['/path/to/import'], - }), - ); - }); - - it('should reject an empty import path', async () => { - const { status, body } = await request(server) - .put(`/library/${library.id}`) - .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ importPaths: [''] }); - - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest(['each value in importPaths should not be empty'])); - }); - - it('should reject duplicate import paths', async () => { - const { status, body } = await request(server) - .put(`/library/${library.id}`) - .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ importPaths: ['/path', '/path'] }); - - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest(["All importPaths's elements must be unique"])); - }); - - it('should change the exclusion pattern', async () => { - const { status, body } = await request(server) - .put(`/library/${library.id}`) - .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ exclusionPatterns: ['**/Raw/**'] }); - - expect(status).toBe(200); - expect(body).toEqual( - expect.objectContaining({ - exclusionPatterns: ['**/Raw/**'], - }), - ); - }); - - it('should reject duplicate exclusion patterns', async () => { - const { status, body } = await request(server) - .put(`/library/${library.id}`) - .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ exclusionPatterns: ['**/*.jpg', '**/*.jpg'] }); - - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest(["All exclusionPatterns's elements must be unique"])); - }); - - it('should reject an empty exclusion pattern', async () => { - const { status, body } = await request(server) - .put(`/library/${library.id}`) - .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ exclusionPatterns: [''] }); - - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest(['each value in exclusionPatterns should not be empty'])); - }); - }); - }); - - describe('GET /library/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).get(`/library/${uuidStub.notFound}`); - - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it('should get library by id', async () => { - const library = await api.libraryApi.create(server, admin.accessToken, { type: LibraryType.EXTERNAL }); - - const { status, body } = await request(server) - .get(`/library/${library.id}`) - .set('Authorization', `Bearer ${admin.accessToken}`); - - expect(status).toBe(200); - expect(body).toEqual( - expect.objectContaining({ - ownerId: admin.userId, - type: LibraryType.EXTERNAL, - name: 'New External Library', - refreshedAt: null, - assetCount: 0, - importPaths: [], - exclusionPatterns: [], - }), - ); - }); - - it("should not allow getting another user's library", async () => { - await Promise.all([ - api.userApi.create(server, admin.accessToken, userDto.user1), - api.userApi.create(server, admin.accessToken, userDto.user2), - ]); - - const [user1, user2] = await Promise.all([ - api.authApi.login(server, userDto.user1), - api.authApi.login(server, userDto.user2), - ]); - - const library = await api.libraryApi.create(server, user1.accessToken, { type: LibraryType.EXTERNAL }); - - const { status, body } = await request(server) - .get(`/library/${library.id}`) - .set('Authorization', `Bearer ${user2.accessToken}`); - - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest('Not found or no library.read access')); - }); - }); - - describe('DELETE /library/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).delete(`/library/${uuidStub.notFound}`); - - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it('should not delete the last upload library', async () => { - const [defaultLibrary] = await api.libraryApi.getAll(server, admin.accessToken); - expect(defaultLibrary).toBeDefined(); - - const { status, body } = await request(server) - .delete(`/library/${defaultLibrary.id}`) - .set('Authorization', `Bearer ${admin.accessToken}`); - - expect(status).toBe(400); - expect(body).toEqual(errorStub.noDeleteUploadLibrary); - }); - - it('should delete an empty library', async () => { - const library = await api.libraryApi.create(server, admin.accessToken, { type: LibraryType.EXTERNAL }); - - const { status, body } = await request(server) - .delete(`/library/${library.id}`) - .set('Authorization', `Bearer ${admin.accessToken}`); - - expect(status).toBe(200); - expect(body).toEqual({}); - - const libraries = await api.libraryApi.getAll(server, admin.accessToken); - expect(libraries).toHaveLength(1); - expect(libraries).not.toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: library.id, - }), - ]), - ); - }); - }); - - describe('GET /library/:id/statistics', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).get(`/library/${uuidStub.notFound}/statistics`); - - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - }); - - describe('POST /library/:id/scan', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).post(`/library/${uuidStub.notFound}/scan`).send({}); - - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - }); - - describe('POST /library/:id/removeOffline', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).post(`/library/${uuidStub.notFound}/removeOffline`).send({}); - - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - }); -}); diff --git a/server/e2e/api/specs/oauth.e2e-spec.ts b/server/e2e/api/specs/oauth.e2e-spec.ts deleted file mode 100644 index b6136971d0..0000000000 --- a/server/e2e/api/specs/oauth.e2e-spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { OAuthController } from '@app/immich'; -import { errorStub } from '@test/fixtures'; -import request from 'supertest'; -import { api } from '../../client'; -import { testApp } from '../utils'; - -describe(`${OAuthController.name} (e2e)`, () => { - let server: any; - - beforeAll(async () => { - server = (await testApp.create()).getHttpServer(); - }); - - afterAll(async () => { - await testApp.teardown(); - }); - - beforeEach(async () => { - await testApp.reset(); - await api.authApi.adminSignUp(server); - }); - - describe('POST /oauth/authorize', () => { - it(`should throw an error if a redirect uri is not provided`, async () => { - const { status, body } = await request(server).post('/oauth/authorize').send({}); - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest(['redirectUri must be a string', 'redirectUri should not be empty'])); - }); - }); -}); diff --git a/server/e2e/api/specs/person.e2e-spec.ts b/server/e2e/api/specs/person.e2e-spec.ts deleted file mode 100644 index 73adcfab71..0000000000 --- a/server/e2e/api/specs/person.e2e-spec.ts +++ /dev/null @@ -1,191 +0,0 @@ -import { IPersonRepository, LoginResponseDto } from '@app/domain'; -import { PersonController } from '@app/immich'; -import { PersonEntity } from '@app/infra/entities'; -import { INestApplication } from '@nestjs/common'; -import { errorStub, uuidStub } from '@test/fixtures'; -import request from 'supertest'; -import { api } from '../../client'; -import { testApp } from '../utils'; - -describe(`${PersonController.name}`, () => { - let app: INestApplication; - let server: any; - let loginResponse: LoginResponseDto; - let accessToken: string; - let personRepository: IPersonRepository; - let visiblePerson: PersonEntity; - let hiddenPerson: PersonEntity; - - beforeAll(async () => { - app = await testApp.create(); - server = app.getHttpServer(); - personRepository = app.get(IPersonRepository); - }); - - afterAll(async () => { - await testApp.teardown(); - }); - - beforeEach(async () => { - await testApp.reset(); - await api.authApi.adminSignUp(server); - loginResponse = await api.authApi.adminLogin(server); - accessToken = loginResponse.accessToken; - - const faceAsset = await api.assetApi.upload(server, accessToken, 'face_asset'); - visiblePerson = await personRepository.create({ - ownerId: loginResponse.userId, - name: 'visible_person', - thumbnailPath: '/thumbnail/face_asset', - }); - await personRepository.createFaces([ - { - assetId: faceAsset.id, - personId: visiblePerson.id, - embedding: Array.from({ length: 512 }, Math.random), - }, - ]); - - hiddenPerson = await personRepository.create({ - ownerId: loginResponse.userId, - name: 'hidden_person', - isHidden: true, - thumbnailPath: '/thumbnail/face_asset', - }); - await personRepository.createFaces([ - { - assetId: faceAsset.id, - personId: hiddenPerson.id, - embedding: Array.from({ length: 512 }, Math.random), - }, - ]); - }); - - describe('GET /person', () => { - beforeEach(async () => {}); - - it('should require authentication', async () => { - const { status, body } = await request(server).get('/person'); - - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it('should return all people (including hidden)', async () => { - const { status, body } = await request(server) - .get('/person') - .set('Authorization', `Bearer ${accessToken}`) - .query({ withHidden: true }); - - expect(status).toBe(200); - expect(body).toEqual({ - total: 2, - people: [ - expect.objectContaining({ name: 'visible_person' }), - expect.objectContaining({ name: 'hidden_person' }), - ], - }); - }); - - it('should return only visible people', async () => { - const { status, body } = await request(server).get('/person').set('Authorization', `Bearer ${accessToken}`); - - expect(status).toBe(200); - expect(body).toEqual({ - total: 2, - people: [expect.objectContaining({ name: 'visible_person' })], - }); - }); - }); - - describe('GET /person/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).get(`/person/${uuidStub.notFound}`); - - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it('should throw error if person with id does not exist', async () => { - const { status, body } = await request(server) - .get(`/person/${uuidStub.notFound}`) - .set('Authorization', `Bearer ${accessToken}`); - - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest()); - }); - - it('should return person information', async () => { - const { status, body } = await request(server) - .get(`/person/${visiblePerson.id}`) - .set('Authorization', `Bearer ${accessToken}`); - - expect(status).toBe(200); - expect(body).toEqual(expect.objectContaining({ id: visiblePerson.id })); - }); - }); - - describe('PUT /person/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).put(`/person/${uuidStub.notFound}`); - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - for (const { key, type } of [ - { key: 'name', type: 'string' }, - { key: 'featureFaceAssetId', type: 'string' }, - { key: 'isHidden', type: 'boolean value' }, - ]) { - it(`should not allow null ${key}`, async () => { - const { status, body } = await request(server) - .put(`/person/${visiblePerson.id}`) - .set('Authorization', `Bearer ${accessToken}`) - .send({ [key]: null }); - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest([`${key} must be a ${type}`])); - }); - } - - it('should not accept invalid birth dates', async () => { - for (const { birthDate, response } of [ - { birthDate: false, response: 'Not found or no person.write access' }, - { birthDate: 'false', response: ['birthDate must be a Date instance'] }, - { birthDate: '123567', response: 'Not found or no person.write access' }, - { birthDate: 123567, response: 'Not found or no person.write access' }, - ]) { - const { status, body } = await request(server) - .put(`/person/${uuidStub.notFound}`) - .set('Authorization', `Bearer ${accessToken}`) - .send({ birthDate }); - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest(response)); - } - }); - - it('should update a date of birth', async () => { - const { status, body } = await request(server) - .put(`/person/${visiblePerson.id}`) - .set('Authorization', `Bearer ${accessToken}`) - .send({ birthDate: '1990-01-01T05:00:00.000Z' }); - expect(status).toBe(200); - expect(body).toMatchObject({ birthDate: '1990-01-01' }); - }); - - it('should clear a date of birth', async () => { - const person = await personRepository.create({ - birthDate: new Date('1990-01-01'), - ownerId: loginResponse.userId, - }); - - expect(person.birthDate).toBeDefined(); - - const { status, body } = await request(server) - .put(`/person/${person.id}`) - .set('Authorization', `Bearer ${accessToken}`) - .send({ birthDate: null }); - expect(status).toBe(200); - expect(body).toMatchObject({ birthDate: null }); - }); - }); -}); diff --git a/server/e2e/api/specs/search.e2e-spec.ts b/server/e2e/api/specs/search.e2e-spec.ts index 74988396d7..0e5cc428cc 100644 --- a/server/e2e/api/specs/search.e2e-spec.ts +++ b/server/e2e/api/specs/search.e2e-spec.ts @@ -44,7 +44,7 @@ describe(`${SearchController.name}`, () => { describe('GET /search (exif)', () => { beforeEach(async () => { - const assetId = (await assetRepository.create(generateAsset(loginResponse.userId, libraries))).id; + const { id: assetId } = await assetRepository.create(generateAsset(loginResponse.userId, libraries)); await assetRepository.upsertExif({ assetId, ...searchStub.exif }); const assetWithMetadata = await assetRepository.getById(assetId, { exifInfo: true }); @@ -166,7 +166,7 @@ describe(`${SearchController.name}`, () => { describe('GET /search (smart info)', () => { beforeEach(async () => { - const assetId = (await assetRepository.create(generateAsset(loginResponse.userId, libraries))).id; + const { id: assetId } = await assetRepository.create(generateAsset(loginResponse.userId, libraries)); await assetRepository.upsertExif({ assetId, ...searchStub.exif }); await smartInfoRepository.upsert({ assetId, ...searchStub.smartInfo }, Array.from({ length: 512 }, Math.random)); @@ -215,7 +215,7 @@ describe(`${SearchController.name}`, () => { describe('GET /search (file name)', () => { beforeEach(async () => { - const assetId = (await assetRepository.create(generateAsset(loginResponse.userId, libraries))).id; + const { id: assetId } = await assetRepository.create(generateAsset(loginResponse.userId, libraries)); await assetRepository.upsertExif({ assetId, ...searchStub.exif }); const assetWithMetadata = await assetRepository.getById(assetId, { exifInfo: true }); diff --git a/server/e2e/api/specs/user.e2e-spec.ts b/server/e2e/api/specs/user.e2e-spec.ts deleted file mode 100644 index cd7c8147a3..0000000000 --- a/server/e2e/api/specs/user.e2e-spec.ts +++ /dev/null @@ -1,299 +0,0 @@ -import { LoginResponseDto, UserResponseDto, UserService } from '@app/domain'; -import { AppModule, UserController } from '@app/immich'; -import { UserEntity } from '@app/infra/entities'; -import { INestApplication } from '@nestjs/common'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { errorStub, userDto, userSignupStub, userStub } from '@test/fixtures'; -import request from 'supertest'; -import { Repository } from 'typeorm'; -import { api } from '../../client'; -import { testApp } from '../utils'; - -describe(`${UserController.name}`, () => { - let app: INestApplication; - let server: any; - let loginResponse: LoginResponseDto; - let accessToken: string; - let userService: UserService; - let userRepository: Repository; - - beforeAll(async () => { - app = await testApp.create(); - server = app.getHttpServer(); - userRepository = app.select(AppModule).get(getRepositoryToken(UserEntity)); - }); - - afterAll(async () => { - await testApp.teardown(); - }); - - beforeEach(async () => { - await testApp.reset(); - await api.authApi.adminSignUp(server); - loginResponse = await api.authApi.adminLogin(server); - accessToken = loginResponse.accessToken; - - userService = app.get(UserService); - }); - - describe('GET /user', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).get('/user'); - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it('should start with the admin', async () => { - const { status, body } = await request(server).get('/user').set('Authorization', `Bearer ${accessToken}`); - expect(status).toEqual(200); - expect(body).toHaveLength(1); - expect(body[0]).toMatchObject({ email: 'admin@immich.app' }); - }); - - it('should hide deleted users', async () => { - const user1 = await api.userApi.create(server, accessToken, { - email: `user1@immich.app`, - password: 'Password123', - name: `User 1`, - }); - - await api.userApi.delete(server, accessToken, user1.id); - - const { status, body } = await request(server) - .get(`/user`) - .query({ isAll: true }) - .set('Authorization', `Bearer ${accessToken}`); - expect(status).toBe(200); - expect(body).toHaveLength(1); - expect(body[0]).toMatchObject({ email: 'admin@immich.app' }); - }); - - it('should include deleted users', async () => { - const user1 = await api.userApi.create(server, accessToken, userDto.user1); - - await api.userApi.delete(server, accessToken, user1.id); - - const { status, body } = await request(server) - .get(`/user`) - .query({ isAll: false }) - .set('Authorization', `Bearer ${accessToken}`); - - expect(status).toBe(200); - expect(body).toHaveLength(2); - expect(body[0]).toMatchObject({ id: user1.id, email: 'user1@immich.app', deletedAt: expect.any(String) }); - expect(body[1]).toMatchObject({ id: loginResponse.userId, email: 'admin@immich.app' }); - }); - }); - - describe('GET /user/info/:id', () => { - it('should require authentication', async () => { - const { status } = await request(server).get(`/user/info/${loginResponse.userId}`); - expect(status).toEqual(401); - }); - - it('should get the user info', async () => { - const { status, body } = await request(server) - .get(`/user/info/${loginResponse.userId}`) - .set('Authorization', `Bearer ${accessToken}`); - expect(status).toBe(200); - expect(body).toMatchObject({ id: loginResponse.userId, email: 'admin@immich.app' }); - }); - }); - - describe('GET /user/me', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).get(`/user/me`); - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it('should get my info', async () => { - const { status, body } = await request(server).get(`/user/me`).set('Authorization', `Bearer ${accessToken}`); - expect(status).toBe(200); - expect(body).toMatchObject({ id: loginResponse.userId, email: 'admin@immich.app' }); - }); - }); - - describe('POST /user', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).post(`/user`).send(userSignupStub); - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - for (const key of Object.keys(userSignupStub)) { - it(`should not allow null ${key}`, async () => { - const { status, body } = await request(server) - .post(`/user`) - .set('Authorization', `Bearer ${accessToken}`) - .send({ ...userSignupStub, [key]: null }); - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest()); - }); - } - - it('should ignore `isAdmin`', async () => { - const { status, body } = await request(server) - .post(`/user`) - .send({ - isAdmin: true, - email: 'user1@immich.app', - password: 'Password123', - name: 'Immich', - }) - .set('Authorization', `Bearer ${accessToken}`); - expect(body).toMatchObject({ - email: 'user1@immich.app', - isAdmin: false, - shouldChangePassword: true, - }); - expect(status).toBe(201); - }); - - it('should create a user without memories enabled', async () => { - const { status, body } = await request(server) - .post(`/user`) - .send({ - email: 'no-memories@immich.app', - password: 'Password123', - name: 'No Memories', - memoriesEnabled: false, - }) - .set('Authorization', `Bearer ${accessToken}`); - expect(body).toMatchObject({ - email: 'no-memories@immich.app', - memoriesEnabled: false, - }); - expect(status).toBe(201); - }); - }); - - describe('DELETE /user/:id', () => { - let userToDelete: UserResponseDto; - - beforeEach(async () => { - userToDelete = await api.userApi.create(server, accessToken, { - email: userStub.user1.email, - name: userStub.user1.name, - password: 'superSecurePassword', - }); - }); - - it('should require authentication', async () => { - const { status, body } = await request(server).delete(`/user/${userToDelete.id}`); - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it('should delete user', async () => { - const deleteRequest = await request(server) - .delete(`/user/${userToDelete.id}`) - .set('Authorization', `Bearer ${accessToken}`); - - expect(deleteRequest.status).toBe(200); - expect(deleteRequest.body).toEqual({ - ...userToDelete, - updatedAt: expect.any(String), - deletedAt: expect.any(String), - }); - - await userRepository.save({ id: deleteRequest.body.id, deletedAt: new Date('1970-01-01').toISOString() }); - - await userService.handleUserDelete({ id: userToDelete.id }); - - const { status, body } = await request(server) - .get('/user') - .query({ isAll: false }) - .set('Authorization', `Bearer ${accessToken}`); - - expect(status).toBe(200); - expect(body).toHaveLength(1); - }); - }); - - describe('PUT /user', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).put(`/user`); - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - for (const key of Object.keys(userStub.admin)) { - it(`should not allow null ${key}`, async () => { - const { status, body } = await request(server) - .put(`/user`) - .set('Authorization', `Bearer ${accessToken}`) - .send({ ...userStub.admin, [key]: null }); - expect(status).toBe(400); - expect(body).toEqual(errorStub.badRequest()); - }); - } - - it('should not allow a non-admin to become an admin', async () => { - const user = await api.userApi.create(server, accessToken, { - email: 'user1@immich.app', - password: 'Password123', - name: 'Immich User', - }); - - const { status, body } = await request(server) - .put(`/user`) - .send({ isAdmin: true, id: user.id }) - .set('Authorization', `Bearer ${accessToken}`); - - expect(status).toBe(400); - expect(body).toEqual(errorStub.alreadyHasAdmin); - }); - - it('ignores updates to profileImagePath', async () => { - const user = await api.userApi.update(server, accessToken, { - id: loginResponse.userId, - profileImagePath: 'invalid.jpg', - } as any); - - expect(user).toMatchObject({ id: loginResponse.userId, profileImagePath: '' }); - }); - - it('should ignore updates to createdAt, updatedAt and deletedAt', async () => { - const before = await api.userApi.get(server, accessToken, loginResponse.userId); - const after = await api.userApi.update(server, accessToken, { - id: loginResponse.userId, - createdAt: '2023-01-01T00:00:00.000Z', - updatedAt: '2023-01-01T00:00:00.000Z', - deletedAt: '2023-01-01T00:00:00.000Z', - } as any); - - expect(after).toStrictEqual(before); - }); - - it('should update first and last name', async () => { - const before = await api.userApi.get(server, accessToken, loginResponse.userId); - const after = await api.userApi.update(server, accessToken, { - id: before.id, - name: 'Name', - }); - - expect(after).toEqual({ - ...before, - updatedAt: expect.any(String), - name: 'Name', - }); - expect(before.updatedAt).not.toEqual(after.updatedAt); - }); - - it('should update memories enabled', async () => { - const before = await api.userApi.get(server, accessToken, loginResponse.userId); - const after = await api.userApi.update(server, accessToken, { - id: before.id, - memoriesEnabled: false, - }); - - expect(after).toMatchObject({ - ...before, - updatedAt: expect.anything(), - memoriesEnabled: false, - }); - expect(before.updatedAt).not.toEqual(after.updatedAt); - }); - }); -}); diff --git a/server/e2e/api/utils.ts b/server/e2e/api/utils.ts index 5dffea98f6..c03c4ada55 100644 --- a/server/e2e/api/utils.ts +++ b/server/e2e/api/utils.ts @@ -4,8 +4,8 @@ import { InfraModule, InfraTestModule, dataSource } from '@app/infra'; import { AssetEntity, AssetType, LibraryType } from '@app/infra/entities'; import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { randomBytes } from 'crypto'; import { DateTime } from 'luxon'; +import { randomBytes } from 'node:crypto'; import { EntityTarget, ObjectLiteral } from 'typeorm'; import { AppService } from '../../src/microservices/app.service'; import { newJobRepositoryMock, newMetadataRepositoryMock } from '../../test'; diff --git a/server/e2e/client/activity-api.ts b/server/e2e/client/activity-api.ts deleted file mode 100644 index f7cac45624..0000000000 --- a/server/e2e/client/activity-api.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ActivityCreateDto, ActivityResponseDto } from '@app/domain'; -import request from 'supertest'; - -export const activityApi = { - create: async (server: any, accessToken: string, dto: ActivityCreateDto) => { - const res = await request(server).post('/activity').set('Authorization', `Bearer ${accessToken}`).send(dto); - expect(res.status === 200 || res.status === 201).toBe(true); - return res.body as ActivityResponseDto; - }, - delete: async (server: any, accessToken: string, id: string) => { - const res = await request(server).delete(`/activity/${id}`).set('Authorization', `Bearer ${accessToken}`); - expect(res.status).toEqual(204); - }, -}; diff --git a/server/e2e/client/album-api.ts b/server/e2e/client/album-api.ts deleted file mode 100644 index 92c75dc64b..0000000000 --- a/server/e2e/client/album-api.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { AddUsersDto, AlbumResponseDto, BulkIdResponseDto, BulkIdsDto, CreateAlbumDto } from '@app/domain'; -import request from 'supertest'; - -export const albumApi = { - create: async (server: any, accessToken: string, dto: CreateAlbumDto) => { - const res = await request(server).post('/album').set('Authorization', `Bearer ${accessToken}`).send(dto); - expect(res.status).toEqual(201); - return res.body as AlbumResponseDto; - }, - addAssets: async (server: any, accessToken: string, id: string, dto: BulkIdsDto) => { - const res = await request(server) - .put(`/album/${id}/assets`) - .set('Authorization', `Bearer ${accessToken}`) - .send(dto); - expect(res.status).toEqual(200); - return res.body as BulkIdResponseDto[]; - }, - addUsers: async (server: any, accessToken: string, id: string, dto: AddUsersDto) => { - const res = await request(server).put(`/album/${id}/users`).set('Authorization', `Bearer ${accessToken}`).send(dto); - expect(res.status).toEqual(200); - return res.body as AlbumResponseDto; - }, - getAllAlbums: async (server: any, accessToken: string) => { - const res = await request(server).get(`/album/`).set('Authorization', `Bearer ${accessToken}`).send(); - expect(res.status).toEqual(200); - return res.body as AlbumResponseDto[]; - }, -}; diff --git a/server/e2e/client/api-key-api.ts b/server/e2e/client/api-key-api.ts deleted file mode 100644 index a35f13f7d9..0000000000 --- a/server/e2e/client/api-key-api.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { APIKeyCreateResponseDto } from '@app/domain'; -import { apiKeyCreateStub } from '@test'; -import request from 'supertest'; - -export const apiKeyApi = { - createApiKey: async (server: any, accessToken: string) => { - const { status, body } = await request(server) - .post('/api-key') - .set('Authorization', `Bearer ${accessToken}`) - .send(apiKeyCreateStub); - - expect(status).toBe(201); - - return body as APIKeyCreateResponseDto; - }, -}; diff --git a/server/e2e/client/auth-api.ts b/server/e2e/client/auth-api.ts index 3043c941f2..f0206d3376 100644 --- a/server/e2e/client/auth-api.ts +++ b/server/e2e/client/auth-api.ts @@ -1,4 +1,4 @@ -import { AuthDeviceResponseDto, LoginCredentialDto, LoginResponseDto, UserResponseDto } from '@app/domain'; +import { LoginCredentialDto, LoginResponseDto, UserResponseDto } from '@app/domain'; import { adminSignupStub, loginResponseStub, loginStub } from '@test'; import request from 'supertest'; @@ -27,19 +27,4 @@ export const authApi = { return body as LoginResponseDto; }, - getAuthDevices: async (server: any, accessToken: string) => { - const { status, body } = await request(server).get('/auth/devices').set('Authorization', `Bearer ${accessToken}`); - - expect(body).toEqual(expect.any(Array)); - expect(status).toBe(200); - - return body as AuthDeviceResponseDto[]; - }, - validateToken: async (server: any, accessToken: string) => { - const { status, body } = await request(server) - .post('/auth/validateToken') - .set('Authorization', `Bearer ${accessToken}`); - expect(body).toEqual({ authStatus: true }); - expect(status).toBe(200); - }, }; diff --git a/server/e2e/client/index.ts b/server/e2e/client/index.ts index d13f2425e8..b0464a34d8 100644 --- a/server/e2e/client/index.ts +++ b/server/e2e/client/index.ts @@ -1,23 +1,15 @@ -import { activityApi } from './activity-api'; -import { albumApi } from './album-api'; -import { apiKeyApi } from './api-key-api'; import { assetApi } from './asset-api'; import { authApi } from './auth-api'; import { libraryApi } from './library-api'; -import { partnerApi } from './partner-api'; -import { serverInfoApi } from './server-info-api'; import { sharedLinkApi } from './shared-link-api'; +import { trashApi } from './trash-api'; import { userApi } from './user-api'; export const api = { - activityApi, authApi, - apiKeyApi, assetApi, libraryApi, - serverInfoApi, sharedLinkApi, - albumApi, + trashApi, userApi, - partnerApi, }; diff --git a/server/e2e/client/library-api.ts b/server/e2e/client/library-api.ts index c40f005457..e0b1331267 100644 --- a/server/e2e/client/library-api.ts +++ b/server/e2e/client/library-api.ts @@ -4,6 +4,8 @@ import { LibraryStatsResponseDto, ScanLibraryDto, UpdateLibraryDto, + ValidateLibraryDto, + ValidateLibraryResponseDto, } from '@app/domain'; import request from 'supertest'; @@ -34,14 +36,14 @@ export const libraryApi = { .post(`/library/${id}/scan`) .set('Authorization', `Bearer ${accessToken}`) .send(dto); - expect(status).toBe(201); + expect(status).toBe(204); }, removeOfflineFiles: async (server: any, accessToken: string, id: string) => { const { status } = await request(server) .post(`/library/${id}/removeOffline`) .set('Authorization', `Bearer ${accessToken}`) .send(); - expect(status).toBe(201); + expect(status).toBe(204); }, getLibraryStatistics: async (server: any, accessToken: string, id: string): Promise => { const { body, status } = await request(server) @@ -58,4 +60,12 @@ export const libraryApi = { expect(status).toBe(200); return body as LibraryResponseDto; }, + validate: async (server: any, accessToken: string, id: string, data: ValidateLibraryDto) => { + const { body, status } = await request(server) + .post(`/library/${id}/validate`) + .set('Authorization', `Bearer ${accessToken}`) + .send(data); + expect(status).toBe(200); + return body as ValidateLibraryResponseDto; + }, }; diff --git a/server/e2e/client/partner-api.ts b/server/e2e/client/partner-api.ts deleted file mode 100644 index 97a9558c5f..0000000000 --- a/server/e2e/client/partner-api.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { PartnerResponseDto } from '@app/domain'; -import request from 'supertest'; - -export const partnerApi = { - create: async (server: any, accessToken: string, id: string) => { - const { status, body } = await request(server).post(`/partner/${id}`).set('Authorization', `Bearer ${accessToken}`); - expect(status).toBe(201); - return body as PartnerResponseDto; - }, -}; diff --git a/server/e2e/client/server-info-api.ts b/server/e2e/client/server-info-api.ts deleted file mode 100644 index f885bc856f..0000000000 --- a/server/e2e/client/server-info-api.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ServerConfigDto } from '@app/domain'; -import request from 'supertest'; - -export const serverInfoApi = { - getConfig: async (server: any) => { - const res = await request(server).get('/server-info/config'); - expect(res.status).toBe(200); - return res.body as ServerConfigDto; - }, -}; diff --git a/server/e2e/client/shared-link-api.ts b/server/e2e/client/shared-link-api.ts index d6179f6b6f..c34093b0ac 100644 --- a/server/e2e/client/shared-link-api.ts +++ b/server/e2e/client/shared-link-api.ts @@ -10,11 +10,4 @@ export const sharedLinkApi = { expect(status).toBe(201); return body as SharedLinkResponseDto; }, - - getMySharedLink: async (server: any, key: string) => { - const { status, body } = await request(server).get('/shared-link/me').query({ key }); - - expect(status).toBe(200); - return body as SharedLinkResponseDto; - }, }; diff --git a/server/e2e/client/trash-api.ts b/server/e2e/client/trash-api.ts new file mode 100644 index 0000000000..a381253f50 --- /dev/null +++ b/server/e2e/client/trash-api.ts @@ -0,0 +1,13 @@ +import request from 'supertest'; +import type { App } from 'supertest/types'; + +export const trashApi = { + async empty(server: App, accessToken: string) { + const { status } = await request(server).post('/trash/empty').set('Authorization', `Bearer ${accessToken}`); + expect(status).toBe(204); + }, + async restore(server: App, accessToken: string) { + const { status } = await request(server).post('/trash/restore').set('Authorization', `Bearer ${accessToken}`); + expect(status).toBe(204); + }, +}; diff --git a/server/e2e/client/user-api.ts b/server/e2e/client/user-api.ts index 5ed0838f75..c538db3a8f 100644 --- a/server/e2e/client/user-api.ts +++ b/server/e2e/client/user-api.ts @@ -18,16 +18,6 @@ export const userApi = { return body as UserResponseDto; }, - get: async (server: any, accessToken: string, id: string) => { - const { status, body } = await request(server) - .get(`/user/info/${id}`) - .set('Authorization', `Bearer ${accessToken}`); - - expect(status).toBe(200); - expect(body).toMatchObject({ id }); - - return body as UserResponseDto; - }, update: async (server: any, accessToken: string, dto: UpdateUserDto) => { const { status, body } = await request(server).put('/user').set('Authorization', `Bearer ${accessToken}`).send(dto); @@ -36,9 +26,6 @@ export const userApi = { return body as UserResponseDto; }, - setExternalPath: async (server: any, accessToken: string, id: string, externalPath: string) => { - return await userApi.update(server, accessToken, { id, externalPath }); - }, delete: async (server: any, accessToken: string, id: string) => { const { status, body } = await request(server).delete(`/user/${id}`).set('Authorization', `Bearer ${accessToken}`); diff --git a/server/e2e/docker-compose.server-e2e.yml b/server/e2e/docker-compose.server-e2e.yml index abb76ef25d..09db7ecf32 100644 --- a/server/e2e/docker-compose.server-e2e.yml +++ b/server/e2e/docker-compose.server-e2e.yml @@ -12,6 +12,7 @@ services: command: ['/usr/src/app/bin/immich-test', 'jobs'] volumes: - /usr/src/app/node_modules + - ../test/assets:/usr/src/app/test/assets:ro environment: - DB_HOSTNAME=database - DB_USERNAME=postgres diff --git a/server/e2e/jobs/specs/formats.e2e-spec.ts b/server/e2e/jobs/specs/formats.e2e-spec.ts deleted file mode 100644 index 5f6ffba311..0000000000 --- a/server/e2e/jobs/specs/formats.e2e-spec.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { LoginResponseDto } from '@app/domain'; -import { AssetType } from '@app/infra/entities'; -import { readFile } from 'fs/promises'; -import { basename, join } from 'path'; -import { IMMICH_TEST_ASSET_PATH, testApp } from '../../../src/test-utils/utils'; -import { api } from '../../client'; - -const JPEG = { - type: AssetType.IMAGE, - originalFileName: 'el_torcal_rocks', - resized: true, - exifInfo: { - dateTimeOriginal: '2012-08-05T11:39:59.000Z', - exifImageWidth: 512, - exifImageHeight: 341, - latitude: null, - longitude: null, - focalLength: 75, - iso: 200, - fNumber: 11, - exposureTime: '1/160', - fileSizeInByte: 53493, - make: 'SONY', - model: 'DSLR-A550', - orientation: null, - description: 'SONY DSC', - }, -}; - -const tests = [ - { input: 'formats/jpg/el_torcal_rocks.jpg', expected: JPEG }, - { input: 'formats/jpeg/el_torcal_rocks.jpeg', expected: JPEG }, - { - input: 'formats/heic/IMG_2682.heic', - expected: { - type: AssetType.IMAGE, - originalFileName: 'IMG_2682', - resized: true, - fileCreatedAt: '2019-03-21T16:04:22.348Z', - exifInfo: { - dateTimeOriginal: '2019-03-21T16:04:22.348Z', - exifImageWidth: 4032, - exifImageHeight: 3024, - latitude: 41.2203, - longitude: -96.071625, - make: 'Apple', - model: 'iPhone 7', - lensModel: 'iPhone 7 back camera 3.99mm f/1.8', - fileSizeInByte: 880703, - exposureTime: '1/887', - iso: 20, - focalLength: 3.99, - fNumber: 1.8, - timeZone: 'America/Chicago', - }, - }, - }, - { - input: 'formats/png/density_plot.png', - expected: { - type: AssetType.IMAGE, - originalFileName: 'density_plot', - resized: true, - exifInfo: { - exifImageWidth: 800, - exifImageHeight: 800, - latitude: null, - longitude: null, - fileSizeInByte: 25408, - }, - }, - }, - { - input: 'formats/raw/Nikon/D80/glarus.nef', - expected: { - type: AssetType.IMAGE, - originalFileName: 'glarus', - resized: true, - fileCreatedAt: '2010-07-20T17:27:12.000Z', - exifInfo: { - make: 'NIKON CORPORATION', - model: 'NIKON D80', - exposureTime: '1/200', - fNumber: 10, - focalLength: 18, - iso: 100, - fileSizeInByte: 9057784, - dateTimeOriginal: '2010-07-20T17:27:12.000Z', - latitude: null, - longitude: null, - orientation: '1', - }, - }, - }, - { - input: 'formats/raw/Nikon/D700/philadelphia.nef', - expected: { - type: AssetType.IMAGE, - originalFileName: 'philadelphia', - resized: true, - fileCreatedAt: '2016-09-22T22:10:29.060Z', - exifInfo: { - make: 'NIKON CORPORATION', - model: 'NIKON D700', - exposureTime: '1/400', - fNumber: 11, - focalLength: 85, - iso: 200, - fileSizeInByte: 15856335, - dateTimeOriginal: '2016-09-22T22:10:29.060Z', - latitude: null, - longitude: null, - orientation: '1', - timeZone: 'UTC-5', - }, - }, - }, -]; - -describe(`Format (e2e)`, () => { - let server: any; - let admin: LoginResponseDto; - - beforeAll(async () => { - const app = await testApp.create(); - server = app.getHttpServer(); - }); - - beforeEach(async () => { - await testApp.reset(); - await api.authApi.adminSignUp(server); - admin = await api.authApi.adminLogin(server); - }); - - afterAll(async () => { - await testApp.teardown(); - }); - - for (const { input, expected } of tests) { - it(`should generate a thumbnail for ${input}`, async () => { - const filepath = join(IMMICH_TEST_ASSET_PATH, input); - const content = await readFile(filepath); - await api.assetApi.upload(server, admin.accessToken, 'test-device-id', { - content, - filename: basename(filepath), - }); - const assets = await api.assetApi.getAllAssets(server, admin.accessToken); - - expect(assets).toHaveLength(1); - - const asset = assets[0]; - - expect(asset.exifInfo).toBeDefined(); - expect(asset.exifInfo).toMatchObject(expected.exifInfo); - expect(asset).toMatchObject(expected); - }); - } -}); diff --git a/server/e2e/jobs/specs/library-watcher.e2e-spec.ts b/server/e2e/jobs/specs/library-watcher.e2e-spec.ts index cb7dd5f894..93f7163531 100644 --- a/server/e2e/jobs/specs/library-watcher.e2e-spec.ts +++ b/server/e2e/jobs/specs/library-watcher.e2e-spec.ts @@ -1,7 +1,7 @@ import { LibraryResponseDto, LibraryService, LoginResponseDto } from '@app/domain'; import { AssetType, LibraryType } from '@app/infra/entities'; -import fs from 'fs/promises'; -import path from 'path'; +import fs from 'node:fs/promises'; +import path from 'node:path'; import { IMMICH_TEST_ASSET_PATH, IMMICH_TEST_ASSET_TEMP_PATH, @@ -20,7 +20,8 @@ describe(`Library watcher (e2e)`, () => { beforeAll(async () => { process.env.IMMICH_CONFIG_FILE = path.normalize(`${__dirname}/../config/library-watcher-e2e-config.json`); - server = (await testApp.create()).getHttpServer(); + const app = await testApp.create(); + server = app.getHttpServer(); libraryService = testApp.get(LibraryService); }); @@ -29,8 +30,6 @@ describe(`Library watcher (e2e)`, () => { await restoreTempFolder(); await api.authApi.adminSignUp(server); admin = await api.authApi.adminLogin(server); - - await api.userApi.setExternalPath(server, admin.accessToken, admin.userId, '/'); }); afterEach(async () => { @@ -204,8 +203,6 @@ describe(`Library watcher (e2e)`, () => { ], }); - await api.userApi.setExternalPath(server, admin.accessToken, admin.userId, '/'); - await fs.mkdir(`${IMMICH_TEST_ASSET_TEMP_PATH}/dir1`, { recursive: true }); await fs.mkdir(`${IMMICH_TEST_ASSET_TEMP_PATH}/dir2`, { recursive: true }); await fs.mkdir(`${IMMICH_TEST_ASSET_TEMP_PATH}/dir3`, { recursive: true }); diff --git a/server/e2e/jobs/specs/library.e2e-spec.ts b/server/e2e/jobs/specs/library.e2e-spec.ts index cb19117668..0657227f8d 100644 --- a/server/e2e/jobs/specs/library.e2e-spec.ts +++ b/server/e2e/jobs/specs/library.e2e-spec.ts @@ -40,19 +40,16 @@ describe(`${LibraryController.name} (e2e)`, () => { type: LibraryType.EXTERNAL, importPaths: [`${IMMICH_TEST_ASSET_PATH}/albums/nature`], }); - await api.userApi.setExternalPath(server, admin.accessToken, admin.userId, '/'); - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); const assets = await api.assetApi.getAllAssets(server, admin.accessToken); expect(assets.length).toBeGreaterThan(2); - const { status, body } = await request(server) + const { status } = await request(server) .delete(`/library/${library.id}`) .set('Authorization', `Bearer ${admin.accessToken}`); - expect(status).toBe(200); - expect(body).toEqual({}); + expect(status).toBe(204); const libraries = await api.libraryApi.getAll(server, admin.accessToken); expect(libraries).toHaveLength(1); @@ -79,8 +76,6 @@ describe(`${LibraryController.name} (e2e)`, () => { type: LibraryType.EXTERNAL, importPaths: [`${IMMICH_TEST_ASSET_PATH}/albums/nature`], }); - await api.userApi.setExternalPath(server, admin.accessToken, admin.userId, '/'); - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); const assets = await api.assetApi.getAllAssets(server, admin.accessToken); @@ -118,16 +113,12 @@ describe(`${LibraryController.name} (e2e)`, () => { }); it('should scan external library with exclusion pattern', async () => { - await api.userApi.setExternalPath(server, admin.accessToken, admin.userId, '/not/a/real/path'); - const library = await api.libraryApi.create(server, admin.accessToken, { type: LibraryType.EXTERNAL, importPaths: [`${IMMICH_TEST_ASSET_PATH}/albums/nature`], exclusionPatterns: ['**/el_corcal*'], }); - await api.userApi.setExternalPath(server, admin.accessToken, admin.userId, '/'); - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); const assets = await api.assetApi.getAllAssets(server, admin.accessToken); @@ -163,7 +154,6 @@ describe(`${LibraryController.name} (e2e)`, () => { type: LibraryType.EXTERNAL, importPaths: [`${IMMICH_TEST_ASSET_TEMP_PATH}`], }); - await api.userApi.setExternalPath(server, admin.accessToken, admin.userId, '/'); await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); @@ -190,39 +180,11 @@ describe(`${LibraryController.name} (e2e)`, () => { ); }); - it('should offline files outside of changed external path', async () => { - const library = await api.libraryApi.create(server, admin.accessToken, { - type: LibraryType.EXTERNAL, - importPaths: [`${IMMICH_TEST_ASSET_PATH}/albums/nature`], - }); - await api.userApi.setExternalPath(server, admin.accessToken, admin.userId, '/'); - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); - - await api.userApi.setExternalPath(server, admin.accessToken, admin.userId, '/some/other/path'); - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); - - const assets = await api.assetApi.getAllAssets(server, admin.accessToken); - - expect(assets).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - isOffline: true, - originalFileName: 'el_torcal_rocks', - }), - expect.objectContaining({ - isOffline: true, - originalFileName: 'tanners_ridge', - }), - ]), - ); - }); - it('should scan new files', async () => { const library = await api.libraryApi.create(server, admin.accessToken, { type: LibraryType.EXTERNAL, importPaths: [`${IMMICH_TEST_ASSET_TEMP_PATH}`], }); - await api.userApi.setExternalPath(server, admin.accessToken, admin.userId, '/'); await fs.promises.cp( `${IMMICH_TEST_ASSET_PATH}/albums/nature/silver_fir.jpg`, @@ -258,7 +220,6 @@ describe(`${LibraryController.name} (e2e)`, () => { type: LibraryType.EXTERNAL, importPaths: [`${IMMICH_TEST_ASSET_TEMP_PATH}`], }); - await api.userApi.setExternalPath(server, admin.accessToken, admin.userId, '/'); await fs.promises.cp( `${IMMICH_TEST_ASSET_PATH}/albums/nature/el_torcal_rocks.jpg`, @@ -305,7 +266,6 @@ describe(`${LibraryController.name} (e2e)`, () => { type: LibraryType.EXTERNAL, importPaths: [`${IMMICH_TEST_ASSET_TEMP_PATH}`], }); - await api.userApi.setExternalPath(server, admin.accessToken, admin.userId, '/'); await fs.promises.cp( `${IMMICH_TEST_ASSET_PATH}/albums/nature/el_torcal_rocks.jpg`, @@ -345,7 +305,6 @@ describe(`${LibraryController.name} (e2e)`, () => { type: LibraryType.EXTERNAL, importPaths: [`${IMMICH_TEST_ASSET_TEMP_PATH}`], }); - await api.userApi.setExternalPath(server, admin.accessToken, admin.userId, '/'); await fs.promises.cp( `${IMMICH_TEST_ASSET_PATH}/albums/nature/el_torcal_rocks.jpg`, @@ -387,72 +346,6 @@ describe(`${LibraryController.name} (e2e)`, () => { }); }); - describe('External path', () => { - let library: LibraryResponseDto; - - beforeEach(async () => { - library = await api.libraryApi.create(server, admin.accessToken, { - type: LibraryType.EXTERNAL, - importPaths: [`${IMMICH_TEST_ASSET_PATH}/albums/nature`], - }); - }); - - it('should not scan assets for user without external path', async () => { - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); - const assets = await api.assetApi.getAllAssets(server, admin.accessToken); - - expect(assets).toEqual([]); - }); - - it("should not import assets outside of user's external path", async () => { - await api.userApi.setExternalPath(server, admin.accessToken, admin.userId, '/not/a/real/path'); - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); - - const assets = await api.assetApi.getAllAssets(server, admin.accessToken); - expect(assets).toEqual([]); - }); - - it.each([`${IMMICH_TEST_ASSET_PATH}/albums/nature`, `${IMMICH_TEST_ASSET_PATH}/albums/nature/`])( - 'should scan external library with external path %s', - async (externalPath: string) => { - await api.userApi.setExternalPath(server, admin.accessToken, admin.userId, externalPath); - - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); - - const assets = await api.assetApi.getAllAssets(server, admin.accessToken); - - expect(assets).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - type: AssetType.IMAGE, - originalFileName: 'el_torcal_rocks', - libraryId: library.id, - resized: true, - exifInfo: expect.objectContaining({ - exifImageWidth: 512, - exifImageHeight: 341, - latitude: null, - longitude: null, - }), - }), - expect.objectContaining({ - type: AssetType.IMAGE, - originalFileName: 'silver_fir', - libraryId: library.id, - resized: true, - exifInfo: expect.objectContaining({ - exifImageWidth: 511, - exifImageHeight: 323, - latitude: null, - longitude: null, - }), - }), - ]), - ); - }, - ); - }); - it('should not scan an upload library', async () => { const library = await api.libraryApi.create(server, admin.accessToken, { type: LibraryType.UPLOAD, @@ -484,7 +377,6 @@ describe(`${LibraryController.name} (e2e)`, () => { type: LibraryType.EXTERNAL, importPaths: [`${IMMICH_TEST_ASSET_TEMP_PATH}`], }); - await api.userApi.setExternalPath(server, admin.accessToken, admin.userId, '/'); await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); @@ -499,19 +391,18 @@ describe(`${LibraryController.name} (e2e)`, () => { .post(`/library/${library.id}/removeOffline`) .set('Authorization', `Bearer ${admin.accessToken}`) .send(); - expect(status).toBe(201); + expect(status).toBe(204); const assets = await api.assetApi.getAllAssets(server, admin.accessToken); expect(assets).toEqual([]); }); - it('should not remvove online files', async () => { + it('should not remove online files', async () => { const library = await api.libraryApi.create(server, admin.accessToken, { type: LibraryType.EXTERNAL, importPaths: [`${IMMICH_TEST_ASSET_PATH}/albums/nature`], }); - await api.userApi.setExternalPath(server, admin.accessToken, admin.userId, '/'); await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); @@ -524,7 +415,7 @@ describe(`${LibraryController.name} (e2e)`, () => { .post(`/library/${library.id}/removeOffline`) .set('Authorization', `Bearer ${admin.accessToken}`) .send(); - expect(status).toBe(201); + expect(status).toBe(204); const assetsAfter = await api.assetApi.getAllAssets(server, admin.accessToken); diff --git a/server/e2e/jobs/specs/metadata.e2e-spec.ts b/server/e2e/jobs/specs/metadata.e2e-spec.ts deleted file mode 100644 index 5eb75fee2d..0000000000 --- a/server/e2e/jobs/specs/metadata.e2e-spec.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { AssetResponseDto, LoginResponseDto } from '@app/domain'; -import { AssetController } from '@app/immich'; -import { exiftool } from 'exiftool-vendored'; -import { readFile, writeFile } from 'fs/promises'; -import { - IMMICH_TEST_ASSET_PATH, - IMMICH_TEST_ASSET_TEMP_PATH, - db, - restoreTempFolder, - testApp, -} from '../../../src/test-utils/utils'; -import { api } from '../../client'; - -describe(`${AssetController.name} (e2e)`, () => { - let server: any; - let admin: LoginResponseDto; - - beforeAll(async () => { - server = (await testApp.create()).getHttpServer(); - }); - - beforeEach(async () => { - await testApp.reset(); - await restoreTempFolder(); - await api.authApi.adminSignUp(server); - admin = await api.authApi.adminLogin(server); - }); - - afterAll(async () => { - await testApp.teardown(); - await restoreTempFolder(); - }); - - describe('should strip metadata of', () => { - let assetWithLocation: AssetResponseDto; - - beforeEach(async () => { - const fileContent = await readFile(`${IMMICH_TEST_ASSET_PATH}/metadata/gps-position/thompson-springs.jpg`); - - await api.assetApi.upload(server, admin.accessToken, 'test-asset-id', { content: fileContent }); - - const assets = await api.assetApi.getAllAssets(server, admin.accessToken); - - expect(assets).toHaveLength(1); - assetWithLocation = assets[0]; - - expect(assetWithLocation).toEqual( - expect.objectContaining({ - exifInfo: expect.objectContaining({ latitude: 39.115, longitude: -108.400968333333 }), - }), - ); - }); - - it('small webp thumbnails', async () => { - const assetId = assetWithLocation.id; - - const thumbnail = await api.assetApi.getWebpThumbnail(server, admin.accessToken, assetId); - - await writeFile(`${IMMICH_TEST_ASSET_TEMP_PATH}/thumbnail.webp`, thumbnail); - - const exifData = await exiftool.read(`${IMMICH_TEST_ASSET_TEMP_PATH}/thumbnail.webp`); - - expect(exifData).not.toHaveProperty('GPSLongitude'); - expect(exifData).not.toHaveProperty('GPSLatitude'); - }); - - it('large jpeg thumbnails', async () => { - const assetId = assetWithLocation.id; - - const thumbnail = await api.assetApi.getJpegThumbnail(server, admin.accessToken, assetId); - - await writeFile(`${IMMICH_TEST_ASSET_TEMP_PATH}/thumbnail.jpg`, thumbnail); - - const exifData = await exiftool.read(`${IMMICH_TEST_ASSET_TEMP_PATH}/thumbnail.jpg`); - - expect(exifData).not.toHaveProperty('GPSLongitude'); - expect(exifData).not.toHaveProperty('GPSLatitude'); - }); - }); - - describe.each([ - // These hashes were created by copying the image files to a Samsung phone, - // exporting the video from Samsung's stock Gallery app, and hashing them locally. - // This ensures that immich+exiftool are extracting the videos the same way Samsung does. - // DO NOT assume immich+exiftool are doing things correctly and just copy whatever hash it gives - // into the test here. - ['Samsung One UI 5.jpg', 'fr14niqCq6N20HB8rJYEvpsUVtI='], - ['Samsung One UI 6.jpg', 'lT9Uviw/FFJYCjfIxAGPTjzAmmw='], - ['Samsung One UI 6.heic', '/ejgzywvgvzvVhUYVfvkLzFBAF0='], - ])('should extract motionphoto video', (file, checksum) => { - it(`with checksum ${checksum} from ${file}`, async () => { - const fileContent = await readFile(`${IMMICH_TEST_ASSET_PATH}/formats/motionphoto/${file}`); - - const response = await api.assetApi.upload(server, admin.accessToken, 'test-asset-id', { content: fileContent }); - const asset = await api.assetApi.get(server, admin.accessToken, response.id); - expect(asset).toHaveProperty('livePhotoVideoId'); - const video = await api.assetApi.get(server, admin.accessToken, asset.livePhotoVideoId as string); - - expect(video.checksum).toStrictEqual(checksum); - }); - }); -}); diff --git a/server/package-lock.json b/server/package-lock.json index 966639fa8d..4ba12e2636 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich", - "version": "1.94.1", + "version": "1.97.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "immich", - "version": "1.94.1", + "version": "1.97.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@babel/runtime": "^7.22.11", @@ -31,8 +31,8 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "cookie-parser": "^1.4.6", - "exiftool-vendored": "~24.4.0", - "exiftool-vendored.pl": "12.73", + "exiftool-vendored": "~24.5.0", + "exiftool-vendored.pl": "12.76", "fluent-ffmpeg": "^2.1.2", "geo-tz": "^8.0.0", "glob": "^10.3.3", @@ -76,8 +76,8 @@ "@types/sharp": "^0.31.1", "@types/supertest": "^6.0.0", "@types/ua-parser-js": "^0.7.36", - "@typescript-eslint/eslint-plugin": "^6.4.1", - "@typescript-eslint/parser": "^6.4.1", + "@typescript-eslint/eslint-plugin": "^7.0.0", + "@typescript-eslint/parser": "^7.0.0", "dotenv": "^16.3.1", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", @@ -2374,9 +2374,9 @@ } }, "node_modules/@nestjs/common": { - "version": "10.3.2", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.3.2.tgz", - "integrity": "sha512-yrtohmQlN5J/gFS/ui86SO+KIfUKeL39JPR3f/3AWgpz+duIfc9cGkfh7FGZQMfG9ZqXf7Zw+PRO9G+D4iEbPw==", + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.3.3.tgz", + "integrity": "sha512-LAkTe8/CF0uNWM0ecuDwUNTHCi1lVSITmmR4FQ6Ftz1E7ujQCnJ5pMRzd8JRN14vdBkxZZ8VbVF0BDUKoKNxMQ==", "dependencies": { "iterare": "1.2.1", "tslib": "2.6.2", @@ -2416,10 +2416,21 @@ "rxjs": "^7.1.0" } }, + "node_modules/@nestjs/config/node_modules/dotenv": { + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.1.tgz", + "integrity": "sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, "node_modules/@nestjs/core": { - "version": "10.3.2", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.3.2.tgz", - "integrity": "sha512-JW3bQvDFY1gB+xXR6E5DzCdKftRszyWtd0YyDkdlKh1+44e2IGybFhSa5HcQBOiRqdVgPqAM5Vqc81rmhgeBnQ==", + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.3.3.tgz", + "integrity": "sha512-kxJWggQAPX3RuZx9JVec69eSLaYLNIox2emkZJpfBJ5Qq7cAq7edQIt1r4LGjTKq6kFubNTPsqhWf5y7yFRBPw==", "hasInstallScript": true, "dependencies": { "@nuxtjs/opencollective": "0.3.2", @@ -2473,9 +2484,9 @@ } }, "node_modules/@nestjs/platform-express": { - "version": "10.3.2", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.3.2.tgz", - "integrity": "sha512-jS0uQ5VCsOezhzW6xzlWquGEaDHQ9B+JTVsuxbac+3XOPt6sbKry8HHOWyjKss9wAKo0lDRSFvpV5oQAF/yl6w==", + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.3.3.tgz", + "integrity": "sha512-GGKSEU48Os7nYFIsUM0nutuFUGn5AbeP8gzFBiBCAtiuJWrXZXpZ58pMBYxAbMf7IrcOZFInHEukjHGAQU0OZw==", "dependencies": { "body-parser": "1.20.2", "cors": "2.8.5", @@ -2493,9 +2504,9 @@ } }, "node_modules/@nestjs/platform-socket.io": { - "version": "10.3.2", - "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.3.2.tgz", - "integrity": "sha512-BnU2tmH6cMSY6PUxen8QKmmLu7fZwe5b/IEx0kipBV9jGnidii+9VhW9sbdbZOQhrZVdGLByYEuaX7rdguq0lg==", + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.3.3.tgz", + "integrity": "sha512-QqM9BMTdYPvXOqx3oWrv130HOtc2krPvfgqgDsPWkBLfR+TssrA5QDaTW8HSjEQAfmugvHwhEAAU4+yXRl6tKg==", "dependencies": { "socket.io": "4.7.4", "tslib": "2.6.2" @@ -2578,9 +2589,9 @@ } }, "node_modules/@nestjs/testing": { - "version": "10.3.2", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.3.2.tgz", - "integrity": "sha512-jetqEPqOPuxmhBinkizmJQg4UZ2IRFrUoMrBDSgg0ogQClokKjnLgkoC5de+Jfm2kub/VpqorHB0me8cCr5jEQ==", + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.3.3.tgz", + "integrity": "sha512-kX20GfjAImL5grd/i69uD/x7sc00BaqGcP2dRG3ilqshQUuy5DOmspLCr3a2C8xmVU7kzK4spT0oTxhe6WcCAA==", "dev": true, "dependencies": { "tslib": "2.6.2" @@ -2620,9 +2631,9 @@ } }, "node_modules/@nestjs/websockets": { - "version": "10.3.2", - "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.3.2.tgz", - "integrity": "sha512-SMQM8hcRgngOabnluRLei46JUYvZ6j3DaZ2bDNYu57G3nlmkxXNktz7/pJG07NcetZL4CyF9pNyuICZql4Nhww==", + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.3.3.tgz", + "integrity": "sha512-cR5cB0bLS87vd0iu7Nud/4x2EH1Vs0aIgwGWd0eH/5SAw0rrDNU81PiOde+rnMXETbxvSVfOZuLRyn7/WQtGUg==", "dependencies": { "iterare": "1.2.1", "object-hash": "3.0.0", @@ -2694,9 +2705,9 @@ } }, "node_modules/@photostructure/tz-lookup": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@photostructure/tz-lookup/-/tz-lookup-9.0.0.tgz", - "integrity": "sha512-gM3Xrs+XhD8ojDN0TgybuzSjsQb9UvF8j9DvR75E2zHlJQNiOztzILvfhVwadgA8JJbSMNzE+kYUnwP8aQnlXw==" + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@photostructure/tz-lookup/-/tz-lookup-9.0.1.tgz", + "integrity": "sha512-inMfhc1QVKheq/PHF0v2vRPnPzTljxscuOKK95o3VlZA4T4w4DeYIpu7dC4W1EyjUhfZJlCBUudpmnFGNCqTog==" }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", @@ -3168,9 +3179,9 @@ } }, "node_modules/@types/node": { - "version": "20.11.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", - "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", + "version": "20.11.20", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.20.tgz", + "integrity": "sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==", "dependencies": { "undici-types": "~5.26.4" } @@ -3270,9 +3281,9 @@ } }, "node_modules/@types/semver": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.7.tgz", - "integrity": "sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "node_modules/@types/send": { @@ -3387,16 +3398,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.0.2.tgz", + "integrity": "sha512-/XtVZJtbaphtdrWjr+CJclaCVGPtOdBpFEnvtNf/jRV0IiEemRrL0qABex/nEt8isYcnFacm3nPHYQwL+Wb7qg==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/scope-manager": "7.0.2", + "@typescript-eslint/type-utils": "7.0.2", + "@typescript-eslint/utils": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -3412,8 +3423,8 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -3422,15 +3433,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.0.2.tgz", + "integrity": "sha512-GdwfDglCxSmU+QTS9vhz2Sop46ebNCXpPPvsByK7hu0rFGRHL+AusKQJ7SoN+LbLh6APFpQwHKmDSwN35Z700Q==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/scope-manager": "7.0.2", + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/typescript-estree": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2", "debug": "^4.3.4" }, "engines": { @@ -3441,7 +3452,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -3450,13 +3461,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.0.2.tgz", + "integrity": "sha512-l6sa2jF3h+qgN2qUMjVR3uCNGjWw4ahGfzIYsCtFrQJCjhbrDPdiihYT8FnnqFwsWX+20hK592yX9I2rxKTP4g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -3467,13 +3478,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.0.2.tgz", + "integrity": "sha512-IKKDcFsKAYlk8Rs4wiFfEwJTQlHcdn8CLwLaxwd6zb8HNiMcQIFX9sWax2k4Cjj7l7mGS5N1zl7RCHOVwHq2VQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/typescript-estree": "7.0.2", + "@typescript-eslint/utils": "7.0.2", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -3485,7 +3496,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -3494,9 +3505,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.0.2.tgz", + "integrity": "sha512-ZzcCQHj4JaXFjdOql6adYV4B/oFOFjPOC9XYwCaZFRvqN8Llfvv4gSxrkQkd2u4Ci62i2c6W6gkDwQJDaRc4nA==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -3507,13 +3518,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.2.tgz", + "integrity": "sha512-3AMc8khTcELFWcKcPc0xiLviEvvfzATpdPj/DXuOGIdQIIFybf4DMT1vKRbuAEOFMwhWt7NFLXRkbjsvKZQyvw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -3559,17 +3570,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.0.2.tgz", + "integrity": "sha512-PZPIONBIB/X684bhT1XlrkjNZJIEevwkKDsdwfiu1WeqBxYEEdIgVDgm8/bbKHVu+6YOpeRqcfImTdImx/4Bsw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/scope-manager": "7.0.2", + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/typescript-estree": "7.0.2", "semver": "^7.5.4" }, "engines": { @@ -3580,16 +3591,16 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.2.tgz", + "integrity": "sha512-8Y+YiBmqPighbm5xA2k4wKTxRzx9EkBu7Rlw+WHqMvRJ3RPz/BMBO9b2ru0LUNmXg120PHUXD5+SWFy2R8DqlQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/types": "7.0.2", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -4269,9 +4280,9 @@ } }, "node_modules/batch-cluster": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/batch-cluster/-/batch-cluster-12.1.0.tgz", - "integrity": "sha512-whGyJU4tr7kyg2USByu0/51mML5HsLAeNz5s03kMDYZNsQsGgDJgI47RdY3r7MciCjPkTaTD5O4eOVqOfEO7pg==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/batch-cluster/-/batch-cluster-13.0.0.tgz", + "integrity": "sha512-EreW0Vi8TwovhYUHBXXRA5tthuU2ynGsZFlboyMJHCCUXYa2AjgwnE3ubBOJs2xJLcuXFJbi6c/8pH5+FVj8Og==", "engines": { "node": ">=14" } @@ -5493,14 +5504,14 @@ } }, "node_modules/dotenv": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.1.tgz", - "integrity": "sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==", + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/motdotla/dotenv?sponsor=1" + "url": "https://dotenvx.com" } }, "node_modules/dotenv-expand": { @@ -5987,34 +5998,34 @@ "dev": true }, "node_modules/exiftool-vendored": { - "version": "24.4.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-24.4.0.tgz", - "integrity": "sha512-n9GjZ+t0sD4mFGyxVCuyVKkyc4wDQraGE9XE3TbWqJBResbIggMBwcYeo9Q5oVBXe2K8EA/WraWDTqHN+C+52g==", + "version": "24.5.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-24.5.0.tgz", + "integrity": "sha512-uLGYfeshak3mYn2ucCsebXfNFdOpeAULlMb84wiJv+4B236n+ypgK/vr8bJgAcsIPSRJXFSz9WonvjjQYYqR3w==", "dependencies": { - "@photostructure/tz-lookup": "^9.0.0", - "@types/luxon": "^3.4.1", - "batch-cluster": "^12.1.0", + "@photostructure/tz-lookup": "^9.0.1", + "@types/luxon": "^3.4.2", + "batch-cluster": "^13.0.0", "he": "^1.2.0", "luxon": "^3.4.4" }, "optionalDependencies": { - "exiftool-vendored.exe": "12.73.0", - "exiftool-vendored.pl": "12.73.0" + "exiftool-vendored.exe": "12.76.0", + "exiftool-vendored.pl": "12.76.0" } }, "node_modules/exiftool-vendored.exe": { - "version": "12.73.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-12.73.0.tgz", - "integrity": "sha512-7za1Iv1hBnO92A+Yua04PHicCcwrP4edCzHaYnDOuea2DhmpIhqCgUUgrShcm4tG58ueRBa3GVvhRW/gCm8n4g==", + "version": "12.76.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-12.76.0.tgz", + "integrity": "sha512-lbKPPs31qpjnhFiMRaVxJX+iNcJ+p0NrRSFLHHaX6KTsfMba6e5i6NykSvU3wMiafzUTef1Fen3XQ+8n1tjjNw==", "optional": true, "os": [ "win32" ] }, "node_modules/exiftool-vendored.pl": { - "version": "12.73.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-12.73.0.tgz", - "integrity": "sha512-qX6kiGUTuQ/HFwuP3VJHU///BSwlaHSWm+yrDTHHQG+w+yvjFdtajDTJ96CUvPA/ecehtbTUMqCUz5xgMmHfBw==", + "version": "12.76.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-12.76.0.tgz", + "integrity": "sha512-4DxqgnvL71YziVoY27ZMgVfLAWDH3pQLljuV5+ffTnTPvz/BWeV+/bVFwRvDqCD3lkCWds0YfVcsycfJgbQ5fA==", "os": [ "!win32" ] @@ -8128,9 +8139,9 @@ } }, "node_modules/joi": { - "version": "17.12.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.12.1.tgz", - "integrity": "sha512-vtxmq+Lsc5SlfqotnfVjlViWfOL9nt/avKNbKYizwf6gsCfq9NYY/ceYRMFD8XDdrjJ9abJyScWmhmIiy+XRtQ==", + "version": "17.12.2", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.12.2.tgz", + "integrity": "sha512-RonXAIzCiHLc8ss3Ibuz45u28GOsWE1UpfDXLbN/9NKbL4tCJf8TWYVKsoYuuh+sAUt7fsSNpA+r2+TBA6Wjmw==", "dependencies": { "@hapi/hoek": "^9.3.0", "@hapi/topo": "^5.1.0", @@ -14095,9 +14106,9 @@ } }, "@nestjs/common": { - "version": "10.3.2", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.3.2.tgz", - "integrity": "sha512-yrtohmQlN5J/gFS/ui86SO+KIfUKeL39JPR3f/3AWgpz+duIfc9cGkfh7FGZQMfG9ZqXf7Zw+PRO9G+D4iEbPw==", + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.3.3.tgz", + "integrity": "sha512-LAkTe8/CF0uNWM0ecuDwUNTHCi1lVSITmmR4FQ6Ftz1E7ujQCnJ5pMRzd8JRN14vdBkxZZ8VbVF0BDUKoKNxMQ==", "requires": { "iterare": "1.2.1", "tslib": "2.6.2", @@ -14113,12 +14124,19 @@ "dotenv-expand": "10.0.0", "lodash": "4.17.21", "uuid": "9.0.1" + }, + "dependencies": { + "dotenv": { + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.1.tgz", + "integrity": "sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==" + } } }, "@nestjs/core": { - "version": "10.3.2", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.3.2.tgz", - "integrity": "sha512-JW3bQvDFY1gB+xXR6E5DzCdKftRszyWtd0YyDkdlKh1+44e2IGybFhSa5HcQBOiRqdVgPqAM5Vqc81rmhgeBnQ==", + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.3.3.tgz", + "integrity": "sha512-kxJWggQAPX3RuZx9JVec69eSLaYLNIox2emkZJpfBJ5Qq7cAq7edQIt1r4LGjTKq6kFubNTPsqhWf5y7yFRBPw==", "requires": { "@nuxtjs/opencollective": "0.3.2", "fast-safe-stringify": "2.1.1", @@ -14135,9 +14153,9 @@ "requires": {} }, "@nestjs/platform-express": { - "version": "10.3.2", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.3.2.tgz", - "integrity": "sha512-jS0uQ5VCsOezhzW6xzlWquGEaDHQ9B+JTVsuxbac+3XOPt6sbKry8HHOWyjKss9wAKo0lDRSFvpV5oQAF/yl6w==", + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.3.3.tgz", + "integrity": "sha512-GGKSEU48Os7nYFIsUM0nutuFUGn5AbeP8gzFBiBCAtiuJWrXZXpZ58pMBYxAbMf7IrcOZFInHEukjHGAQU0OZw==", "requires": { "body-parser": "1.20.2", "cors": "2.8.5", @@ -14147,9 +14165,9 @@ } }, "@nestjs/platform-socket.io": { - "version": "10.3.2", - "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.3.2.tgz", - "integrity": "sha512-BnU2tmH6cMSY6PUxen8QKmmLu7fZwe5b/IEx0kipBV9jGnidii+9VhW9sbdbZOQhrZVdGLByYEuaX7rdguq0lg==", + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.3.3.tgz", + "integrity": "sha512-QqM9BMTdYPvXOqx3oWrv130HOtc2krPvfgqgDsPWkBLfR+TssrA5QDaTW8HSjEQAfmugvHwhEAAU4+yXRl6tKg==", "requires": { "socket.io": "4.7.4", "tslib": "2.6.2" @@ -14199,9 +14217,9 @@ } }, "@nestjs/testing": { - "version": "10.3.2", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.3.2.tgz", - "integrity": "sha512-jetqEPqOPuxmhBinkizmJQg4UZ2IRFrUoMrBDSgg0ogQClokKjnLgkoC5de+Jfm2kub/VpqorHB0me8cCr5jEQ==", + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.3.3.tgz", + "integrity": "sha512-kX20GfjAImL5grd/i69uD/x7sc00BaqGcP2dRG3ilqshQUuy5DOmspLCr3a2C8xmVU7kzK4spT0oTxhe6WcCAA==", "dev": true, "requires": { "tslib": "2.6.2" @@ -14216,9 +14234,9 @@ } }, "@nestjs/websockets": { - "version": "10.3.2", - "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.3.2.tgz", - "integrity": "sha512-SMQM8hcRgngOabnluRLei46JUYvZ6j3DaZ2bDNYu57G3nlmkxXNktz7/pJG07NcetZL4CyF9pNyuICZql4Nhww==", + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.3.3.tgz", + "integrity": "sha512-cR5cB0bLS87vd0iu7Nud/4x2EH1Vs0aIgwGWd0eH/5SAw0rrDNU81PiOde+rnMXETbxvSVfOZuLRyn7/WQtGUg==", "requires": { "iterare": "1.2.1", "object-hash": "3.0.0", @@ -14262,9 +14280,9 @@ } }, "@photostructure/tz-lookup": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@photostructure/tz-lookup/-/tz-lookup-9.0.0.tgz", - "integrity": "sha512-gM3Xrs+XhD8ojDN0TgybuzSjsQb9UvF8j9DvR75E2zHlJQNiOztzILvfhVwadgA8JJbSMNzE+kYUnwP8aQnlXw==" + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@photostructure/tz-lookup/-/tz-lookup-9.0.1.tgz", + "integrity": "sha512-inMfhc1QVKheq/PHF0v2vRPnPzTljxscuOKK95o3VlZA4T4w4DeYIpu7dC4W1EyjUhfZJlCBUudpmnFGNCqTog==" }, "@pkgjs/parseargs": { "version": "0.11.0", @@ -14712,9 +14730,9 @@ } }, "@types/node": { - "version": "20.11.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", - "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", + "version": "20.11.20", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.20.tgz", + "integrity": "sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==", "requires": { "undici-types": "~5.26.4" } @@ -14801,9 +14819,9 @@ } }, "@types/semver": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.7.tgz", - "integrity": "sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "@types/send": { @@ -14918,16 +14936,16 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.0.2.tgz", + "integrity": "sha512-/XtVZJtbaphtdrWjr+CJclaCVGPtOdBpFEnvtNf/jRV0IiEemRrL0qABex/nEt8isYcnFacm3nPHYQwL+Wb7qg==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/scope-manager": "7.0.2", + "@typescript-eslint/type-utils": "7.0.2", + "@typescript-eslint/utils": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -14937,54 +14955,54 @@ } }, "@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.0.2.tgz", + "integrity": "sha512-GdwfDglCxSmU+QTS9vhz2Sop46ebNCXpPPvsByK7hu0rFGRHL+AusKQJ7SoN+LbLh6APFpQwHKmDSwN35Z700Q==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/scope-manager": "7.0.2", + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/typescript-estree": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.0.2.tgz", + "integrity": "sha512-l6sa2jF3h+qgN2qUMjVR3uCNGjWw4ahGfzIYsCtFrQJCjhbrDPdiihYT8FnnqFwsWX+20hK592yX9I2rxKTP4g==", "dev": true, "requires": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2" } }, "@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.0.2.tgz", + "integrity": "sha512-IKKDcFsKAYlk8Rs4wiFfEwJTQlHcdn8CLwLaxwd6zb8HNiMcQIFX9sWax2k4Cjj7l7mGS5N1zl7RCHOVwHq2VQ==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/typescript-estree": "7.0.2", + "@typescript-eslint/utils": "7.0.2", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.0.2.tgz", + "integrity": "sha512-ZzcCQHj4JaXFjdOql6adYV4B/oFOFjPOC9XYwCaZFRvqN8Llfvv4gSxrkQkd2u4Ci62i2c6W6gkDwQJDaRc4nA==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.2.tgz", + "integrity": "sha512-3AMc8khTcELFWcKcPc0xiLviEvvfzATpdPj/DXuOGIdQIIFybf4DMT1vKRbuAEOFMwhWt7NFLXRkbjsvKZQyvw==", "dev": true, "requires": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -15014,27 +15032,27 @@ } }, "@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.0.2.tgz", + "integrity": "sha512-PZPIONBIB/X684bhT1XlrkjNZJIEevwkKDsdwfiu1WeqBxYEEdIgVDgm8/bbKHVu+6YOpeRqcfImTdImx/4Bsw==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/scope-manager": "7.0.2", + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/typescript-estree": "7.0.2", "semver": "^7.5.4" } }, "@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.2.tgz", + "integrity": "sha512-8Y+YiBmqPighbm5xA2k4wKTxRzx9EkBu7Rlw+WHqMvRJ3RPz/BMBO9b2ru0LUNmXg120PHUXD5+SWFy2R8DqlQ==", "dev": true, "requires": { - "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/types": "7.0.2", "eslint-visitor-keys": "^3.4.1" } }, @@ -15583,9 +15601,9 @@ "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" }, "batch-cluster": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/batch-cluster/-/batch-cluster-12.1.0.tgz", - "integrity": "sha512-whGyJU4tr7kyg2USByu0/51mML5HsLAeNz5s03kMDYZNsQsGgDJgI47RdY3r7MciCjPkTaTD5O4eOVqOfEO7pg==" + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/batch-cluster/-/batch-cluster-13.0.0.tgz", + "integrity": "sha512-EreW0Vi8TwovhYUHBXXRA5tthuU2ynGsZFlboyMJHCCUXYa2AjgwnE3ubBOJs2xJLcuXFJbi6c/8pH5+FVj8Og==" }, "bcrypt": { "version": "5.1.1", @@ -16476,9 +16494,9 @@ } }, "dotenv": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.1.tgz", - "integrity": "sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==" + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==" }, "dotenv-expand": { "version": "10.0.0", @@ -16823,15 +16841,15 @@ } }, "exiftool-vendored": { - "version": "24.4.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-24.4.0.tgz", - "integrity": "sha512-n9GjZ+t0sD4mFGyxVCuyVKkyc4wDQraGE9XE3TbWqJBResbIggMBwcYeo9Q5oVBXe2K8EA/WraWDTqHN+C+52g==", + "version": "24.5.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-24.5.0.tgz", + "integrity": "sha512-uLGYfeshak3mYn2ucCsebXfNFdOpeAULlMb84wiJv+4B236n+ypgK/vr8bJgAcsIPSRJXFSz9WonvjjQYYqR3w==", "requires": { - "@photostructure/tz-lookup": "^9.0.0", - "@types/luxon": "^3.4.1", - "batch-cluster": "^12.1.0", - "exiftool-vendored.exe": "12.73.0", - "exiftool-vendored.pl": "12.73.0", + "@photostructure/tz-lookup": "^9.0.1", + "@types/luxon": "^3.4.2", + "batch-cluster": "^13.0.0", + "exiftool-vendored.exe": "12.76.0", + "exiftool-vendored.pl": "12.76.0", "he": "^1.2.0", "luxon": "^3.4.4" }, @@ -16844,15 +16862,15 @@ } }, "exiftool-vendored.exe": { - "version": "12.73.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-12.73.0.tgz", - "integrity": "sha512-7za1Iv1hBnO92A+Yua04PHicCcwrP4edCzHaYnDOuea2DhmpIhqCgUUgrShcm4tG58ueRBa3GVvhRW/gCm8n4g==", + "version": "12.76.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-12.76.0.tgz", + "integrity": "sha512-lbKPPs31qpjnhFiMRaVxJX+iNcJ+p0NrRSFLHHaX6KTsfMba6e5i6NykSvU3wMiafzUTef1Fen3XQ+8n1tjjNw==", "optional": true }, "exiftool-vendored.pl": { - "version": "12.73.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-12.73.0.tgz", - "integrity": "sha512-qX6kiGUTuQ/HFwuP3VJHU///BSwlaHSWm+yrDTHHQG+w+yvjFdtajDTJ96CUvPA/ecehtbTUMqCUz5xgMmHfBw==" + "version": "12.76.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-12.76.0.tgz", + "integrity": "sha512-4DxqgnvL71YziVoY27ZMgVfLAWDH3pQLljuV5+ffTnTPvz/BWeV+/bVFwRvDqCD3lkCWds0YfVcsycfJgbQ5fA==" }, "exit": { "version": "0.1.2", @@ -18435,9 +18453,9 @@ } }, "joi": { - "version": "17.12.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.12.1.tgz", - "integrity": "sha512-vtxmq+Lsc5SlfqotnfVjlViWfOL9nt/avKNbKYizwf6gsCfq9NYY/ceYRMFD8XDdrjJ9abJyScWmhmIiy+XRtQ==", + "version": "17.12.2", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.12.2.tgz", + "integrity": "sha512-RonXAIzCiHLc8ss3Ibuz45u28GOsWE1UpfDXLbN/9NKbL4tCJf8TWYVKsoYuuh+sAUt7fsSNpA+r2+TBA6Wjmw==", "requires": { "@hapi/hoek": "^9.3.0", "@hapi/topo": "^5.1.0", diff --git a/server/package.json b/server/package.json index 7ed2e551d3..d09854dede 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "immich", - "version": "1.94.1", + "version": "1.97.0", "description": "", "author": "", "private": true, @@ -56,8 +56,8 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "cookie-parser": "^1.4.6", - "exiftool-vendored": "~24.4.0", - "exiftool-vendored.pl": "12.73", + "exiftool-vendored": "~24.5.0", + "exiftool-vendored.pl": "12.76", "fluent-ffmpeg": "^2.1.2", "geo-tz": "^8.0.0", "glob": "^10.3.3", @@ -101,8 +101,8 @@ "@types/sharp": "^0.31.1", "@types/supertest": "^6.0.0", "@types/ua-parser-js": "^0.7.36", - "@typescript-eslint/eslint-plugin": "^6.4.1", - "@typescript-eslint/parser": "^6.4.1", + "@typescript-eslint/eslint-plugin": "^7.0.0", + "@typescript-eslint/parser": "^7.0.0", "dotenv": "^16.3.1", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", @@ -161,6 +161,6 @@ "globalSetup": "/test/global-setup.js" }, "volta": { - "node": "20.11.0" + "node": "20.11.1" } } diff --git a/server/src/domain/album/album-response.dto.ts b/server/src/domain/album/album-response.dto.ts index 1a266abac4..168b385928 100644 --- a/server/src/domain/album/album-response.dto.ts +++ b/server/src/domain/album/album-response.dto.ts @@ -1,6 +1,7 @@ import { AlbumEntity } from '@app/infra/entities'; import { ApiProperty } from '@nestjs/swagger'; import { AssetResponseDto, mapAsset } from '../asset'; +import { AuthDto } from '../auth/auth.dto'; import { UserResponseDto, mapUser } from '../user'; export class AlbumResponseDto { @@ -24,7 +25,7 @@ export class AlbumResponseDto { isActivityEnabled!: boolean; } -export const mapAlbum = (entity: AlbumEntity, withAssets: boolean): AlbumResponseDto => { +export const mapAlbum = (entity: AlbumEntity, withAssets: boolean, auth?: AuthDto): AlbumResponseDto => { const sharedUsers: UserResponseDto[] = []; if (entity.sharedUsers) { @@ -59,7 +60,7 @@ export const mapAlbum = (entity: AlbumEntity, withAssets: boolean): AlbumRespons hasSharedLink, startDate, endDate, - assets: (withAssets ? assets : []).map((asset) => mapAsset(asset)), + assets: (withAssets ? assets : []).map((asset) => mapAsset(asset, { auth })), assetCount: entity.assets?.length || 0, isActivityEnabled: entity.isActivityEnabled, }; diff --git a/server/src/domain/album/album.service.ts b/server/src/domain/album/album.service.ts index b14779a802..9a7b940f77 100644 --- a/server/src/domain/album/album.service.ts +++ b/server/src/domain/album/album.service.ts @@ -104,7 +104,7 @@ export class AlbumService { const [albumMetadataForIds] = await this.albumRepository.getMetadataForIds([album.id]); return { - ...mapAlbum(album, withAssets), + ...mapAlbum(album, withAssets, auth), startDate: albumMetadataForIds.startDate, endDate: albumMetadataForIds.endDate, assetCount: albumMetadataForIds.assetCount, diff --git a/server/src/domain/asset/asset.service.spec.ts b/server/src/domain/asset/asset.service.spec.ts index a6b2cde3e8..9f077835a5 100644 --- a/server/src/domain/asset/asset.service.spec.ts +++ b/server/src/domain/asset/asset.service.spec.ts @@ -286,6 +286,7 @@ describe(AssetService.name, () => { describe('getMapMarkers', () => { it('should get geo information of assets', async () => { + partnerMock.getAll.mockResolvedValue([]); assetMock.getMapMarkers.mockResolvedValue( [assetStub.withLocation].map((asset) => ({ id: asset.id, @@ -705,7 +706,7 @@ describe(AssetService.name, () => { stackParentId: 'parent', }); - expect(communicationMock.send).toHaveBeenCalledWith(ClientEvent.ASSET_UPDATE, authStub.user1.user.id, [ + expect(communicationMock.send).toHaveBeenCalledWith(ClientEvent.ASSET_STACK_UPDATE, authStub.user1.user.id, [ 'asset-1', 'parent', ]); diff --git a/server/src/domain/asset/asset.service.ts b/server/src/domain/asset/asset.service.ts index d46678b8b4..dd54085060 100644 --- a/server/src/domain/asset/asset.service.ts +++ b/server/src/domain/asset/asset.service.ts @@ -157,8 +157,16 @@ export class AssetService { return folder; } - getMapMarkers(auth: AuthDto, options: MapMarkerDto): Promise { - return this.assetRepository.getMapMarkers(auth.user.id, options); + async getMapMarkers(auth: AuthDto, options: MapMarkerDto): Promise { + const userIds: string[] = [auth.user.id]; + if (options.withPartners) { + const partners = await this.partnerRepository.getAll(auth.user.id); + const partnersIds = partners + .filter((partner) => partner.sharedBy && partner.sharedWith && partner.sharedById != auth.user.id) + .map((partner) => partner.sharedById); + userIds.push(...partnersIds); + } + return this.assetRepository.getMapMarkers(userIds, options); } async getMemoryLane(auth: AuthDto, dto: MemoryLaneDto): Promise { @@ -172,7 +180,7 @@ export class AssetService { return { title: `${years} year${years > 1 ? 's' : ''} since...`, - asset: mapAsset(asset), + asset: mapAsset(asset, { auth }), }; }) .groupBy((asset) => asset.title) @@ -222,8 +230,8 @@ export class AssetService { const timeBucketOptions = await this.buildTimeBucketOptions(auth, dto); const assets = await this.assetRepository.getTimeBucket(dto.timeBucket, timeBucketOptions); return !auth.sharedLink || auth.sharedLink?.showExif - ? assets.map((asset) => mapAsset(asset, { withStack: true })) - : assets.map((asset) => mapAsset(asset, { stripMetadata: true })); + ? assets.map((asset) => mapAsset(asset, { withStack: true, auth })) + : assets.map((asset) => mapAsset(asset, { stripMetadata: true, auth })); } async buildTimeBucketOptions(auth: AuthDto, dto: TimeBucketDto): Promise { @@ -253,7 +261,7 @@ export class AssetService { async getRandom(auth: AuthDto, count: number): Promise { const assets = await this.assetRepository.getRandom(auth.user.id, count); - return assets.map((a) => mapAsset(a)); + return assets.map((a) => mapAsset(a, { auth })); } async getUserAssetsByDeviceId(auth: AuthDto, deviceId: string) { @@ -284,10 +292,10 @@ export class AssetService { } if (auth.sharedLink && !auth.sharedLink.showExif) { - return mapAsset(asset, { stripMetadata: true, withStack: true }); + return mapAsset(asset, { stripMetadata: true, withStack: true, auth }); } - const data = mapAsset(asset, { withStack: true }); + const data = mapAsset(asset, { withStack: true, auth }); if (auth.sharedLink) { delete data.owner; @@ -307,7 +315,7 @@ export class AssetService { await this.updateMetadata({ id, description, dateTimeOriginal, latitude, longitude }); const asset = await this.assetRepository.save({ id, ...rest }); - return mapAsset(asset); + return mapAsset(asset, { auth }); } async updateAll(auth: AuthDto, dto: AssetBulkUpdateDto): Promise { @@ -318,7 +326,7 @@ export class AssetService { const stackIdsToCheckForDelete: string[] = []; if (removeParent) { (options as Partial).stack = null; - const assets = await this.assetRepository.getByIds(ids); + const assets = await this.assetRepository.getByIds(ids, { stack: true }); stackIdsToCheckForDelete.push(...new Set(assets.filter((a) => !!a.stackId).map((a) => a.stackId!))); // This updates the updatedAt column of the parents to indicate that one of its children is removed // All the unique parent's -> parent is set to null @@ -373,7 +381,7 @@ export class AssetService { .flatMap((stack) => (stack ? [stack] : [])) .filter((stack) => stack.assets.length < 2); await Promise.all(stacksToDelete.map((as) => this.assetStackRepository.delete(as.id))); - this.communicationRepository.send(ClientEvent.ASSET_UPDATE, auth.user.id, ids); + this.communicationRepository.send(ClientEvent.ASSET_STACK_UPDATE, auth.user.id, ids); } async handleAssetDeletionCheck() { @@ -491,7 +499,11 @@ export class AssetService { primaryAssetId: newParentId, }); - this.communicationRepository.send(ClientEvent.ASSET_UPDATE, auth.user.id, [...childIds, newParentId, oldParentId]); + this.communicationRepository.send(ClientEvent.ASSET_STACK_UPDATE, auth.user.id, [ + ...childIds, + newParentId, + oldParentId, + ]); await this.assetRepository.updateAll([oldParentId, newParentId, ...childIds], { updatedAt: new Date() }); } diff --git a/server/src/domain/asset/dto/map-marker.dto.ts b/server/src/domain/asset/dto/map-marker.dto.ts index 79bb9fa385..b703d6e73e 100644 --- a/server/src/domain/asset/dto/map-marker.dto.ts +++ b/server/src/domain/asset/dto/map-marker.dto.ts @@ -25,4 +25,10 @@ export class MapMarkerDto { @IsDate() @Type(() => Date) fileCreatedBefore?: Date; + + @ApiProperty() + @Optional() + @IsBoolean() + @Transform(toBoolean) + withPartners?: boolean; } diff --git a/server/src/domain/asset/response-dto/asset-response.dto.ts b/server/src/domain/asset/response-dto/asset-response.dto.ts index 94a9f8a42d..2961a9dcc7 100644 --- a/server/src/domain/asset/response-dto/asset-response.dto.ts +++ b/server/src/domain/asset/response-dto/asset-response.dto.ts @@ -1,3 +1,4 @@ +import { AuthDto } from '@app/domain/auth/auth.dto'; import { AssetEntity, AssetFaceEntity, AssetType } from '@app/infra/entities'; import { ApiProperty } from '@nestjs/swagger'; import { PersonWithFacesResponseDto, mapFacesWithoutPerson, mapPerson } from '../../person/person.dto'; @@ -50,6 +51,7 @@ export class AssetResponseDto extends SanitizedAssetResponseDto { export type AssetMapOptions = { stripMetadata?: boolean; withStack?: boolean; + auth?: AuthDto; }; const peopleWithFaces = (faces: AssetFaceEntity[]): PersonWithFacesResponseDto[] => { @@ -73,23 +75,21 @@ const peopleWithFaces = (faces: AssetFaceEntity[]): PersonWithFacesResponseDto[] export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): AssetResponseDto { const { stripMetadata = false, withStack = false } = options; - const sanitizedAssetResponse: SanitizedAssetResponseDto = { - id: entity.id, - type: entity.type, - thumbhash: entity.thumbhash?.toString('base64') ?? null, - localDateTime: entity.localDateTime, - resized: !!entity.resizePath, - duration: entity.duration ?? '0:00:00.00000', - livePhotoVideoId: entity.livePhotoVideoId, - hasMetadata: false, - }; - if (stripMetadata) { + const sanitizedAssetResponse: SanitizedAssetResponseDto = { + id: entity.id, + type: entity.type, + thumbhash: entity.thumbhash?.toString('base64') ?? null, + localDateTime: entity.localDateTime, + resized: !!entity.resizePath, + duration: entity.duration ?? '0:00:00.00000', + livePhotoVideoId: entity.livePhotoVideoId, + hasMetadata: false, + }; return sanitizedAssetResponse as AssetResponseDto; } return { - ...sanitizedAssetResponse, id: entity.id, deviceAssetId: entity.deviceAssetId, ownerId: entity.ownerId, @@ -105,8 +105,8 @@ export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): As fileModifiedAt: entity.fileModifiedAt, localDateTime: entity.localDateTime, updatedAt: entity.updatedAt, - isFavorite: entity.isFavorite, - isArchived: entity.isArchived, + isFavorite: options.auth?.user.id === entity.ownerId ? entity.isFavorite : false, + isArchived: options.auth?.user.id === entity.ownerId ? entity.isArchived : false, isTrashed: !!entity.deletedAt, duration: entity.duration ?? '0:00:00.00000', exifInfo: entity.exifInfo ? mapExif(entity.exifInfo) : undefined, @@ -119,7 +119,7 @@ export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): As stack: withStack ? entity.stack?.assets .filter((a) => a.id !== entity.stack?.primaryAssetId) - .map((a) => mapAsset(a, { stripMetadata })) + .map((a) => mapAsset(a, { stripMetadata, auth: options.auth })) : undefined, stackCount: entity.stack?.assets?.length ?? null, isExternal: entity.isExternal, diff --git a/server/src/domain/audit/audit.service.ts b/server/src/domain/audit/audit.service.ts index 887b72e2cd..a7c003fad6 100644 --- a/server/src/domain/audit/audit.service.ts +++ b/server/src/domain/audit/audit.service.ts @@ -167,7 +167,7 @@ export class AuditService { `Found ${libraryFiles.size} original files, ${thumbFiles.size} thumbnails, ${videoFiles.size} encoded videos, ${profileFiles.size} profile files`, ); const pagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (options) => - this.assetRepository.getAll(options, { withDeleted: true }), + this.assetRepository.getAll(options, { withDeleted: true, withArchived: true }), ); let assetCount = 0; diff --git a/server/src/domain/auth/auth.service.spec.ts b/server/src/domain/auth/auth.service.spec.ts index 8ebb75857a..359b28a00c 100644 --- a/server/src/domain/auth/auth.service.spec.ts +++ b/server/src/domain/auth/auth.service.spec.ts @@ -52,6 +52,14 @@ const fixtures = { }, }; +const oauthUserWithDefaultQuota = { + email: email, + name: ' ', + oauthId: sub, + quotaSizeInBytes: 1_073_741_824, + storageLabel: null, +}; + describe('AuthService', () => { let sut: AuthService; let accessMock: jest.Mocked; @@ -62,14 +70,13 @@ describe('AuthService', () => { let userTokenMock: jest.Mocked; let shareMock: jest.Mocked; let keyMock: jest.Mocked; - let callbackMock: jest.Mock; - afterEach(() => { - jest.resetModules(); - }); + let callbackMock: jest.Mock; + let userinfoMock: jest.Mock; beforeEach(async () => { callbackMock = jest.fn().mockReturnValue({ access_token: 'access-token' }); + userinfoMock = jest.fn().mockResolvedValue({ sub, email }); jest.spyOn(generators, 'state').mockReturnValue('state'); jest.spyOn(Issuer, 'discover').mockResolvedValue({ @@ -83,7 +90,7 @@ describe('AuthService', () => { authorizationUrl: jest.fn().mockReturnValue('http://authorization-url'), callbackParams: jest.fn().mockReturnValue({ state: 'state' }), callback: callbackMock, - userinfo: jest.fn().mockResolvedValue({ sub, email }), + userinfo: userinfoMock, }), } as any); @@ -491,6 +498,87 @@ describe('AuthService', () => { expect(callbackMock).toHaveBeenCalledWith('http://mobile-redirect', { state: 'state' }, { state: 'state' }); }); + + it('should use the default quota', async () => { + configMock.load.mockResolvedValue(systemConfigStub.withDefaultStorageQuota); + userMock.getByEmail.mockResolvedValue(null); + userMock.getAdmin.mockResolvedValue(userStub.user1); + userMock.create.mockResolvedValue(userStub.user1); + + await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( + loginResponseStub.user1oauth, + ); + + expect(userMock.create).toHaveBeenCalledWith(oauthUserWithDefaultQuota); + }); + + it('should ignore an invalid storage quota', async () => { + configMock.load.mockResolvedValue(systemConfigStub.withDefaultStorageQuota); + userMock.getByEmail.mockResolvedValue(null); + userMock.getAdmin.mockResolvedValue(userStub.user1); + userMock.create.mockResolvedValue(userStub.user1); + userinfoMock.mockResolvedValue({ sub, email, immich_quota: 'abc' }); + + await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( + loginResponseStub.user1oauth, + ); + + expect(userMock.create).toHaveBeenCalledWith(oauthUserWithDefaultQuota); + }); + + it('should ignore a negative quota', async () => { + configMock.load.mockResolvedValue(systemConfigStub.withDefaultStorageQuota); + userMock.getByEmail.mockResolvedValue(null); + userMock.getAdmin.mockResolvedValue(userStub.user1); + userMock.create.mockResolvedValue(userStub.user1); + userinfoMock.mockResolvedValue({ sub, email, immich_quota: -5 }); + + await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( + loginResponseStub.user1oauth, + ); + + expect(userMock.create).toHaveBeenCalledWith(oauthUserWithDefaultQuota); + }); + + it('should not set quota for 0 quota', async () => { + configMock.load.mockResolvedValue(systemConfigStub.withDefaultStorageQuota); + userMock.getByEmail.mockResolvedValue(null); + userMock.getAdmin.mockResolvedValue(userStub.user1); + userMock.create.mockResolvedValue(userStub.user1); + userinfoMock.mockResolvedValue({ sub, email, immich_quota: 0 }); + + await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( + loginResponseStub.user1oauth, + ); + + expect(userMock.create).toHaveBeenCalledWith({ + email: email, + name: ' ', + oauthId: sub, + quotaSizeInBytes: null, + storageLabel: null, + }); + }); + + it('should use a valid storage quota', async () => { + configMock.load.mockResolvedValue(systemConfigStub.withDefaultStorageQuota); + userMock.getByEmail.mockResolvedValue(null); + userMock.getAdmin.mockResolvedValue(userStub.user1); + userMock.create.mockResolvedValue(userStub.user1); + userinfoMock.mockResolvedValue({ sub, email, immich_quota: 5 }); + + await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( + loginResponseStub.user1oauth, + ); + + expect(userMock.create).toHaveBeenCalledWith({ + email: email, + name: ' ', + oauthId: sub, + quotaSizeInBytes: 5_368_709_120, + storageLabel: null, + }); + }); }); describe('link', () => { diff --git a/server/src/domain/auth/auth.service.ts b/server/src/domain/auth/auth.service.ts index a2c0d2df93..fe01ef39b6 100644 --- a/server/src/domain/auth/auth.service.ts +++ b/server/src/domain/auth/auth.service.ts @@ -7,11 +7,13 @@ import { InternalServerErrorException, UnauthorizedException, } from '@nestjs/common'; +import { isNumber, isString } from 'class-validator'; import cookieParser from 'cookie'; import { DateTime } from 'luxon'; import { IncomingHttpHeaders } from 'node:http'; import { ClientMetadata, Issuer, UserinfoResponse, custom, generators } from 'openid-client'; import { AccessCore, Permission } from '../access'; +import { HumanReadableSize } from '../domain.util'; import { IAccessRepository, ICryptoRepository, @@ -64,6 +66,12 @@ interface OAuthProfile extends UserinfoResponse { email: string; } +interface ClaimOptions { + key: string; + default: T; + isValid: (value: unknown) => boolean; +} + @Injectable() export class AuthService { private access: AccessCore; @@ -234,9 +242,11 @@ export class AuthService { } } + const { autoRegister, defaultStorageQuota, storageLabelClaim, storageQuotaClaim } = config.oauth; + // register new user if (!user) { - if (!config.oauth.autoRegister) { + if (!autoRegister) { this.logger.warn( `Unable to register ${profile.email}. To enable set OAuth Auto Register to true in admin settings.`, ); @@ -246,17 +256,24 @@ export class AuthService { this.logger.log(`Registering new user: ${profile.email}/${profile.sub}`); this.logger.verbose(`OAuth Profile: ${JSON.stringify(profile)}`); - let storageLabel: string | null = profile[config.oauth.storageLabelClaim as keyof OAuthProfile] as string; - if (typeof storageLabel !== 'string') { - storageLabel = null; - } + const storageLabel = this.getClaim(profile, { + key: storageLabelClaim, + default: '', + isValid: isString, + }); + const storageQuota = this.getClaim(profile, { + key: storageQuotaClaim, + default: defaultStorageQuota, + isValid: (value: unknown) => isNumber(value) && value >= 0, + }); const userName = profile.name ?? `${profile.given_name || ''} ${profile.family_name || ''}`; user = await this.userCore.createUser({ name: userName, email: profile.email, oauthId: profile.sub, - storageLabel, + quotaSizeInBytes: storageQuota * HumanReadableSize.GiB || null, + storageLabel: storageLabel || null, }); } @@ -443,4 +460,9 @@ export class AuthService { } return [accessTokenCookie, authTypeCookie, isAuthenticatedCookie]; } + + private getClaim(profile: OAuthProfile, options: ClaimOptions): T { + const value = profile[options.key as keyof OAuthProfile]; + return options.isValid(value) ? (value as T) : options.default; + } } diff --git a/server/src/domain/database/database.service.ts b/server/src/domain/database/database.service.ts index 5ea9e1a474..d697d032b3 100644 --- a/server/src/domain/database/database.service.ts +++ b/server/src/domain/database/database.service.ts @@ -72,7 +72,7 @@ export class DatabaseService { In this case, please run 'CREATE EXTENSION IF NOT EXISTS ${this.vectorExt}' manually as a superuser. See https://immich.app/docs/guides/database-queries for how to query the database. - Alternatively, if your Postgres instance has ${extName[otherExt]}, you may use this instead by setting the environment variable 'VECTOR_EXTENSION=${otherExt}'. + Alternatively, if your Postgres instance has ${extName[otherExt]}, you may use this instead by setting the environment variable 'DB_VECTOR_EXTENSION=${extName[otherExt]}'. Note that switching between the two extensions after a successful startup is not supported. The exception is if your version of Immich prior to upgrading was 1.90.2 or earlier. In this case, you may set either extension now, but you will not be able to switch to the other extension following a successful startup. diff --git a/server/src/domain/domain.config.ts b/server/src/domain/domain.config.ts index ed1283ec2f..b77fd74126 100644 --- a/server/src/domain/domain.config.ts +++ b/server/src/domain/domain.config.ts @@ -18,12 +18,12 @@ export const immichAppConfig: ConfigModuleOptions = { DB_PASSWORD: WHEN_DB_URL_SET, DB_DATABASE_NAME: WHEN_DB_URL_SET, DB_URL: Joi.string().optional(), + DB_VECTOR_EXTENSION: Joi.string().optional().valid('pgvector', 'pgvecto.rs').default('pgvecto.rs'), LOG_LEVEL: Joi.string() .optional() .valid(...Object.values(LogLevel)), MACHINE_LEARNING_PORT: Joi.number().optional(), MICROSERVICES_PORT: Joi.number().optional(), SERVER_PORT: Joi.number().optional(), - VECTOR_EXTENSION: Joi.string().optional().valid('pgvector', 'pgvecto.rs').default('pgvecto.rs'), }), }; diff --git a/server/src/domain/domain.constant.ts b/server/src/domain/domain.constant.ts index 4e7c4d5524..0dc9c54140 100644 --- a/server/src/domain/domain.constant.ts +++ b/server/src/domain/domain.constant.ts @@ -91,7 +91,7 @@ export const citiesFile = 'cities500.txt'; export const geodataDatePath = join(GEODATA_ROOT_PATH, 'geodata-date.txt'); export const geodataAdmin1Path = join(GEODATA_ROOT_PATH, 'admin1CodesASCII.txt'); export const geodataAdmin2Path = join(GEODATA_ROOT_PATH, 'admin2Codes.txt'); -export const geodataCitites500Path = join(GEODATA_ROOT_PATH, citiesFile); +export const geodataCities500Path = join(GEODATA_ROOT_PATH, citiesFile); const image: Record = { '.3fr': ['image/3fr', 'image/x-hasselblad-3fr'], diff --git a/server/src/domain/job/job.service.spec.ts b/server/src/domain/job/job.service.spec.ts index 58cda724f8..5a4d26b3c4 100644 --- a/server/src/domain/job/job.service.spec.ts +++ b/server/src/domain/job/job.service.spec.ts @@ -286,12 +286,7 @@ describe(JobService.name, () => { }, { item: { name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: 'asset-1' } }, - jobs: [ - JobName.GENERATE_WEBP_THUMBNAIL, - JobName.GENERATE_THUMBHASH_THUMBNAIL, - JobName.SMART_SEARCH, - JobName.FACE_DETECTION, - ], + jobs: [JobName.GENERATE_WEBP_THUMBNAIL, JobName.GENERATE_THUMBHASH_THUMBNAIL], }, { item: { name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: 'asset-1', source: 'upload' } }, diff --git a/server/src/domain/job/job.service.ts b/server/src/domain/job/job.service.ts index 25055f1f31..1010185b2c 100644 --- a/server/src/domain/job/job.service.ts +++ b/server/src/domain/job/job.service.ts @@ -248,16 +248,18 @@ export class JobService { const jobs: JobItem[] = [ { name: JobName.GENERATE_WEBP_THUMBNAIL, data: item.data }, { name: JobName.GENERATE_THUMBHASH_THUMBNAIL, data: item.data }, - { name: JobName.SMART_SEARCH, data: item.data }, - { name: JobName.FACE_DETECTION, data: item.data }, ]; - const [asset] = await this.assetRepository.getByIds([item.data.id]); - if (asset) { - if (asset.type === AssetType.VIDEO) { - jobs.push({ name: JobName.VIDEO_CONVERSION, data: item.data }); - } else if (asset.livePhotoVideoId) { - jobs.push({ name: JobName.VIDEO_CONVERSION, data: { id: asset.livePhotoVideoId } }); + if (item.data.source === 'upload') { + jobs.push({ name: JobName.SMART_SEARCH, data: item.data }, { name: JobName.FACE_DETECTION, data: item.data }); + + const [asset] = await this.assetRepository.getByIds([item.data.id]); + if (asset) { + if (asset.type === AssetType.VIDEO) { + jobs.push({ name: JobName.VIDEO_CONVERSION, data: item.data }); + } else if (asset.livePhotoVideoId) { + jobs.push({ name: JobName.VIDEO_CONVERSION, data: { id: asset.livePhotoVideoId } }); + } } } diff --git a/server/src/domain/library/library.dto.ts b/server/src/domain/library/library.dto.ts index 638841ec63..b57d56e7b2 100644 --- a/server/src/domain/library/library.dto.ts +++ b/server/src/domain/library/library.dto.ts @@ -1,59 +1,66 @@ import { LibraryEntity, LibraryType } from '@app/infra/entities'; import { ApiProperty } from '@nestjs/swagger'; -import { ArrayUnique, IsBoolean, IsEnum, IsNotEmpty, IsOptional, IsString } from 'class-validator'; -import { ValidateUUID } from '../domain.util'; +import { ArrayMaxSize, ArrayUnique, IsBoolean, IsEnum, IsNotEmpty, IsString } from 'class-validator'; +import { Optional, ValidateUUID } from '../domain.util'; export class CreateLibraryDto { @IsEnum(LibraryType) @ApiProperty({ enumName: 'LibraryType', enum: LibraryType }) type!: LibraryType; + @ValidateUUID({ optional: true }) + ownerId?: string; + @IsString() - @IsOptional() + @Optional() @IsNotEmpty() name?: string; - @IsOptional() + @Optional() @IsBoolean() isVisible?: boolean; - @IsOptional() + @Optional() @IsString({ each: true }) @IsNotEmpty({ each: true }) @ArrayUnique() + @ArrayMaxSize(128) importPaths?: string[]; - @IsOptional() + @Optional() @IsString({ each: true }) @IsNotEmpty({ each: true }) @ArrayUnique() + @ArrayMaxSize(128) exclusionPatterns?: string[]; - @IsOptional() + @Optional() @IsBoolean() isWatched?: boolean; } export class UpdateLibraryDto { - @IsOptional() + @Optional() @IsString() @IsNotEmpty() name?: string; - @IsOptional() + @Optional() @IsBoolean() isVisible?: boolean; - @IsOptional() + @Optional() @IsString({ each: true }) @IsNotEmpty({ each: true }) @ArrayUnique() + @ArrayMaxSize(128) importPaths?: string[]; - @IsOptional() + @Optional() @IsNotEmpty({ each: true }) @IsString({ each: true }) @ArrayUnique() + @ArrayMaxSize(128) exclusionPatterns?: string[]; } @@ -63,6 +70,32 @@ export class CrawlOptionsDto { exclusionPatterns?: string[]; } +export class ValidateLibraryDto { + @Optional() + @IsString({ each: true }) + @IsNotEmpty({ each: true }) + @ArrayUnique() + @ArrayMaxSize(128) + importPaths?: string[]; + + @Optional() + @IsNotEmpty({ each: true }) + @IsString({ each: true }) + @ArrayUnique() + @ArrayMaxSize(128) + exclusionPatterns?: string[]; +} + +export class ValidateLibraryResponseDto { + importPaths?: ValidateLibraryImportPathResponseDto[]; +} + +export class ValidateLibraryImportPathResponseDto { + importPath!: string; + isValid?: boolean = false; + message?: string; +} + export class LibrarySearchDto { @ValidateUUID({ optional: true }) userId?: string; @@ -70,14 +103,21 @@ export class LibrarySearchDto { export class ScanLibraryDto { @IsBoolean() - @IsOptional() + @Optional() refreshModifiedFiles?: boolean; @IsBoolean() - @IsOptional() + @Optional() refreshAllFiles?: boolean = false; } +export class SearchLibraryDto { + @IsEnum(LibraryType) + @ApiProperty({ enumName: 'LibraryType', enum: LibraryType }) + @Optional() + type?: LibraryType; +} + export class LibraryResponseDto { id!: string; ownerId!: string; diff --git a/server/src/domain/library/library.service.spec.ts b/server/src/domain/library/library.service.spec.ts index c86aa4a4e7..ba1dd8374b 100644 --- a/server/src/domain/library/library.service.spec.ts +++ b/server/src/domain/library/library.service.spec.ts @@ -54,12 +54,6 @@ describe(LibraryService.name, () => { cryptoMock = newCryptoRepositoryMock(); storageMock = newStorageRepositoryMock(); - storageMock.stat.mockResolvedValue({ - size: 100, - mtime: new Date('2023-01-01'), - ctime: new Date('2023-01-01'), - } as Stats); - // Always validate owner access for library. accessMock.library.checkOwnerAccess.mockImplementation(async (_, libraryIds) => libraryIds); @@ -146,24 +140,6 @@ describe(LibraryService.name, () => { }); describe('handleQueueAssetRefresh', () => { - it("should not queue assets outside of user's external path", async () => { - const mockLibraryJob: ILibraryRefreshJob = { - id: libraryStub.externalLibrary1.id, - refreshModifiedFiles: false, - refreshAllFiles: false, - }; - - libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1); - storageMock.crawl.mockResolvedValue(['/data/user2/photo.jpg']); - assetMock.getByLibraryId.mockResolvedValue([]); - libraryMock.getOnlineAssetPaths.mockResolvedValue([]); - userMock.get.mockResolvedValue(userStub.externalPath1); - - await sut.handleQueueAssetRefresh(mockLibraryJob); - - expect(jobMock.queue.mock.calls).toEqual([]); - }); - it('should queue new assets', async () => { const mockLibraryJob: ILibraryRefreshJob = { id: libraryStub.externalLibrary1.id, @@ -174,8 +150,7 @@ describe(LibraryService.name, () => { libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1); storageMock.crawl.mockResolvedValue(['/data/user1/photo.jpg']); assetMock.getByLibraryId.mockResolvedValue([]); - libraryMock.getOnlineAssetPaths.mockResolvedValue([]); - userMock.get.mockResolvedValue(userStub.externalPath1); + userMock.get.mockResolvedValue(userStub.admin); await sut.handleQueueAssetRefresh(mockLibraryJob); @@ -202,8 +177,7 @@ describe(LibraryService.name, () => { libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1); storageMock.crawl.mockResolvedValue(['/data/user1/photo.jpg']); assetMock.getByLibraryId.mockResolvedValue([]); - libraryMock.getOnlineAssetPaths.mockResolvedValue([]); - userMock.get.mockResolvedValue(userStub.externalPath1); + userMock.get.mockResolvedValue(userStub.admin); await sut.handleQueueAssetRefresh(mockLibraryJob); @@ -220,45 +194,6 @@ describe(LibraryService.name, () => { ]); }); - it("should mark assets outside of the user's external path as offline", async () => { - const mockLibraryJob: ILibraryRefreshJob = { - id: libraryStub.externalLibrary1.id, - refreshModifiedFiles: false, - refreshAllFiles: false, - }; - - libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1); - storageMock.crawl.mockResolvedValue(['/data/user1/photo.jpg']); - assetMock.getByLibraryId.mockResolvedValue([assetStub.external]); - libraryMock.getOnlineAssetPaths.mockResolvedValue([]); - userMock.get.mockResolvedValue(userStub.externalPath2); - - await sut.handleQueueAssetRefresh(mockLibraryJob); - - expect(assetMock.updateAll.mock.calls).toEqual([ - [ - [assetStub.external.id], - { - isOffline: true, - }, - ], - ]); - }); - - it('should not scan libraries owned by user without external path', async () => { - const mockLibraryJob: ILibraryRefreshJob = { - id: libraryStub.externalLibrary1.id, - refreshModifiedFiles: false, - refreshAllFiles: false, - }; - - libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1); - - userMock.get.mockResolvedValue(userStub.user1); - - await expect(sut.handleQueueAssetRefresh(mockLibraryJob)).resolves.toBe(false); - }); - it('should not scan upload libraries', async () => { const mockLibraryJob: ILibraryRefreshJob = { id: libraryStub.externalLibrary1.id, @@ -270,14 +205,52 @@ describe(LibraryService.name, () => { await expect(sut.handleQueueAssetRefresh(mockLibraryJob)).resolves.toBe(false); }); + + it('should ignore import paths that do not exist', async () => { + storageMock.stat.mockImplementation((path): Promise => { + if (path === libraryStub.externalLibraryWithImportPaths1.importPaths[0]) { + const error = { code: 'ENOENT' } as any; + throw error; + } + return Promise.resolve({ + isDirectory: () => true, + } as Stats); + }); + + storageMock.checkFileExists.mockResolvedValue(true); + + const mockLibraryJob: ILibraryRefreshJob = { + id: libraryStub.externalLibraryWithImportPaths1.id, + refreshModifiedFiles: false, + refreshAllFiles: false, + }; + + libraryMock.get.mockResolvedValue(libraryStub.externalLibraryWithImportPaths1); + storageMock.crawl.mockResolvedValue([]); + assetMock.getByLibraryId.mockResolvedValue([]); + userMock.get.mockResolvedValue(userStub.externalPathRoot); + + await sut.handleQueueAssetRefresh(mockLibraryJob); + + expect(storageMock.crawl).toHaveBeenCalledWith({ + pathsToCrawl: [libraryStub.externalLibraryWithImportPaths1.importPaths[1]], + exclusionPatterns: [], + }); + }); }); describe('handleAssetRefresh', () => { let mockUser: UserEntity; beforeEach(() => { - mockUser = userStub.externalPath1; + mockUser = userStub.admin; userMock.get.mockResolvedValue(mockUser); + + storageMock.stat.mockResolvedValue({ + size: 100, + mtime: new Date('2023-01-01'), + ctime: new Date('2023-01-01'), + } as Stats); }); it('should reject an unknown file extension', async () => { @@ -747,26 +720,6 @@ describe(LibraryService.name, () => { }); }); - describe('getAllForUser', () => { - it('should return all libraries for user', async () => { - libraryMock.getAllByUserId.mockResolvedValue([libraryStub.uploadLibrary1, libraryStub.externalLibrary1]); - await expect(sut.getAllForUser(authStub.admin)).resolves.toEqual([ - expect.objectContaining({ - id: libraryStub.uploadLibrary1.id, - name: libraryStub.uploadLibrary1.name, - ownerId: libraryStub.uploadLibrary1.ownerId, - }), - expect.objectContaining({ - id: libraryStub.externalLibrary1.id, - name: libraryStub.externalLibrary1.name, - ownerId: libraryStub.externalLibrary1.ownerId, - }), - ]); - - expect(libraryMock.getAllByUserId).toHaveBeenCalledWith(authStub.admin.user.id); - }); - }); - describe('getStatistics', () => { it('should return library statistics', async () => { libraryMock.getStatistics.mockResolvedValue({ photos: 10, videos: 0, total: 10, usage: 1337 }); @@ -1104,9 +1057,15 @@ describe(LibraryService.name, () => { libraryMock.update.mockResolvedValue(libraryStub.externalLibraryWithImportPaths1); libraryMock.get.mockResolvedValue(libraryStub.externalLibraryWithImportPaths1); - await expect(sut.update(authStub.admin, authStub.admin.user.id, { importPaths: ['/foo'] })).resolves.toEqual( - mapLibrary(libraryStub.externalLibraryWithImportPaths1), - ); + storageMock.stat.mockResolvedValue({ + isDirectory: () => true, + } as Stats); + + storageMock.checkFileExists.mockResolvedValue(true); + + await expect( + sut.update(authStub.admin, authStub.admin.user.id, { importPaths: ['/data/user1/foo'] }), + ).resolves.toEqual(mapLibrary(libraryStub.externalLibraryWithImportPaths1)); expect(libraryMock.update).toHaveBeenCalledWith( expect.objectContaining({ @@ -1142,7 +1101,7 @@ describe(LibraryService.name, () => { }); }); - describe('watchAll new', () => { + describe('watchAll', () => { describe('watching disabled', () => { beforeEach(async () => { configMock.load.mockResolvedValue(systemConfigStub.libraryWatchDisabled); @@ -1523,4 +1482,101 @@ describe(LibraryService.name, () => { ]); }); }); + + describe('validate', () => { + it('should validate directory', async () => { + storageMock.stat.mockResolvedValue({ + isDirectory: () => true, + } as Stats); + + storageMock.checkFileExists.mockResolvedValue(true); + + const result = await sut.validate(authStub.external1, libraryStub.externalLibraryWithImportPaths1.id, { + importPaths: ['/data/user1/'], + }); + + expect(result.importPaths).toEqual([ + { + importPath: '/data/user1/', + isValid: true, + message: undefined, + }, + ]); + }); + + it('should detect when path does not exist', async () => { + storageMock.stat.mockImplementation(() => { + const error = { code: 'ENOENT' } as any; + throw error; + }); + + const result = await sut.validate(authStub.external1, libraryStub.externalLibraryWithImportPaths1.id, { + importPaths: ['/data/user1/'], + }); + + expect(result.importPaths).toEqual([ + { + importPath: '/data/user1/', + isValid: false, + message: 'Path does not exist (ENOENT)', + }, + ]); + }); + + it('should detect when path is not a directory', async () => { + storageMock.stat.mockResolvedValue({ + isDirectory: () => false, + } as Stats); + + const result = await sut.validate(authStub.external1, libraryStub.externalLibraryWithImportPaths1.id, { + importPaths: ['/data/user1/file'], + }); + + expect(result.importPaths).toEqual([ + { + importPath: '/data/user1/file', + isValid: false, + message: 'Not a directory', + }, + ]); + }); + + it('should return an unknown exception from stat', async () => { + storageMock.stat.mockImplementation(() => { + throw new Error('Unknown error'); + }); + + const result = await sut.validate(authStub.external1, libraryStub.externalLibraryWithImportPaths1.id, { + importPaths: ['/data/user1/'], + }); + + expect(result.importPaths).toEqual([ + { + importPath: '/data/user1/', + isValid: false, + message: 'Error: Unknown error', + }, + ]); + }); + + it('should detect when access rights are missing', async () => { + storageMock.stat.mockResolvedValue({ + isDirectory: () => true, + } as Stats); + + storageMock.checkFileExists.mockResolvedValue(false); + + const result = await sut.validate(authStub.external1, libraryStub.externalLibraryWithImportPaths1.id, { + importPaths: ['/data/user1/'], + }); + + expect(result.importPaths).toEqual([ + { + importPath: '/data/user1/', + isValid: false, + message: 'Lacking read permission for folder', + }, + ]); + }); + }); }); diff --git a/server/src/domain/library/library.service.ts b/server/src/domain/library/library.service.ts index 3454f3547b..4d89126859 100644 --- a/server/src/domain/library/library.service.ts +++ b/server/src/domain/library/library.service.ts @@ -29,7 +29,11 @@ import { LibraryResponseDto, LibraryStatsResponseDto, ScanLibraryDto, + SearchLibraryDto, UpdateLibraryDto, + ValidateLibraryDto, + ValidateLibraryImportPathResponseDto, + ValidateLibraryResponseDto, mapLibrary, } from './library.dto'; @@ -109,20 +113,13 @@ export class LibraryService extends EventEmitter { ignore: library.exclusionPatterns, }); - const config = await this.configCore.getConfig(); - const { usePolling, interval } = config.library.watch; - - this.logger.debug(`Settings for watcher: usePolling: ${usePolling}, interval: ${interval}`); - let _resolve: () => void; const ready$ = new Promise((resolve) => (_resolve = resolve)); this.watchers[id] = this.storageRepository.watch( library.importPaths, { - usePolling, - interval, - binaryInterval: interval, + usePolling: false, ignoreInitial: true, }, { @@ -186,6 +183,7 @@ export class LibraryService extends EventEmitter { async getStatistics(auth: AuthDto, id: string): Promise { await this.access.requirePermission(auth, Permission.LIBRARY_READ, id); + return this.repository.getStatistics(id); } @@ -193,17 +191,18 @@ export class LibraryService extends EventEmitter { return this.repository.getCountForUser(auth.user.id); } - async getAllForUser(auth: AuthDto): Promise { - const libraries = await this.repository.getAllByUserId(auth.user.id); - return libraries.map((library) => mapLibrary(library)); - } - async get(auth: AuthDto, id: string): Promise { await this.access.requirePermission(auth, Permission.LIBRARY_READ, id); + const library = await this.findOrFail(id); return mapLibrary(library); } + async getAll(auth: AuthDto, dto: SearchLibraryDto): Promise { + const libraries = await this.repository.getAll(false, dto.type); + return libraries.map((library) => mapLibrary(library)); + } + async handleQueueCleanup(): Promise { this.logger.debug('Cleaning up any pending library deletions'); const pendingDeletion = await this.repository.getAllDeleted(); @@ -238,8 +237,14 @@ export class LibraryService extends EventEmitter { } } + let ownerId = auth.user.id; + + if (dto.ownerId) { + ownerId = dto.ownerId; + } + const library = await this.repository.create({ - ownerId: auth.user.id, + ownerId, name: dto.name, type: dto.type, importPaths: dto.importPaths ?? [], @@ -270,10 +275,69 @@ export class LibraryService extends EventEmitter { ); } + private async validateImportPath(importPath: string): Promise { + const validation = new ValidateLibraryImportPathResponseDto(); + validation.importPath = importPath; + + try { + const stat = await this.storageRepository.stat(importPath); + + if (!stat.isDirectory()) { + validation.message = 'Not a directory'; + return validation; + } + } catch (error: any) { + if (error.code === 'ENOENT') { + validation.message = 'Path does not exist (ENOENT)'; + return validation; + } + validation.message = String(error); + return validation; + } + + const access = await this.storageRepository.checkFileExists(importPath, R_OK); + + if (!access) { + validation.message = 'Lacking read permission for folder'; + return validation; + } + + validation.isValid = true; + return validation; + } + + public async validate(auth: AuthDto, id: string, dto: ValidateLibraryDto): Promise { + await this.access.requirePermission(auth, Permission.LIBRARY_UPDATE, id); + + const response = new ValidateLibraryResponseDto(); + + if (dto.importPaths) { + response.importPaths = await Promise.all( + dto.importPaths.map(async (importPath) => { + return await this.validateImportPath(importPath); + }), + ); + } + + return response; + } + async update(auth: AuthDto, id: string, dto: UpdateLibraryDto): Promise { await this.access.requirePermission(auth, Permission.LIBRARY_UPDATE, id); + const library = await this.repository.update({ id, ...dto }); + if (dto.importPaths) { + const validation = await this.validate(auth, id, { importPaths: dto.importPaths }); + if (validation.importPaths) { + for (const path of validation.importPaths) { + if (!path.isValid) { + throw new BadRequestException(`Invalid import path: ${path.message}`); + } + } + } + } + if (dto.importPaths || dto.exclusionPatterns) { // Re-watch library to use new paths and/or exclusion patterns await this.watch(id); @@ -337,7 +401,7 @@ export class LibraryService extends EventEmitter { return true; } else { // File can't be accessed and does not already exist in db - throw new BadRequestException("Can't access file", { cause: error }); + throw new BadRequestException('Cannot access file', { cause: error }); } } @@ -509,6 +573,14 @@ export class LibraryService extends EventEmitter { return true; } + // Check if a given path is in a user's external path. Both arguments are assumed to be normalized + private isInExternalPath(filePath: string, externalPath: string | null): boolean { + if (externalPath === null) { + return false; + } + return filePath.startsWith(externalPath); + } + async handleQueueAssetRefresh(job: ILibraryRefreshJob): Promise { const library = await this.repository.get(job.id); if (!library || library.type !== LibraryType.EXTERNAL) { @@ -516,24 +588,28 @@ export class LibraryService extends EventEmitter { return false; } - const user = await this.userRepository.get(library.ownerId, {}); - if (!user?.externalPath) { - this.logger.warn('User has no external path set, cannot refresh library'); - return false; - } - this.logger.verbose(`Refreshing library: ${job.id}`); + + const pathValidation = await Promise.all( + library.importPaths.map(async (importPath) => await this.validateImportPath(importPath)), + ); + + const validImportPaths = pathValidation + .map((validation) => { + if (!validation.isValid) { + this.logger.error(`Skipping invalid import path: ${validation.importPath}. Reason: ${validation.message}`); + } + return validation; + }) + .filter((validation) => validation.isValid) + .map((validation) => validation.importPath); + const rawPaths = await this.storageRepository.crawl({ - pathsToCrawl: library.importPaths, + pathsToCrawl: validImportPaths, exclusionPatterns: library.exclusionPatterns, }); - const crawledAssetPaths = rawPaths - .map((filePath) => path.normalize(filePath)) - .filter((assetPath) => - // Filter out paths that are not within the user's external path - assetPath.match(new RegExp(`^${user.externalPath}`)), - ) as string[]; + const crawledAssetPaths = rawPaths.map((filePath) => path.normalize(filePath)); this.logger.debug(`Found ${crawledAssetPaths.length} asset(s) when crawling import paths ${library.importPaths}`); const assetsInLibrary = await this.assetRepository.getByLibraryId([job.id]); diff --git a/server/src/domain/media/media.service.spec.ts b/server/src/domain/media/media.service.spec.ts index 6406b2887a..0944016379 100644 --- a/server/src/domain/media/media.service.spec.ts +++ b/server/src/domain/media/media.service.spec.ts @@ -1,5 +1,6 @@ import { AssetType, + AudioCodec, Colorspace, ExifEntity, SystemConfigKey, @@ -475,7 +476,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v h264', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -542,7 +543,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v h264', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -571,7 +572,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v h264', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -629,7 +630,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v h264', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -704,8 +705,38 @@ describe(MediaService.name, () => { ); }); - it('should transcode when audio doesnt match target', async () => { - mediaMock.probe.mockResolvedValue(probeStub.audioStreamMp3); + it('should copy video stream when video matches target', async () => { + mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer); + configMock.load.mockResolvedValue([ + { key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.HEVC }, + { key: SystemConfigKey.FFMPEG_ACCEPTED_AUDIO_CODECS, value: [AudioCodec.AAC] }, + ]); + assetMock.getByIds.mockResolvedValue([assetStub.video]); + await sut.handleVideoConversion({ id: assetStub.video.id }); + expect(mediaMock.transcode).toHaveBeenCalledWith( + '/original/path.ext', + 'upload/encoded-video/user-id/as/se/asset-id.mp4', + { + inputOptions: [], + outputOptions: [ + '-c:v copy', + '-c:a aac', + '-movflags faststart', + '-fps_mode passthrough', + '-map 0:0', + '-map 0:1', + '-tag:v hvc1', + '-v verbose', + '-preset ultrafast', + '-crf 23', + ], + twoPass: false, + }, + ); + }); + + it('should copy audio stream when audio matches target', async () => { + mediaMock.probe.mockResolvedValue(probeStub.audioStreamAac); configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.OPTIMAL }]); assetMock.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); @@ -716,7 +747,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v h264', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -743,7 +774,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v h264', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -758,11 +789,11 @@ describe(MediaService.name, () => { ); }); - it('should not transcode an invalid transcode value', async () => { + it('should throw an exception if transcode value is invalid', async () => { mediaMock.probe.mockResolvedValue(probeStub.videoStream2160p); configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: 'invalid' }]); - assetMock.getByIds.mockResolvedValue([assetStub.video]); - await sut.handleVideoConversion({ id: assetStub.video.id }); + + await expect(sut.handleVideoConversion({ id: assetStub.video.id })).rejects.toThrow(); expect(mediaMock.transcode).not.toHaveBeenCalled(); }); @@ -809,7 +840,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v h264', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -841,7 +872,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v h264', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -870,7 +901,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v h264', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -901,7 +932,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v vp9', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -935,7 +966,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v vp9', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -967,7 +998,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v vp9', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -999,7 +1030,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v vp9', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1030,7 +1061,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v vp9', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1060,7 +1091,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v h264', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1090,7 +1121,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v h264', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1106,7 +1137,7 @@ describe(MediaService.name, () => { }); it('should disable thread pooling for hevc if thread limit is above 0', async () => { - mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer); + mediaMock.probe.mockResolvedValue(probeStub.videoStreamVp9); configMock.load.mockResolvedValue([ { key: SystemConfigKey.FFMPEG_THREADS, value: 2 }, { key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.HEVC }, @@ -1120,7 +1151,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v hevc', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1140,7 +1171,7 @@ describe(MediaService.name, () => { }); it('should omit thread flags for hevc if thread limit is at or below 0', async () => { - mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer); + mediaMock.probe.mockResolvedValue(probeStub.videoStreamVp9); configMock.load.mockResolvedValue([ { key: SystemConfigKey.FFMPEG_THREADS, value: 0 }, { key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.HEVC }, @@ -1154,7 +1185,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v hevc', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1221,7 +1252,7 @@ describe(MediaService.name, () => { '-rc-lookahead 20', '-i_qfactor 0.75', `-c:v h264_nvenc`, - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1259,7 +1290,7 @@ describe(MediaService.name, () => { '-rc-lookahead 20', '-i_qfactor 0.75', `-c:v h264_nvenc`, - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1293,7 +1324,7 @@ describe(MediaService.name, () => { '-rc-lookahead 20', '-i_qfactor 0.75', `-c:v h264_nvenc`, - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1328,7 +1359,7 @@ describe(MediaService.name, () => { '-rc-lookahead 20', '-i_qfactor 0.75', `-c:v h264_nvenc`, - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1359,7 +1390,7 @@ describe(MediaService.name, () => { '-rc-lookahead 20', '-i_qfactor 0.75', `-c:v h264_nvenc`, - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1391,7 +1422,7 @@ describe(MediaService.name, () => { inputOptions: ['-init_hw_device qsv=hw', '-filter_hw_device hw'], outputOptions: [ `-c:v h264_qsv`, - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1428,7 +1459,7 @@ describe(MediaService.name, () => { inputOptions: ['-init_hw_device qsv=hw,child_device=/dev/dri/renderD128', '-filter_hw_device hw'], outputOptions: [ `-c:v h264_qsv`, - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1464,7 +1495,7 @@ describe(MediaService.name, () => { inputOptions: ['-init_hw_device qsv=hw', '-filter_hw_device hw'], outputOptions: [ `-c:v h264_qsv`, - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1497,7 +1528,7 @@ describe(MediaService.name, () => { inputOptions: ['-init_hw_device qsv=hw', '-filter_hw_device hw'], outputOptions: [ `-c:v vp9_qsv`, - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1541,7 +1572,7 @@ describe(MediaService.name, () => { inputOptions: ['-init_hw_device vaapi=accel:/dev/dri/renderD128', '-filter_hw_device accel'], outputOptions: [ `-c:v h264_vaapi`, - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1573,7 +1604,7 @@ describe(MediaService.name, () => { inputOptions: ['-init_hw_device vaapi=accel:/dev/dri/renderD128', '-filter_hw_device accel'], outputOptions: [ `-c:v h264_vaapi`, - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1607,7 +1638,7 @@ describe(MediaService.name, () => { inputOptions: ['-init_hw_device vaapi=accel:/dev/dri/renderD128', '-filter_hw_device accel'], outputOptions: [ `-c:v h264_vaapi`, - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1637,7 +1668,7 @@ describe(MediaService.name, () => { inputOptions: ['-init_hw_device vaapi=accel:/dev/dri/card1', '-filter_hw_device accel'], outputOptions: [ `-c:v h264_vaapi`, - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1663,7 +1694,7 @@ describe(MediaService.name, () => { inputOptions: ['-init_hw_device vaapi=accel:/dev/dri/renderD129', '-filter_hw_device accel'], outputOptions: [ `-c:v h264_vaapi`, - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1697,7 +1728,7 @@ describe(MediaService.name, () => { inputOptions: ['-init_hw_device vaapi=accel:/dev/dri/renderD128', '-filter_hw_device accel'], outputOptions: [ `-c:v h264_vaapi`, - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1730,7 +1761,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v h264', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1756,7 +1787,7 @@ describe(MediaService.name, () => { it('should set vbr options for rkmpp when max bitrate is enabled', async () => { storageMock.readdir.mockResolvedValue(['renderD128']); - mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer); + mediaMock.probe.mockResolvedValue(probeStub.videoStreamVp9); configMock.load.mockResolvedValue([ { key: SystemConfigKey.FFMPEG_ACCEL, value: TranscodeHWAccel.RKMPP }, { key: SystemConfigKey.FFMPEG_MAX_BITRATE, value: '10000k' }, @@ -1768,10 +1799,10 @@ describe(MediaService.name, () => { '/original/path.ext', 'upload/encoded-video/user-id/as/se/asset-id.mp4', { - inputOptions: [], + inputOptions: ['-hwaccel rkmpp', '-hwaccel_output_format drm_prime', '-afbc rga'], outputOptions: [ - `-c:v hevc_rkmpp_encoder`, - '-c:a aac', + `-c:v hevc_rkmpp`, + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1779,17 +1810,12 @@ describe(MediaService.name, () => { '-g 256', '-tag:v hvc1', '-v verbose', + '-vf scale_rkrga=-2:720:format=nv12:afbc=1', '-level 153', - '-rc_mode 3', - '-quality_min 0', - '-quality_max 100', + '-rc_mode AVBR', '-b:v 10000k', - '-width 1280', - '-height 720', ], twoPass: false, - ffmpegPath: 'ffmpeg_mpp', - ldLibraryPath: '/lib/aarch64-linux-gnu:/lib/ffmpeg-mpp', }, ); }); @@ -1808,26 +1834,22 @@ describe(MediaService.name, () => { '/original/path.ext', 'upload/encoded-video/user-id/as/se/asset-id.mp4', { - inputOptions: [], + inputOptions: ['-hwaccel rkmpp', '-hwaccel_output_format drm_prime', '-afbc rga'], outputOptions: [ - `-c:v h264_rkmpp_encoder`, - '-c:a aac', + `-c:v h264_rkmpp`, + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', '-map 0:1', '-g 256', '-v verbose', + '-vf scale_rkrga=-2:720:format=nv12:afbc=1', '-level 51', - '-rc_mode 2', - '-quality_min 51', - '-quality_max 51', - '-width 1280', - '-height 720', + '-rc_mode CQP', + '-qp_init 30', ], twoPass: false, - ffmpegPath: 'ffmpeg_mpp', - ldLibraryPath: '/lib/aarch64-linux-gnu:/lib/ffmpeg-mpp', }, ); }); @@ -1845,7 +1867,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v h264', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1872,7 +1894,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v h264', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', @@ -1899,7 +1921,7 @@ describe(MediaService.name, () => { inputOptions: [], outputOptions: [ '-c:v h264', - '-c:a aac', + '-c:a copy', '-movflags faststart', '-fps_mode passthrough', '-map 0:0', diff --git a/server/src/domain/media/media.service.ts b/server/src/domain/media/media.service.ts index 68f861d7e2..6a5c8ff9d3 100644 --- a/server/src/domain/media/media.service.ts +++ b/server/src/domain/media/media.service.ts @@ -6,6 +6,7 @@ import { Colorspace, TranscodeHWAccel, TranscodePolicy, + TranscodeTarget, VideoCodec, } from '@app/infra/entities'; import { ImmichLogger } from '@app/infra/logger'; @@ -197,7 +198,7 @@ export class MediaService { } const mainAudioStream = this.getMainStream(audioStreams); const config = { ...ffmpeg, targetResolution: size.toString() }; - const options = new ThumbnailConfig(config).getOptions(mainVideoStream, mainAudioStream); + const options = new ThumbnailConfig(config).getOptions(TranscodeTarget.VIDEO, mainVideoStream, mainAudioStream); await this.mediaRepository.transcode(asset.originalPath, path, options); break; } @@ -267,7 +268,6 @@ export class MediaService { const mainVideoStream = this.getMainStream(videoStreams); const mainAudioStream = this.getMainStream(audioStreams); const containerExtension = format.formatName; - const bitrate = format.bitrate; if (!mainVideoStream || !containerExtension) { return false; } @@ -279,15 +279,8 @@ export class MediaService { const { ffmpeg: config } = await this.configCore.getConfig(); - const required = this.isTranscodeRequired( - asset, - mainVideoStream, - mainAudioStream, - containerExtension, - config, - bitrate, - ); - if (!required) { + const target = this.getTranscodeTarget(config, mainVideoStream, mainAudioStream); + if (target === TranscodeTarget.NONE) { if (asset.encodedVideoPath) { this.logger.log(`Transcoded video exists for asset ${asset.id}, but is no longer required. Deleting...`); await this.jobRepository.queue({ name: JobName.DELETE_FILES, data: { files: [asset.encodedVideoPath] } }); @@ -299,13 +292,15 @@ export class MediaService { let transcodeOptions; try { - transcodeOptions = await this.getCodecConfig(config).then((c) => c.getOptions(mainVideoStream, mainAudioStream)); + transcodeOptions = await this.getCodecConfig(config).then((c) => + c.getOptions(target, mainVideoStream, mainAudioStream), + ); } catch (error) { this.logger.error(`An error occurred while configuring transcoding options: ${error}`); return false; } - this.logger.log(`Start encoding video ${asset.id} ${JSON.stringify(transcodeOptions)}`); + this.logger.log(`Started encoding video ${asset.id} ${JSON.stringify(transcodeOptions)}`); try { await this.mediaRepository.transcode(input, output, transcodeOptions); } catch (error) { @@ -316,11 +311,13 @@ export class MediaService { ); } config.accel = TranscodeHWAccel.DISABLED; - transcodeOptions = await this.getCodecConfig(config).then((c) => c.getOptions(mainVideoStream, mainAudioStream)); + transcodeOptions = await this.getCodecConfig(config).then((c) => + c.getOptions(target, mainVideoStream, mainAudioStream), + ); await this.mediaRepository.transcode(input, output, transcodeOptions); } - this.logger.log(`Encoding success ${asset.id}`); + this.logger.log(`Successfully encoded ${asset.id}`); await this.assetRepository.save({ id: asset.id, encodedVideoPath: output }); @@ -331,55 +328,88 @@ export class MediaService { return streams.sort((stream1, stream2) => stream2.frameCount - stream1.frameCount)[0]; } - private isTranscodeRequired( - asset: AssetEntity, - videoStream: VideoStreamInfo, + private getTranscodeTarget( + config: SystemConfigFFmpegDto, + videoStream: VideoStreamInfo | null, audioStream: AudioStreamInfo | null, - containerExtension: string, - ffmpegConfig: SystemConfigFFmpegDto, - bitrate: number, - ): boolean { - const isTargetVideoCodec = ffmpegConfig.acceptedVideoCodecs.includes(videoStream.codecName as VideoCodec); - const isTargetContainer = ['mov,mp4,m4a,3gp,3g2,mj2', 'mp4', 'mov'].includes(containerExtension); - const isTargetAudioCodec = - audioStream == null || ffmpegConfig.acceptedAudioCodecs.includes(audioStream.codecName as AudioCodec); + ): TranscodeTarget { + if (videoStream == null && audioStream == null) { + return TranscodeTarget.NONE; + } - this.logger.verbose( - `${asset.id}: AudioCodecName ${audioStream?.codecName ?? 'None'}, AudioStreamCodecType ${ - audioStream?.codecType ?? 'None' - }, containerExtension ${containerExtension}`, - ); + const isAudioTranscodeRequired = this.isAudioTranscodeRequired(config, audioStream); + const isVideoTranscodeRequired = this.isVideoTranscodeRequired(config, videoStream); - const allTargetsMatching = isTargetVideoCodec && isTargetAudioCodec && isTargetContainer; - const scalingEnabled = ffmpegConfig.targetResolution !== 'original'; - const targetRes = Number.parseInt(ffmpegConfig.targetResolution); - const isLargerThanTargetRes = scalingEnabled && Math.min(videoStream.height, videoStream.width) > targetRes; - const isLargerThanTargetBitrate = bitrate > this.parseBitrateToBps(ffmpegConfig.maxBitrate); + if (isAudioTranscodeRequired && isVideoTranscodeRequired) { + return TranscodeTarget.ALL; + } + + if (isAudioTranscodeRequired) { + return TranscodeTarget.AUDIO; + } + + if (isVideoTranscodeRequired) { + return TranscodeTarget.VIDEO; + } + + return TranscodeTarget.NONE; + } + + private isAudioTranscodeRequired(ffmpegConfig: SystemConfigFFmpegDto, stream: AudioStreamInfo | null): boolean { + if (stream == null) { + return false; + } switch (ffmpegConfig.transcode) { case TranscodePolicy.DISABLED: { return false; } - case TranscodePolicy.ALL: { return true; } - - case TranscodePolicy.REQUIRED: { - return !allTargetsMatching || videoStream.isHDR; - } - - case TranscodePolicy.OPTIMAL: { - return !allTargetsMatching || isLargerThanTargetRes || videoStream.isHDR; - } - + case TranscodePolicy.REQUIRED: + case TranscodePolicy.OPTIMAL: case TranscodePolicy.BITRATE: { - return !allTargetsMatching || isLargerThanTargetBitrate || videoStream.isHDR; + return !ffmpegConfig.acceptedAudioCodecs.includes(stream.codecName as AudioCodec); } - default: { + throw new Error(`Unsupported transcode policy: ${ffmpegConfig.transcode}`); + } + } + } + + private isVideoTranscodeRequired(ffmpegConfig: SystemConfigFFmpegDto, stream: VideoStreamInfo | null): boolean { + if (stream == null) { + return false; + } + + const scalingEnabled = ffmpegConfig.targetResolution !== 'original'; + const targetRes = Number.parseInt(ffmpegConfig.targetResolution); + const isLargerThanTargetRes = scalingEnabled && Math.min(stream.height, stream.width) > targetRes; + const isLargerThanTargetBitrate = stream.bitrate > this.parseBitrateToBps(ffmpegConfig.maxBitrate); + + const isTargetVideoCodec = ffmpegConfig.acceptedVideoCodecs.includes(stream.codecName as VideoCodec); + const isRequired = !isTargetVideoCodec || stream.isHDR; + + switch (ffmpegConfig.transcode) { + case TranscodePolicy.DISABLED: { return false; } + case TranscodePolicy.ALL: { + return true; + } + case TranscodePolicy.REQUIRED: { + return isRequired; + } + case TranscodePolicy.OPTIMAL: { + return isRequired || isLargerThanTargetRes; + } + case TranscodePolicy.BITRATE: { + return isRequired || isLargerThanTargetBitrate; + } + default: { + throw new Error(`Unsupported transcode policy: ${ffmpegConfig.transcode}`); + } } } diff --git a/server/src/domain/media/media.util.ts b/server/src/domain/media/media.util.ts index ab3e43ec9f..d5f08ab0de 100644 --- a/server/src/domain/media/media.util.ts +++ b/server/src/domain/media/media.util.ts @@ -1,4 +1,4 @@ -import { CQMode, ToneMapping, TranscodeHWAccel, VideoCodec } from '@app/infra/entities'; +import { CQMode, ToneMapping, TranscodeHWAccel, TranscodeTarget, VideoCodec } from '@app/infra/entities'; import { AudioStreamInfo, BitrateDistribution, @@ -12,29 +12,33 @@ class BaseConfig implements VideoCodecSWConfig { presets = ['veryslow', 'slower', 'slow', 'medium', 'fast', 'faster', 'veryfast', 'superfast', 'ultrafast']; constructor(protected config: SystemConfigFFmpegDto) {} - getOptions(videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo) { + getOptions(target: TranscodeTarget, videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo) { const options = { - inputOptions: this.getBaseInputOptions(), - outputOptions: [...this.getBaseOutputOptions(videoStream, audioStream), '-v verbose'], + inputOptions: this.getBaseInputOptions(videoStream), + outputOptions: [...this.getBaseOutputOptions(target, videoStream, audioStream), '-v verbose'], twoPass: this.eligibleForTwoPass(), } as TranscodeOptions; - const filters = this.getFilterOptions(videoStream); - if (filters.length > 0) { - options.outputOptions.push(`-vf ${filters.join(',')}`); + if ([TranscodeTarget.ALL, TranscodeTarget.VIDEO].includes(target)) { + const filters = this.getFilterOptions(videoStream); + if (filters.length > 0) { + options.outputOptions.push(`-vf ${filters.join(',')}`); + } } + options.outputOptions.push(...this.getPresetOptions(), ...this.getThreadOptions(), ...this.getBitrateOptions()); return options; } - getBaseInputOptions(): string[] { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getBaseInputOptions(videoStream: VideoStreamInfo): string[] { return []; } - getBaseOutputOptions(videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo) { + getBaseOutputOptions(target: TranscodeTarget, videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo) { const options = [ - `-c:v ${this.getVideoCodec()}`, - `-c:a ${this.getAudioCodec()}`, + `-c:v ${[TranscodeTarget.ALL, TranscodeTarget.VIDEO].includes(target) ? this.getVideoCodec() : 'copy'}`, + `-c:a ${[TranscodeTarget.ALL, TranscodeTarget.AUDIO].includes(target) ? this.getAudioCodec() : 'copy'}`, // Makes a second pass moving the moov atom to the // beginning of the file for improved playback speed. '-movflags faststart', @@ -398,14 +402,14 @@ export class NVENCConfig extends BaseHWConfig { return ['-init_hw_device cuda=cuda:0', '-filter_hw_device cuda']; } - getBaseOutputOptions(videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo) { + getBaseOutputOptions(target: TranscodeTarget, videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo) { const options = [ // below settings recommended from https://docs.nvidia.com/video-technologies/video-codec-sdk/12.0/ffmpeg-with-nvidia-gpu/index.html#command-line-for-latency-tolerant-high-quality-transcoding '-tune hq', '-qmin 0', '-rc-lookahead 20', '-i_qfactor 0.75', - ...super.getBaseOutputOptions(videoStream, audioStream), + ...super.getBaseOutputOptions(target, videoStream, audioStream), ]; if (this.getBFrames() > 0) { options.push('-b_ref_mode middle', '-b_qfactor 1.1'); @@ -483,8 +487,8 @@ export class QSVConfig extends BaseHWConfig { return [`-init_hw_device qsv=hw${qsvString}`, '-filter_hw_device hw']; } - getBaseOutputOptions(videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo) { - const options = super.getBaseOutputOptions(videoStream, audioStream); + getBaseOutputOptions(target: TranscodeTarget, videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo) { + const options = super.getBaseOutputOptions(target, videoStream, audioStream); // VP9 requires enabling low power mode https://git.ffmpeg.org/gitweb/ffmpeg.git/commit/33583803e107b6d532def0f9d949364b01b6ad5a if (this.config.targetVideoCodec === VideoCodec.VP9) { options.push('-low_power 1'); @@ -604,33 +608,28 @@ export class VAAPIConfig extends BaseHWConfig { } export class RKMPPConfig extends BaseHWConfig { - getOptions(videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo): TranscodeOptions { - const options = super.getOptions(videoStream, audioStream); - options.ffmpegPath = 'ffmpeg_mpp'; - options.ldLibraryPath = '/lib/aarch64-linux-gnu:/lib/ffmpeg-mpp'; - options.outputOptions.push(...this.getSizeOptions(videoStream)); - return options; - } - eligibleForTwoPass(): boolean { return false; } - getBaseInputOptions() { + getBaseInputOptions(videoStream: VideoStreamInfo) { if (this.devices.length === 0) { throw new Error('No RKMPP device found'); } - return []; + if (this.shouldToneMap(videoStream)) { + // disable hardware decoding + return []; + } + return ['-hwaccel rkmpp', '-hwaccel_output_format drm_prime', '-afbc rga']; } getFilterOptions(videoStream: VideoStreamInfo) { - return this.shouldToneMap(videoStream) ? this.getToneMapping() : []; - } - - getSizeOptions(videoStream: VideoStreamInfo) { + if (this.shouldToneMap(videoStream)) { + // use software filter options + return super.getFilterOptions(videoStream); + } if (this.shouldScale(videoStream)) { - const { width, height } = this.getSize(videoStream); - return [`-width ${width}`, `-height ${height}`]; + return [`scale_rkrga=${this.getScaling(videoStream)}:format=nv12:afbc=1`]; } return []; } @@ -654,12 +653,11 @@ export class RKMPPConfig extends BaseHWConfig { getBitrateOptions() { const bitrate = this.getMaxBitrateValue(); if (bitrate > 0) { - return ['-rc_mode 3', '-quality_min 0', '-quality_max 100', `-b:v ${bitrate}${this.getBitrateUnit()}`]; - } else { - // convert CQP from 51-10 to 0-100, values below 10 are set to 10 - const quality = Math.floor(125 - Math.max(this.config.crf, 10) * (125 / 51)); - return ['-rc_mode 2', `-quality_min ${quality}`, `-quality_max ${quality}`]; + // -b:v specifies max bitrate, average bitrate is derived automatically... + return ['-rc_mode AVBR', `-b:v ${bitrate}${this.getBitrateUnit()}`]; } + // use CRF value as QP value + return ['-rc_mode CQP', `-qp_init ${this.config.crf}`]; } getSupportedCodecs() { @@ -667,6 +665,6 @@ export class RKMPPConfig extends BaseHWConfig { } getVideoCodec(): string { - return `${this.config.targetVideoCodec}_rkmpp_encoder`; + return `${this.config.targetVideoCodec}_rkmpp`; } } diff --git a/server/src/domain/metadata/metadata.service.ts b/server/src/domain/metadata/metadata.service.ts index 94c3e9ae3f..562568adf6 100644 --- a/server/src/domain/metadata/metadata.service.ts +++ b/server/src/domain/metadata/metadata.service.ts @@ -493,7 +493,7 @@ export class MetadataService { model: tags.Model ?? null, modifyDate: exifDate(tags.ModifyDate) ?? asset.fileModifiedAt, orientation: validate(tags.Orientation)?.toString() ?? null, - profileDescription: tags.ProfileDescription || tags.ProfileName || null, + profileDescription: tags.ProfileDescription || null, projectionType: tags.ProjectionType ? String(tags.ProjectionType).toUpperCase() : null, timeZone: tags.tz ?? null, }; diff --git a/server/src/domain/partner/partner.service.spec.ts b/server/src/domain/partner/partner.service.spec.ts index 0f9e173b70..2bc5f3ca90 100644 --- a/server/src/domain/partner/partner.service.spec.ts +++ b/server/src/domain/partner/partner.service.spec.ts @@ -18,7 +18,6 @@ const responseDto = { createdAt: new Date('2021-01-01'), deletedAt: null, updatedAt: new Date('2021-01-01'), - externalPath: null, memoriesEnabled: true, avatarColor: UserAvatarColor.PRIMARY, quotaSizeInBytes: null, @@ -37,7 +36,6 @@ const responseDto = { createdAt: new Date('2021-01-01'), deletedAt: null, updatedAt: new Date('2021-01-01'), - externalPath: null, memoriesEnabled: true, avatarColor: UserAvatarColor.PRIMARY, inTimeline: true, diff --git a/server/src/domain/person/person.dto.ts b/server/src/domain/person/person.dto.ts index 360a9b2348..b8ad8f0451 100644 --- a/server/src/domain/person/person.dto.ts +++ b/server/src/domain/person/person.dto.ts @@ -127,7 +127,8 @@ export class PersonStatisticsResponseDto { export class PeopleResponseDto { @ApiProperty({ type: 'integer' }) total!: number; - + @ApiProperty({ type: 'integer' }) + hidden!: number; people!: PersonResponseDto[]; } diff --git a/server/src/domain/person/person.service.spec.ts b/server/src/domain/person/person.service.spec.ts index 5da8666016..ffda9034bd 100644 --- a/server/src/domain/person/person.service.spec.ts +++ b/server/src/domain/person/person.service.spec.ts @@ -114,35 +114,12 @@ describe(PersonService.name, () => { }); describe('getAll', () => { - it('should get all people with thumbnails', async () => { - personMock.getAllForUser.mockResolvedValue([personStub.withName, personStub.noThumbnail]); - personMock.getNumberOfPeople.mockResolvedValue(1); - await expect(sut.getAll(authStub.admin, { withHidden: undefined })).resolves.toEqual({ - total: 1, - people: [responseDto], - }); - expect(personMock.getAllForUser).toHaveBeenCalledWith(authStub.admin.user.id, { - minimumFaceCount: 3, - withHidden: false, - }); - }); - it('should get all visible people with thumbnails', async () => { - personMock.getAllForUser.mockResolvedValue([personStub.withName, personStub.hidden]); - personMock.getNumberOfPeople.mockResolvedValue(2); - await expect(sut.getAll(authStub.admin, { withHidden: false })).resolves.toEqual({ - total: 2, - people: [responseDto], - }); - expect(personMock.getAllForUser).toHaveBeenCalledWith(authStub.admin.user.id, { - minimumFaceCount: 3, - withHidden: false, - }); - }); it('should get all hidden and visible people with thumbnails', async () => { personMock.getAllForUser.mockResolvedValue([personStub.withName, personStub.hidden]); - personMock.getNumberOfPeople.mockResolvedValue(2); + personMock.getNumberOfPeople.mockResolvedValue({ total: 2, hidden: 1 }); await expect(sut.getAll(authStub.admin, { withHidden: true })).resolves.toEqual({ total: 2, + hidden: 1, people: [ responseDto, { diff --git a/server/src/domain/person/person.service.ts b/server/src/domain/person/person.service.ts index 359084bf21..6300cc743c 100644 --- a/server/src/domain/person/person.service.ts +++ b/server/src/domain/person/person.service.ts @@ -82,15 +82,12 @@ export class PersonService { minimumFaceCount: machineLearning.facialRecognition.minFaces, withHidden: dto.withHidden || false, }); - const total = await this.repository.getNumberOfPeople(auth.user.id); - const persons: PersonResponseDto[] = people - // with thumbnails - .filter((person) => !!person.thumbnailPath) - .map((person) => mapPerson(person)); + const { total, hidden } = await this.repository.getNumberOfPeople(auth.user.id); return { - people: persons.filter((person) => dto.withHidden || !person.isHidden), + people: people.map((person) => mapPerson(person)), total, + hidden, }; } @@ -410,8 +407,8 @@ export class PersonService { }); // `matches` also includes the face itself - if (matches.length <= 1) { - this.logger.debug(`Face ${id} has no matches`); + if (machineLearning.facialRecognition.minFaces > 1 && matches.length <= 1) { + this.logger.debug(`Face ${id} only matched the face itself, skipping`); return true; } diff --git a/server/src/domain/repositories/asset.repository.ts b/server/src/domain/repositories/asset.repository.ts index 4ff58cdb1b..7a2941ab9d 100644 --- a/server/src/domain/repositories/asset.repository.ts +++ b/server/src/domain/repositories/asset.repository.ts @@ -1,4 +1,4 @@ -import { AssetSearchOptions, SearchExploreItem } from '@app/domain'; +import { AssetSearchOneToOneRelationOptions, AssetSearchOptions, SearchExploreItem } from '@app/domain'; import { AssetEntity, AssetJobStatusEntity, AssetType, ExifEntity } from '@app/infra/entities'; import { FindOptionsRelations, FindOptionsSelect } from 'typeorm'; import { Paginated, PaginationOptions } from '../domain.util'; @@ -133,6 +133,10 @@ export interface IAssetRepository { getByLibraryIdAndOriginalPath(libraryId: string, originalPath: string): Promise; deleteAll(ownerId: string): Promise; getAll(pagination: PaginationOptions, options?: AssetSearchOptions): Paginated; + getAllByFileCreationDate( + pagination: PaginationOptions, + options?: AssetSearchOneToOneRelationOptions, + ): Paginated; getAllByDeviceId(userId: string, deviceId: string): Promise; updateAll(ids: string[], options: Partial): Promise; save(asset: Pick & Partial): Promise; @@ -140,7 +144,7 @@ export interface IAssetRepository { softDeleteAll(ids: string[]): Promise; restoreAll(ids: string[]): Promise; findLivePhotoMatch(options: LivePhotoSearchOptions): Promise; - getMapMarkers(ownerId: string, options?: MapMarkerSearchOptions): Promise; + getMapMarkers(ownerIds: string[], options?: MapMarkerSearchOptions): Promise; getStatistics(ownerId: string, options: AssetStatsOptions): Promise; getTimeBuckets(options: TimeBucketOptions): Promise; getTimeBucket(timeBucket: string, options: TimeBucketOptions): Promise; diff --git a/server/src/domain/repositories/communication.repository.ts b/server/src/domain/repositories/communication.repository.ts index 29617f0310..daf0aef0a4 100644 --- a/server/src/domain/repositories/communication.repository.ts +++ b/server/src/domain/repositories/communication.repository.ts @@ -1,3 +1,5 @@ +import { AssetResponseDto, ReleaseNotification, ServerVersionResponseDto } from '@app/domain'; + export const ICommunicationRepository = 'ICommunicationRepository'; export enum ClientEvent { @@ -7,6 +9,7 @@ export enum ClientEvent { ASSET_UPDATE = 'on_asset_update', ASSET_HIDDEN = 'on_asset_hidden', ASSET_RESTORE = 'on_asset_restore', + ASSET_STACK_UPDATE = 'on_asset_stack_update', PERSON_THUMBNAIL = 'on_person_thumbnail', SERVER_VERSION = 'on_server_version', CONFIG_UPDATE = 'on_config_update', @@ -17,12 +20,26 @@ export enum ServerEvent { CONFIG_UPDATE = 'config:update', } +export interface ClientEventMap { + [ClientEvent.UPLOAD_SUCCESS]: AssetResponseDto; + [ClientEvent.ASSET_DELETE]: string; + [ClientEvent.ASSET_TRASH]: string[]; + [ClientEvent.ASSET_UPDATE]: AssetResponseDto; + [ClientEvent.ASSET_HIDDEN]: string; + [ClientEvent.ASSET_RESTORE]: string[]; + [ClientEvent.ASSET_STACK_UPDATE]: string[]; + [ClientEvent.PERSON_THUMBNAIL]: string; + [ClientEvent.SERVER_VERSION]: ServerVersionResponseDto; + [ClientEvent.CONFIG_UPDATE]: Record; + [ClientEvent.NEW_RELEASE]: ReleaseNotification; +} + export type OnConnectCallback = (userId: string) => Promise; export type OnServerEventCallback = () => Promise; export interface ICommunicationRepository { - send(event: ClientEvent, userId: string, data: any): void; - broadcast(event: ClientEvent, data: any): void; + send(event: E, userId: string, data: ClientEventMap[E]): void; + broadcast(event: E, data: ClientEventMap[E]): void; on(event: 'connect', callback: OnConnectCallback): void; on(event: ServerEvent, callback: OnServerEventCallback): void; sendServerEvent(event: ServerEvent): void; diff --git a/server/src/domain/repositories/library.repository.ts b/server/src/domain/repositories/library.repository.ts index 7ba6cd409f..395373bcc9 100644 --- a/server/src/domain/repositories/library.repository.ts +++ b/server/src/domain/repositories/library.repository.ts @@ -5,7 +5,6 @@ export const ILibraryRepository = 'ILibraryRepository'; export interface ILibraryRepository { getCountForUser(ownerId: string): Promise; - getAllByUserId(userId: string, type?: LibraryType): Promise; getAll(withDeleted?: boolean, type?: LibraryType): Promise; getAllDeleted(): Promise; get(id: string, withDeleted?: boolean): Promise; @@ -16,7 +15,5 @@ export interface ILibraryRepository { getUploadLibraryCount(ownerId: string): Promise; update(library: Partial): Promise; getStatistics(id: string): Promise; - getOnlineAssetPaths(id: string): Promise; getAssetIds(id: string, withDeleted?: boolean): Promise; - existsByName(name: string, withDeleted?: boolean): Promise; } diff --git a/server/src/domain/repositories/media.repository.ts b/server/src/domain/repositories/media.repository.ts index 60135e62dc..ed6f884493 100644 --- a/server/src/domain/repositories/media.repository.ts +++ b/server/src/domain/repositories/media.repository.ts @@ -1,4 +1,4 @@ -import { VideoCodec } from '@app/infra/entities'; +import { TranscodeTarget, VideoCodec } from '@app/infra/entities'; import { Writable } from 'node:stream'; export const IMediaRepository = 'IMediaRepository'; @@ -16,15 +16,14 @@ export interface VideoStreamInfo { width: number; rotation: number; codecName?: string; - codecType?: string; frameCount: number; isHDR: boolean; + bitrate: number; } export interface AudioStreamInfo { index: number; codecName?: string; - codecType?: string; frameCount: number; } @@ -52,8 +51,6 @@ export interface TranscodeOptions { inputOptions: string[]; outputOptions: string[]; twoPass: boolean; - ffmpegPath?: string; - ldLibraryPath?: string; } export interface BitrateDistribution { @@ -64,7 +61,7 @@ export interface BitrateDistribution { } export interface VideoCodecSWConfig { - getOptions(videoStream: VideoStreamInfo, audioStream: AudioStreamInfo): TranscodeOptions; + getOptions(target: TranscodeTarget, videoStream: VideoStreamInfo, audioStream: AudioStreamInfo): TranscodeOptions; } export interface VideoCodecHWConfig extends VideoCodecSWConfig { diff --git a/server/src/domain/repositories/person.repository.ts b/server/src/domain/repositories/person.repository.ts index 80240091a9..85c11fe921 100644 --- a/server/src/domain/repositories/person.repository.ts +++ b/server/src/domain/repositories/person.repository.ts @@ -28,6 +28,11 @@ export interface PersonStatistics { assets: number; } +export interface PeopleStatistics { + total: number; + hidden: number; +} + export interface IPersonRepository { getAll(pagination: PaginationOptions, options?: FindManyOptions): Paginated; getAllForUser(userId: string, options: PersonSearchOptions): Promise; @@ -54,7 +59,7 @@ export interface IPersonRepository { getRandomFace(personId: string): Promise; getStatistics(personId: string): Promise; reassignFace(assetFaceId: string, newPersonId: string): Promise; - getNumberOfPeople(userId: string): Promise; + getNumberOfPeople(userId: string): Promise; reassignFaces(data: UpdateFacesData): Promise; update(entity: Partial): Promise; } diff --git a/server/src/domain/repositories/search.repository.ts b/server/src/domain/repositories/search.repository.ts index 4d720f98ad..c9fec3cf71 100644 --- a/server/src/domain/repositories/search.repository.ts +++ b/server/src/domain/repositories/search.repository.ts @@ -1,4 +1,4 @@ -import { AssetEntity, AssetFaceEntity, AssetType, SmartInfoEntity } from '@app/infra/entities'; +import { AssetEntity, AssetFaceEntity, AssetType, GeodataPlacesEntity, SmartInfoEntity } from '@app/infra/entities'; import { Paginated } from '../domain.util'; export const ISearchRepository = 'ISearchRepository'; @@ -66,13 +66,13 @@ export interface SearchAssetIDOptions { id?: string; } -export interface SearchUserIDOptions { +export interface SearchUserIdOptions { deviceId?: string; libraryId?: string; - ownerId?: string; + userIds?: string[]; } -export type SearchIDOptions = SearchAssetIDOptions & SearchUserIDOptions; +export type SearchIdOptions = SearchAssetIDOptions & SearchUserIdOptions; export interface SearchStatusOptions { isArchived?: boolean; @@ -83,6 +83,7 @@ export interface SearchStatusOptions { isOffline?: boolean; isReadOnly?: boolean; isVisible?: boolean; + isNotInAlbum?: boolean; type?: AssetType; withArchived?: boolean; withDeleted?: boolean; @@ -132,6 +133,10 @@ export interface SearchEmbeddingOptions { userIds: string[]; } +export interface SearchPeopleOptions { + personIds?: string[]; +} + export interface SearchOrderOptions { orderDirection?: 'ASC' | 'DESC'; } @@ -141,13 +146,18 @@ export interface SearchPaginationOptions { size: number; } -export type AssetSearchOptions = SearchDateOptions & - SearchIDOptions & +type BaseAssetSearchOptions = SearchDateOptions & + SearchIdOptions & SearchExifOptions & SearchOrderOptions & SearchPathOptions & - SearchRelationOptions & - SearchStatusOptions; + SearchStatusOptions & + SearchUserIdOptions & + SearchPeopleOptions; + +export type AssetSearchOptions = BaseAssetSearchOptions & SearchRelationOptions; + +export type AssetSearchOneToOneRelationOptions = BaseAssetSearchOptions & SearchOneToOneRelationOptions; export type AssetSearchBuilderOptions = Omit; @@ -156,7 +166,8 @@ export type SmartSearchOptions = SearchDateOptions & SearchExifOptions & SearchOneToOneRelationOptions & SearchStatusOptions & - SearchUserIDOptions; + SearchUserIdOptions & + SearchPeopleOptions; export interface FaceEmbeddingSearch extends SearchEmbeddingOptions { hasPerson?: boolean; @@ -175,4 +186,6 @@ export interface ISearchRepository { searchSmart(pagination: SearchPaginationOptions, options: SmartSearchOptions): Paginated; searchFaces(search: FaceEmbeddingSearch): Promise; upsert(smartInfo: Partial, embedding?: Embedding): Promise; + searchPlaces(placeName: string): Promise; + deleteAllSearchEmbeddings(): Promise; } diff --git a/server/src/domain/search/dto/search-suggestion.dto.ts b/server/src/domain/search/dto/search-suggestion.dto.ts index 36a7524587..824a1066c4 100644 --- a/server/src/domain/search/dto/search-suggestion.dto.ts +++ b/server/src/domain/search/dto/search-suggestion.dto.ts @@ -1,5 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsEnum, IsNotEmpty, IsOptional, IsString } from 'class-validator'; +import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; +import { Optional } from '../../domain.util'; export enum SearchSuggestionType { COUNTRY = 'country', @@ -16,18 +17,18 @@ export class SearchSuggestionRequestDto { type!: SearchSuggestionType; @IsString() - @IsOptional() + @Optional() country?: string; @IsString() - @IsOptional() + @Optional() state?: string; @IsString() - @IsOptional() + @Optional() make?: string; @IsString() - @IsOptional() + @Optional() model?: string; } diff --git a/server/src/domain/search/dto/search.dto.ts b/server/src/domain/search/dto/search.dto.ts index a4e0396688..c529f6887b 100644 --- a/server/src/domain/search/dto/search.dto.ts +++ b/server/src/domain/search/dto/search.dto.ts @@ -1,5 +1,5 @@ import { AssetOrder } from '@app/domain/asset/dto/asset.dto'; -import { AssetType } from '@app/infra/entities'; +import { AssetType, GeodataPlacesEntity } from '@app/infra/entities'; import { ApiProperty } from '@nestjs/swagger'; import { Transform, Type } from 'class-transformer'; import { IsBoolean, IsEnum, IsInt, IsNotEmpty, IsString, Max, Min } from 'class-validator'; @@ -23,6 +23,7 @@ class BaseSearchDto { isArchived?: boolean; @QueryBoolean({ optional: true }) + @ApiProperty({ default: false }) withArchived?: boolean; @QueryBoolean({ optional: true }) @@ -118,6 +119,12 @@ class BaseSearchDto { @Type(() => Number) @Optional() size?: number; + + @QueryBoolean({ optional: true }) + isNotInAlbum?: boolean; + + @Optional() + personIds?: string[]; } export class MetadataSearchDto extends BaseSearchDto { @@ -234,6 +241,12 @@ export class SearchDto { size?: number; } +export class SearchPlacesDto { + @IsString() + @IsNotEmpty() + name!: string; +} + export class SearchPeopleDto { @IsString() @IsNotEmpty() @@ -244,3 +257,21 @@ export class SearchPeopleDto { @Optional() withHidden?: boolean; } + +export class PlacesResponseDto { + name!: string; + latitude!: number; + longitude!: number; + admin1name?: string; + admin2name?: string; +} + +export function mapPlaces(place: GeodataPlacesEntity): PlacesResponseDto { + return { + name: place.name, + latitude: place.latitude, + longitude: place.longitude, + admin1name: place.admin1Name, + admin2name: place.admin2Name, + }; +} diff --git a/server/src/domain/search/search.service.ts b/server/src/domain/search/search.service.ts index 49cca2ab48..5b56399981 100644 --- a/server/src/domain/search/search.service.ts +++ b/server/src/domain/search/search.service.ts @@ -16,7 +16,15 @@ import { SearchStrategy, } from '../repositories'; import { FeatureFlag, SystemConfigCore } from '../system-config'; -import { MetadataSearchDto, SearchDto, SearchPeopleDto, SmartSearchDto } from './dto'; +import { + MetadataSearchDto, + PlacesResponseDto, + SearchDto, + SearchPeopleDto, + SearchPlacesDto, + SmartSearchDto, + mapPlaces, +} from './dto'; import { SearchSuggestionRequestDto, SearchSuggestionType } from './dto/search-suggestion.dto'; import { SearchResponseDto } from './response-dto'; @@ -41,6 +49,11 @@ export class SearchService { return this.personRepository.getByName(auth.user.id, dto.name, { withHidden: dto.withHidden }); } + async searchPlaces(dto: SearchPlacesDto): Promise { + const places = await this.searchRepository.searchPlaces(dto.name); + return places.map((place) => mapPlaces(place)); + } + async getExploreData(auth: AuthDto): Promise[]> { await this.configCore.requireFeature(FeatureFlag.SEARCH); const options = { maxFields: 12, minAssetsPerField: 5 }; @@ -60,6 +73,7 @@ export class SearchService { async searchMetadata(auth: AuthDto, dto: MetadataSearchDto): Promise { let checksum: Buffer | undefined; + const userIds = await this.getUserIdsToSearch(auth); if (dto.checksum) { const encoding = dto.checksum.length === 28 ? 'base64' : 'hex'; @@ -74,7 +88,7 @@ export class SearchService { { ...dto, checksum, - ownerId: auth.user.id, + userIds, orderDirection: dto.order ? enumToOrder[dto.order] : 'DESC', }, ); @@ -181,26 +195,22 @@ export class SearchService { } async getSearchSuggestions(auth: AuthDto, dto: SearchSuggestionRequestDto): Promise { - if (dto.type === SearchSuggestionType.COUNTRY) { - return this.metadataRepository.getCountries(auth.user.id); + switch (dto.type) { + case SearchSuggestionType.COUNTRY: { + return this.metadataRepository.getCountries(auth.user.id); + } + case SearchSuggestionType.STATE: { + return this.metadataRepository.getStates(auth.user.id, dto.country); + } + case SearchSuggestionType.CITY: { + return this.metadataRepository.getCities(auth.user.id, dto.country, dto.state); + } + case SearchSuggestionType.CAMERA_MAKE: { + return this.metadataRepository.getCameraMakes(auth.user.id, dto.model); + } + case SearchSuggestionType.CAMERA_MODEL: { + return this.metadataRepository.getCameraModels(auth.user.id, dto.make); + } } - - if (dto.type === SearchSuggestionType.STATE) { - return this.metadataRepository.getStates(auth.user.id, dto.country); - } - - if (dto.type === SearchSuggestionType.CITY) { - return this.metadataRepository.getCities(auth.user.id, dto.country, dto.state); - } - - if (dto.type === SearchSuggestionType.CAMERA_MAKE) { - return this.metadataRepository.getCameraMakes(auth.user.id, dto.model); - } - - if (dto.type === SearchSuggestionType.CAMERA_MODEL) { - return this.metadataRepository.getCameraModels(auth.user.id, dto.make); - } - - return []; } } diff --git a/server/src/domain/server-info/server-info.dto.ts b/server/src/domain/server-info/server-info.dto.ts index b631bdc7ef..b3ef426dae 100644 --- a/server/src/domain/server-info/server-info.dto.ts +++ b/server/src/domain/server-info/server-info.dto.ts @@ -1,5 +1,6 @@ -import { FeatureFlags, IVersion } from '@app/domain'; +import { FeatureFlags, IVersion, type VersionType } from '@app/domain'; import { ApiProperty, ApiResponseProperty } from '@nestjs/swagger'; +import type { DateTime } from 'luxon'; import { SystemConfigThemeDto } from '../system-config/dto/system-config-theme.dto'; export class ServerPingResponse { @@ -105,3 +106,10 @@ export class ServerFeaturesDto implements FeatureFlags { sidecar!: boolean; search!: boolean; } + +export interface ReleaseNotification { + isAvailable: VersionType; + checkedAt: DateTime | null; + serverVersion: ServerVersionResponseDto; + releaseVersion: ServerVersionResponseDto; +} diff --git a/server/src/domain/smart-info/smart-info.service.spec.ts b/server/src/domain/smart-info/smart-info.service.spec.ts index 5da7b7824b..9835ea1a53 100644 --- a/server/src/domain/smart-info/smart-info.service.spec.ts +++ b/server/src/domain/smart-info/smart-info.service.spec.ts @@ -71,6 +71,7 @@ describe(SmartInfoService.name, () => { expect(jobMock.queueAll).toHaveBeenCalledWith([{ name: JobName.SMART_SEARCH, data: { id: assetStub.image.id } }]); expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.SMART_SEARCH); + expect(searchMock.deleteAllSearchEmbeddings).not.toHaveBeenCalled(); }); it('should queue all the assets', async () => { @@ -83,6 +84,7 @@ describe(SmartInfoService.name, () => { expect(jobMock.queueAll).toHaveBeenCalledWith([{ name: JobName.SMART_SEARCH, data: { id: assetStub.image.id } }]); expect(assetMock.getAll).toHaveBeenCalled(); + expect(searchMock.deleteAllSearchEmbeddings).toHaveBeenCalled(); }); }); diff --git a/server/src/domain/smart-info/smart-info.service.ts b/server/src/domain/smart-info/smart-info.service.ts index d193b29b51..19d5668cc5 100644 --- a/server/src/domain/smart-info/smart-info.service.ts +++ b/server/src/domain/smart-info/smart-info.service.ts @@ -50,6 +50,10 @@ export class SmartInfoService { return true; } + if (force) { + await this.repository.deleteAllSearchEmbeddings(); + } + const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => { return force ? this.assetRepository.getAll(pagination) diff --git a/server/src/domain/storage-template/storage-template.service.ts b/server/src/domain/storage-template/storage-template.service.ts index d696982540..857d1df327 100644 --- a/server/src/domain/storage-template/storage-template.service.ts +++ b/server/src/domain/storage-template/storage-template.service.ts @@ -117,7 +117,7 @@ export class StorageTemplateService { return true; } const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => - this.assetRepository.getAll(pagination), + this.assetRepository.getAll(pagination, { withExif: true }), ); const users = await this.userRepository.getList(); diff --git a/server/src/domain/system-config/dto/system-config-library.dto.ts b/server/src/domain/system-config/dto/system-config-library.dto.ts index caf73498f2..fdbae600f6 100644 --- a/server/src/domain/system-config/dto/system-config-library.dto.ts +++ b/server/src/domain/system-config/dto/system-config-library.dto.ts @@ -1,12 +1,9 @@ import { validateCronExpression } from '@app/domain'; -import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { IsBoolean, - IsInt, IsNotEmpty, IsObject, - IsPositive, IsString, Validate, ValidateIf, @@ -38,14 +35,6 @@ export class SystemConfigLibraryScanDto { export class SystemConfigLibraryWatchDto { @IsBoolean() enabled!: boolean; - - @IsBoolean() - usePolling!: boolean; - - @IsInt() - @IsPositive() - @ApiProperty({ type: 'integer' }) - interval!: number; } export class SystemConfigLibraryDto { diff --git a/server/src/domain/system-config/dto/system-config-oauth.dto.ts b/server/src/domain/system-config/dto/system-config-oauth.dto.ts index 52aa47a7ea..04159b8d34 100644 --- a/server/src/domain/system-config/dto/system-config-oauth.dto.ts +++ b/server/src/domain/system-config/dto/system-config-oauth.dto.ts @@ -1,4 +1,4 @@ -import { IsBoolean, IsNotEmpty, IsString, IsUrl, ValidateIf } from 'class-validator'; +import { IsBoolean, IsNotEmpty, IsNumber, IsString, IsUrl, Min, ValidateIf } from 'class-validator'; const isEnabled = (config: SystemConfigOAuthDto) => config.enabled; const isOverrideEnabled = (config: SystemConfigOAuthDto) => config.mobileOverrideEnabled; @@ -23,6 +23,10 @@ export class SystemConfigOAuthDto { @IsString() clientSecret!: string; + @IsNumber() + @Min(0) + defaultStorageQuota!: number; + @IsBoolean() enabled!: boolean; @@ -47,4 +51,7 @@ export class SystemConfigOAuthDto { @IsString() storageLabelClaim!: string; + + @IsString() + storageQuotaClaim!: string; } diff --git a/server/src/domain/system-config/system-config.core.ts b/server/src/domain/system-config/system-config.core.ts index 1591e87d63..a9d41d76d7 100644 --- a/server/src/domain/system-config/system-config.core.ts +++ b/server/src/domain/system-config/system-config.core.ts @@ -33,7 +33,7 @@ export const defaults = Object.freeze({ targetVideoCodec: VideoCodec.H264, acceptedVideoCodecs: [VideoCodec.H264], targetAudioCodec: AudioCodec.AAC, - acceptedAudioCodecs: [AudioCodec.AAC], + acceptedAudioCodecs: [AudioCodec.AAC, AudioCodec.MP3, AudioCodec.LIBOPUS], targetResolution: '720', maxBitrate: '0', bframes: -1, @@ -93,6 +93,7 @@ export const defaults = Object.freeze({ buttonText: 'Login with OAuth', clientId: '', clientSecret: '', + defaultStorageQuota: 0, enabled: false, issuerUrl: '', mobileOverrideEnabled: false, @@ -100,6 +101,7 @@ export const defaults = Object.freeze({ scope: 'openid email profile', signingAlgorithm: 'RS256', storageLabelClaim: 'preferred_username', + storageQuotaClaim: 'immich_quota', }, passwordLogin: { enabled: true, @@ -132,8 +134,6 @@ export const defaults = Object.freeze({ }, watch: { enabled: false, - usePolling: false, - interval: 10_000, }, }, server: { diff --git a/server/src/domain/system-config/system-config.service.spec.ts b/server/src/domain/system-config/system-config.service.spec.ts index 8addc63a0f..a3d29b1eee 100644 --- a/server/src/domain/system-config/system-config.service.spec.ts +++ b/server/src/domain/system-config/system-config.service.spec.ts @@ -43,7 +43,7 @@ const updatedConfig = Object.freeze({ threads: 0, preset: 'ultrafast', targetAudioCodec: AudioCodec.AAC, - acceptedAudioCodecs: [AudioCodec.AAC], + acceptedAudioCodecs: [AudioCodec.AAC, AudioCodec.MP3, AudioCodec.LIBOPUS], targetResolution: '720', targetVideoCodec: VideoCodec.H264, acceptedVideoCodecs: [VideoCodec.H264], @@ -93,6 +93,7 @@ const updatedConfig = Object.freeze({ buttonText: 'Login with OAuth', clientId: '', clientSecret: '', + defaultStorageQuota: 0, enabled: false, issuerUrl: '', mobileOverrideEnabled: false, @@ -100,6 +101,7 @@ const updatedConfig = Object.freeze({ scope: 'openid email profile', signingAlgorithm: 'RS256', storageLabelClaim: 'preferred_username', + storageQuotaClaim: 'immich_quota', }, passwordLogin: { enabled: true, @@ -136,8 +138,6 @@ const updatedConfig = Object.freeze({ }, watch: { enabled: false, - usePolling: false, - interval: 10_000, }, }, }); diff --git a/server/src/domain/user/dto/create-user.dto.ts b/server/src/domain/user/dto/create-user.dto.ts index 179974eae8..e6dbb81675 100644 --- a/server/src/domain/user/dto/create-user.dto.ts +++ b/server/src/domain/user/dto/create-user.dto.ts @@ -21,10 +21,6 @@ export class CreateUserDto { @Transform(toSanitized) storageLabel?: string | null; - @Optional({ nullable: true }) - @IsString() - externalPath?: string | null; - @Optional() @IsBoolean() memoriesEnabled?: boolean; diff --git a/server/src/domain/user/dto/update-user.dto.ts b/server/src/domain/user/dto/update-user.dto.ts index 3c6977ff05..1cab11627b 100644 --- a/server/src/domain/user/dto/update-user.dto.ts +++ b/server/src/domain/user/dto/update-user.dto.ts @@ -25,10 +25,6 @@ export class UpdateUserDto { @Transform(toSanitized) storageLabel?: string; - @Optional() - @IsString() - externalPath?: string; - @IsNotEmpty() @IsUUID('4') @ApiProperty({ format: 'uuid' }) diff --git a/server/src/domain/user/response-dto/user-response.dto.ts b/server/src/domain/user/response-dto/user-response.dto.ts index 15800b9933..a82337945e 100644 --- a/server/src/domain/user/response-dto/user-response.dto.ts +++ b/server/src/domain/user/response-dto/user-response.dto.ts @@ -22,7 +22,6 @@ export class UserDto { export class UserResponseDto extends UserDto { storageLabel!: string | null; - externalPath!: string | null; shouldChangePassword!: boolean; isAdmin!: boolean; createdAt!: Date; @@ -50,7 +49,6 @@ export function mapUser(entity: UserEntity): UserResponseDto { return { ...mapSimpleUser(entity), storageLabel: entity.storageLabel, - externalPath: entity.externalPath, shouldChangePassword: entity.shouldChangePassword, isAdmin: entity.isAdmin, createdAt: entity.createdAt, diff --git a/server/src/domain/user/user.core.ts b/server/src/domain/user/user.core.ts index 691bc7de49..6134e97cea 100644 --- a/server/src/domain/user/user.core.ts +++ b/server/src/domain/user/user.core.ts @@ -1,6 +1,5 @@ import { LibraryType, UserEntity } from '@app/infra/entities'; import { BadRequestException, ForbiddenException } from '@nestjs/common'; -import path from 'node:path'; import sanitize from 'sanitize-filename'; import { ICryptoRepository, ILibraryRepository, IUserRepository } from '../repositories'; import { UserResponseDto } from './response-dto'; @@ -42,7 +41,6 @@ export class UserCore { // Users can never update the isAdmin property. delete dto.isAdmin; delete dto.storageLabel; - delete dto.externalPath; } else if (dto.isAdmin && user.id !== id) { // Admin cannot create another admin. throw new BadRequestException('The server already has an admin'); @@ -70,12 +68,6 @@ export class UserCore { dto.storageLabel = null; } - if (dto.externalPath === '') { - dto.externalPath = null; - } else if (dto.externalPath) { - dto.externalPath = path.normalize(dto.externalPath); - } - return this.userRepository.update(id, dto); } diff --git a/server/src/immich/api-v1/asset/asset-repository.ts b/server/src/immich/api-v1/asset/asset-repository.ts index 7d55fa790e..2f54db27d0 100644 --- a/server/src/immich/api-v1/asset/asset-repository.ts +++ b/server/src/immich/api-v1/asset/asset-repository.ts @@ -1,10 +1,8 @@ -import { AssetEntity, ExifEntity } from '@app/infra/entities'; -import { OptionalBetween } from '@app/infra/infra.utils'; +import { AssetEntity } from '@app/infra/entities'; import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { In } from 'typeorm/find-options/operator/In.js'; import { Repository } from 'typeorm/repository/Repository.js'; -import { AssetSearchDto } from './dto/asset-search.dto'; import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto'; import { SearchPropertiesDto } from './dto/search-properties.dto'; import { CuratedLocationsResponseDto } from './response-dto/curated-locations-response.dto'; @@ -21,7 +19,6 @@ export interface AssetOwnerCheck extends AssetCheck { export interface IAssetRepositoryV1 { get(id: string): Promise; - getAllByUserId(userId: string, dto: AssetSearchDto): Promise; getLocationsByUserId(userId: string): Promise; getDetectedObjectsByUserId(userId: string): Promise; getSearchPropertiesByUserId(userId: string): Promise; @@ -34,10 +31,7 @@ export const IAssetRepositoryV1 = 'IAssetRepositoryV1'; @Injectable() export class AssetRepositoryV1 implements IAssetRepositoryV1 { - constructor( - @InjectRepository(AssetEntity) private assetRepository: Repository, - @InjectRepository(ExifEntity) private exifRepository: Repository, - ) {} + constructor(@InjectRepository(AssetEntity) private assetRepository: Repository) {} getSearchPropertiesByUserId(userId: string): Promise { return this.assetRepository @@ -89,33 +83,6 @@ export class AssetRepositoryV1 implements IAssetRepositoryV1 { ); } - /** - * Get all assets belong to the user on the database - * @param ownerId - */ - getAllByUserId(ownerId: string, dto: AssetSearchDto): Promise { - return this.assetRepository.find({ - where: { - ownerId, - isVisible: true, - isFavorite: dto.isFavorite, - isArchived: dto.isArchived, - updatedAt: OptionalBetween(dto.updatedAfter, dto.updatedBefore), - }, - relations: { - exifInfo: true, - tags: true, - stack: { assets: true }, - }, - skip: dto.skip || 0, - take: dto.take, - order: { - fileCreatedAt: 'DESC', - }, - withDeleted: true, - }); - } - get(id: string): Promise { return this.assetRepository.findOne({ where: { id }, diff --git a/server/src/immich/api-v1/asset/asset.service.spec.ts b/server/src/immich/api-v1/asset/asset.service.spec.ts index 9f0aa371e8..48354d440e 100644 --- a/server/src/immich/api-v1/asset/asset.service.spec.ts +++ b/server/src/immich/api-v1/asset/asset.service.spec.ts @@ -77,7 +77,6 @@ describe('AssetService', () => { beforeEach(() => { assetRepositoryMockV1 = { get: jest.fn(), - getAllByUserId: jest.fn(), getDetectedObjectsByUserId: jest.fn(), getLocationsByUserId: jest.fn(), getSearchPropertiesByUserId: jest.fn(), diff --git a/server/src/immich/api-v1/asset/asset.service.ts b/server/src/immich/api-v1/asset/asset.service.ts index f438e55e9c..5dcc487be4 100644 --- a/server/src/immich/api-v1/asset/asset.service.ts +++ b/server/src/immich/api-v1/asset/asset.service.ts @@ -114,8 +114,11 @@ export class AssetService { public async getAllAssets(auth: AuthDto, dto: AssetSearchDto): Promise { const userId = dto.userId || auth.user.id; await this.access.requirePermission(auth, Permission.TIMELINE_READ, userId); - const assets = await this.assetRepositoryV1.getAllByUserId(userId, dto); - return assets.map((asset) => mapAsset(asset, { withStack: true })); + const assets = await this.assetRepository.getAllByFileCreationDate( + { take: dto.take ?? 1000, skip: dto.skip }, + { ...dto, userIds: [userId], withDeleted: true, orderDirection: 'DESC', withExif: true, isVisible: true }, + ); + return assets.items.map((asset) => mapAsset(asset)); } async serveThumbnail(auth: AuthDto, assetId: string, dto: GetAssetThumbnailDto): Promise { @@ -338,7 +341,6 @@ export class AssetService { fileCreatedAt: dto.fileCreatedAt, fileModifiedAt: dto.fileModifiedAt, localDateTime: dto.fileCreatedAt, - deletedAt: null, type: mimeTypes.assetType(file.originalPath), isFavorite: dto.isFavorite, @@ -346,17 +348,9 @@ export class AssetService { duration: dto.duration || null, isVisible: dto.isVisible ?? true, livePhotoVideo: livePhotoAssetId === null ? null : ({ id: livePhotoAssetId } as AssetEntity), - resizePath: null, - webpPath: null, - thumbhash: null, - encodedVideoPath: null, - tags: [], - sharedLinks: [], originalFileName: parse(file.originalName).name, - faces: [], sidecarPath: sidecarPath || null, isReadOnly: dto.isReadOnly ?? false, - isExternal: dto.isExternal ?? false, isOffline: dto.isOffline ?? false, }); diff --git a/server/src/immich/api-v1/asset/dto/create-asset.dto.ts b/server/src/immich/api-v1/asset/dto/create-asset.dto.ts index ae347e61b6..9850384d96 100644 --- a/server/src/immich/api-v1/asset/dto/create-asset.dto.ts +++ b/server/src/immich/api-v1/asset/dto/create-asset.dto.ts @@ -3,7 +3,10 @@ import { ApiProperty } from '@nestjs/swagger'; import { Transform, Type } from 'class-transformer'; import { IsBoolean, IsDate, IsNotEmpty, IsString } from 'class-validator'; -export class CreateAssetBase { +export class CreateAssetDto { + @ValidateUUID({ optional: true }) + libraryId?: string; + @IsNotEmpty() @IsString() deviceAssetId!: string; @@ -22,6 +25,10 @@ export class CreateAssetBase { @Type(() => Date) fileModifiedAt!: Date; + @Optional() + @IsString() + duration?: string; + @Optional() @IsBoolean() @Transform(toBoolean) @@ -37,28 +44,16 @@ export class CreateAssetBase { @Transform(toBoolean) isVisible?: boolean; - @Optional() - @IsString() - duration?: string; - - @Optional() - @IsBoolean() - isExternal?: boolean; - @Optional() @IsBoolean() + @Transform(toBoolean) isOffline?: boolean; -} -export class CreateAssetDto extends CreateAssetBase { @Optional() @IsBoolean() @Transform(toBoolean) isReadOnly?: boolean; - @ValidateUUID({ optional: true }) - libraryId?: string; - // The properties below are added to correctly generate the API docs // and client SDKs. Validation should be handled in the controller. @ApiProperty({ type: 'string', format: 'binary' }) diff --git a/server/src/immich/controllers/library.controller.ts b/server/src/immich/controllers/library.controller.ts index 56dd5d8e76..9ad7119799 100644 --- a/server/src/immich/controllers/library.controller.ts +++ b/server/src/immich/controllers/library.controller.ts @@ -5,11 +5,14 @@ import { LibraryStatsResponseDto, LibraryResponseDto as ResponseDto, ScanLibraryDto, + SearchLibraryDto, UpdateLibraryDto as UpdateDto, + ValidateLibraryDto, + ValidateLibraryResponseDto, } from '@app/domain'; -import { Body, Controller, Delete, Get, Param, Post, Put } from '@nestjs/common'; +import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { Auth, Authenticated } from '../app.guard'; +import { AdminRoute, Auth, Authenticated } from '../app.guard'; import { UseValidation } from '../app.utils'; import { UUIDParamDto } from './dto/uuid-param.dto'; @@ -17,12 +20,13 @@ import { UUIDParamDto } from './dto/uuid-param.dto'; @Controller('library') @Authenticated() @UseValidation() +@AdminRoute() export class LibraryController { constructor(private service: LibraryService) {} @Get() - getLibraries(@Auth() auth: AuthDto): Promise { - return this.service.getAllForUser(auth); + getAllLibraries(@Auth() auth: AuthDto, @Query() dto: SearchLibraryDto): Promise { + return this.service.getAll(auth, dto); } @Post() @@ -36,11 +40,22 @@ export class LibraryController { } @Get(':id') - getLibraryInfo(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { + getLibrary(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.get(auth, id); } + @Post(':id/validate') + @HttpCode(200) + validate( + @Auth() auth: AuthDto, + @Param() { id }: UUIDParamDto, + @Body() dto: ValidateLibraryDto, + ): Promise { + return this.service.validate(auth, id, dto); + } + @Delete(':id') + @HttpCode(HttpStatus.NO_CONTENT) deleteLibrary(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.delete(auth, id); } @@ -51,11 +66,13 @@ export class LibraryController { } @Post(':id/scan') + @HttpCode(HttpStatus.NO_CONTENT) scanLibrary(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @Body() dto: ScanLibraryDto) { return this.service.queueScan(auth, id, dto); } @Post(':id/removeOffline') + @HttpCode(HttpStatus.NO_CONTENT) removeOfflineFiles(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto) { return this.service.queueRemoveOffline(auth, id); } diff --git a/server/src/immich/controllers/partner.controller.ts b/server/src/immich/controllers/partner.controller.ts index 75f716f58d..6370d8e718 100644 --- a/server/src/immich/controllers/partner.controller.ts +++ b/server/src/immich/controllers/partner.controller.ts @@ -15,6 +15,7 @@ export class PartnerController { @Get() @ApiQuery({ name: 'direction', type: 'string', enum: PartnerDirection, required: true }) + // TODO: remove 'direction' and convert to full query dto getPartners(@Auth() auth: AuthDto, @Query('direction') direction: PartnerDirection): Promise { return this.service.getAll(auth, direction); } diff --git a/server/src/immich/controllers/search.controller.ts b/server/src/immich/controllers/search.controller.ts index f8438b2e35..b807da9665 100644 --- a/server/src/immich/controllers/search.controller.ts +++ b/server/src/immich/controllers/search.controller.ts @@ -2,15 +2,17 @@ import { AuthDto, MetadataSearchDto, PersonResponseDto, + PlacesResponseDto, SearchDto, SearchExploreResponseDto, SearchPeopleDto, + SearchPlacesDto, SearchResponseDto, SearchService, SmartSearchDto, } from '@app/domain'; import { SearchSuggestionRequestDto } from '@app/domain/search/dto/search-suggestion.dto'; -import { Controller, Get, Query } from '@nestjs/common'; +import { Body, Controller, Get, Post, Query } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { Auth, Authenticated } from '../app.guard'; import { UseValidation } from '../app.utils'; @@ -22,13 +24,13 @@ import { UseValidation } from '../app.utils'; export class SearchController { constructor(private service: SearchService) {} - @Get('metadata') - searchMetadata(@Auth() auth: AuthDto, @Query() dto: MetadataSearchDto): Promise { + @Post('metadata') + searchMetadata(@Auth() auth: AuthDto, @Body() dto: MetadataSearchDto): Promise { return this.service.searchMetadata(auth, dto); } - @Get('smart') - searchSmart(@Auth() auth: AuthDto, @Query() dto: SmartSearchDto): Promise { + @Post('smart') + searchSmart(@Auth() auth: AuthDto, @Body() dto: SmartSearchDto): Promise { return this.service.searchSmart(auth, dto); } @@ -48,6 +50,11 @@ export class SearchController { return this.service.searchPerson(auth, dto); } + @Get('places') + searchPlaces(@Query() dto: SearchPlacesDto): Promise { + return this.service.searchPlaces(dto); + } + @Get('suggestions') getSearchSuggestions(@Auth() auth: AuthDto, @Query() dto: SearchSuggestionRequestDto): Promise { return this.service.getSearchSuggestions(auth, dto); diff --git a/server/src/infra/database.config.ts b/server/src/infra/database.config.ts index 93926e51cf..773e79f8a0 100644 --- a/server/src/infra/database.config.ts +++ b/server/src/infra/database.config.ts @@ -30,4 +30,4 @@ export const databaseConfig: PostgresConnectionOptions = { export const dataSource = new DataSource(databaseConfig); export const vectorExt = - process.env.VECTOR_EXTENSION === 'pgvector' ? DatabaseExtension.VECTOR : DatabaseExtension.VECTORS; + process.env.DB_VECTOR_EXTENSION === 'pgvector' ? DatabaseExtension.VECTOR : DatabaseExtension.VECTORS; diff --git a/server/src/infra/entities/asset.entity.ts b/server/src/infra/entities/asset.entity.ts index 10973be74e..3732711581 100644 --- a/server/src/infra/entities/asset.entity.ts +++ b/server/src/infra/entities/asset.entity.ts @@ -85,6 +85,7 @@ export class AssetEntity { @DeleteDateColumn({ type: 'timestamptz', nullable: true }) deletedAt!: Date | null; + @Index('idx_asset_file_created_at') @Column({ type: 'timestamptz' }) fileCreatedAt!: Date; diff --git a/server/src/infra/entities/geodata-admin1.entity.ts b/server/src/infra/entities/geodata-admin1.entity.ts deleted file mode 100644 index 36cf0a805e..0000000000 --- a/server/src/infra/entities/geodata-admin1.entity.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Column, Entity, PrimaryColumn } from 'typeorm'; - -@Entity('geodata_admin1') -export class GeodataAdmin1Entity { - @PrimaryColumn({ type: 'varchar' }) - key!: string; - - @Column({ type: 'varchar' }) - name!: string; -} diff --git a/server/src/infra/entities/geodata-admin2.entity.ts b/server/src/infra/entities/geodata-admin2.entity.ts deleted file mode 100644 index bd03e83776..0000000000 --- a/server/src/infra/entities/geodata-admin2.entity.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Column, Entity, PrimaryColumn } from 'typeorm'; - -@Entity('geodata_admin2') -export class GeodataAdmin2Entity { - @PrimaryColumn({ type: 'varchar' }) - key!: string; - - @Column({ type: 'varchar' }) - name!: string; -} diff --git a/server/src/infra/entities/geodata-places.entity.ts b/server/src/infra/entities/geodata-places.entity.ts index 244e4261b0..966a50d5c9 100644 --- a/server/src/infra/entities/geodata-places.entity.ts +++ b/server/src/infra/entities/geodata-places.entity.ts @@ -1,6 +1,4 @@ -import { GeodataAdmin1Entity } from '@app/infra/entities/geodata-admin1.entity'; -import { GeodataAdmin2Entity } from '@app/infra/entities/geodata-admin2.entity'; -import { Column, Entity, ManyToOne, PrimaryColumn } from 'typeorm'; +import { Column, Entity, PrimaryColumn } from 'typeorm'; @Entity('geodata_places', { synchronize: false }) export class GeodataPlacesEntity { @@ -21,7 +19,7 @@ export class GeodataPlacesEntity { // asExpression: 'll_to_earth((latitude)::double precision, (longitude)::double precision)', // type: 'earth', // }) - earthCoord!: unknown; + // earthCoord!: unknown; @Column({ type: 'char', length: 2 }) countryCode!: string; @@ -32,27 +30,14 @@ export class GeodataPlacesEntity { @Column({ type: 'varchar', length: 80, nullable: true }) admin2Code!: string; - @Column({ - type: 'varchar', - generatedType: 'STORED', - asExpression: `"countryCode" || '.' || "admin1Code"`, - nullable: true, - }) - admin1Key!: string; + @Column({ type: 'varchar', nullable: true }) + admin1Name!: string; - @ManyToOne(() => GeodataAdmin1Entity, { eager: true, nullable: true, createForeignKeyConstraints: false }) - admin1!: GeodataAdmin1Entity; + @Column({ type: 'varchar', nullable: true }) + admin2Name!: string; - @Column({ - type: 'varchar', - generatedType: 'STORED', - asExpression: `"countryCode" || '.' || "admin1Code" || '.' || "admin2Code"`, - nullable: true, - }) - admin2Key!: string; - - @ManyToOne(() => GeodataAdmin2Entity, { eager: true, nullable: true, createForeignKeyConstraints: false }) - admin2!: GeodataAdmin2Entity; + @Column({ type: 'varchar', nullable: true }) + alternateNames!: string; @Column({ type: 'date' }) modificationDate!: Date; diff --git a/server/src/infra/entities/index.ts b/server/src/infra/entities/index.ts index 957e15a887..af620790ef 100644 --- a/server/src/infra/entities/index.ts +++ b/server/src/infra/entities/index.ts @@ -7,8 +7,6 @@ import { AssetStackEntity } from './asset-stack.entity'; import { AssetEntity } from './asset.entity'; import { AuditEntity } from './audit.entity'; import { ExifEntity } from './exif.entity'; -import { GeodataAdmin1Entity } from './geodata-admin1.entity'; -import { GeodataAdmin2Entity } from './geodata-admin2.entity'; import { GeodataPlacesEntity } from './geodata-places.entity'; import { LibraryEntity } from './library.entity'; import { MoveEntity } from './move.entity'; @@ -32,8 +30,6 @@ export * from './asset-stack.entity'; export * from './asset.entity'; export * from './audit.entity'; export * from './exif.entity'; -export * from './geodata-admin1.entity'; -export * from './geodata-admin2.entity'; export * from './geodata-places.entity'; export * from './library.entity'; export * from './move.entity'; @@ -59,8 +55,6 @@ export const databaseEntities = [ AuditEntity, ExifEntity, GeodataPlacesEntity, - GeodataAdmin1Entity, - GeodataAdmin2Entity, MoveEntity, PartnerEntity, PersonEntity, diff --git a/server/src/infra/entities/system-config.entity.ts b/server/src/infra/entities/system-config.entity.ts index b0aef8f2be..e2d0c71f6b 100644 --- a/server/src/infra/entities/system-config.entity.ts +++ b/server/src/infra/entities/system-config.entity.ts @@ -7,7 +7,7 @@ export class SystemConfigEntity { key!: SystemConfigKey; @Column({ type: 'varchar', nullable: true, transformer: { to: JSON.stringify, from: JSON.parse } }) - value!: T; + value!: T | T[]; } export type SystemConfigValue = string | number | boolean; @@ -51,8 +51,6 @@ export enum SystemConfigKey { LIBRARY_SCAN_CRON_EXPRESSION = 'library.scan.cronExpression', LIBRARY_WATCH_ENABLED = 'library.watch.enabled', - LIBRARY_WATCH_USE_POLLING = 'library.watch.usePolling', - LIBRARY_WATCH_INTERVAL = 'library.watch.interval', LOGGING_ENABLED = 'logging.enabled', LOGGING_LEVEL = 'logging.level', @@ -82,6 +80,7 @@ export enum SystemConfigKey { OAUTH_BUTTON_TEXT = 'oauth.buttonText', OAUTH_CLIENT_ID = 'oauth.clientId', OAUTH_CLIENT_SECRET = 'oauth.clientSecret', + OAUTH_DEFAULT_STORAGE_QUOTA = 'oauth.defaultStorageQuota', OAUTH_ENABLED = 'oauth.enabled', OAUTH_ISSUER_URL = 'oauth.issuerUrl', OAUTH_MOBILE_OVERRIDE_ENABLED = 'oauth.mobileOverrideEnabled', @@ -89,6 +88,7 @@ export enum SystemConfigKey { OAUTH_SCOPE = 'oauth.scope', OAUTH_SIGNING_ALGORITHM = 'oauth.signingAlgorithm', OAUTH_STORAGE_LABEL_CLAIM = 'oauth.storageLabelClaim', + OAUTH_STORAGE_QUOTA_CLAIM = 'oauth.storageQuotaClaim', PASSWORD_LOGIN_ENABLED = 'passwordLogin.enabled', @@ -118,6 +118,13 @@ export enum TranscodePolicy { DISABLED = 'disabled', } +export enum TranscodeTarget { + NONE, + AUDIO, + VIDEO, + ALL, +} + export enum VideoCodec { H264 = 'h264', HEVC = 'hevc', @@ -222,6 +229,7 @@ export interface SystemConfig { buttonText: string; clientId: string; clientSecret: string; + defaultStorageQuota: number; enabled: boolean; issuerUrl: string; mobileOverrideEnabled: boolean; @@ -229,6 +237,7 @@ export interface SystemConfig { scope: string; signingAlgorithm: string; storageLabelClaim: string; + storageQuotaClaim: string; }; passwordLogin: { enabled: boolean; @@ -261,8 +270,6 @@ export interface SystemConfig { }; watch: { enabled: boolean; - usePolling: boolean; - interval: number; }; }; server: { diff --git a/server/src/infra/entities/user.entity.ts b/server/src/infra/entities/user.entity.ts index dbc563ee0a..c574595ea8 100644 --- a/server/src/infra/entities/user.entity.ts +++ b/server/src/infra/entities/user.entity.ts @@ -43,9 +43,6 @@ export class UserEntity { @Column({ type: 'varchar', unique: true, default: null }) storageLabel!: string | null; - @Column({ type: 'varchar', default: null }) - externalPath!: string | null; - @Column({ default: '', select: false }) password?: string; diff --git a/server/src/infra/infra.utils.ts b/server/src/infra/infra.utils.ts index 89bd319662..745f5a38ff 100644 --- a/server/src/infra/infra.utils.ts +++ b/server/src/infra/infra.utils.ts @@ -139,24 +139,51 @@ export function searchAssetBuilder( ); const exifInfo = _.omitBy(_.pick(options, ['city', 'country', 'lensModel', 'make', 'model', 'state']), _.isUndefined); - if (Object.keys(exifInfo).length > 0) { - builder.leftJoin(`${builder.alias}.exifInfo`, 'exifInfo'); + const hasExifQuery = Object.keys(exifInfo).length > 0; + + if (options.withExif && !hasExifQuery) { + builder.leftJoinAndSelect(`${builder.alias}.exifInfo`, 'exifInfo'); + } + + if (hasExifQuery) { + options.withExif + ? builder.leftJoinAndSelect(`${builder.alias}.exifInfo`, 'exifInfo') + : builder.leftJoin(`${builder.alias}.exifInfo`, 'exifInfo'); + builder.andWhere({ exifInfo }); } - const id = _.pick(options, ['checksum', 'deviceAssetId', 'deviceId', 'id', 'libraryId', 'ownerId']); + const id = _.pick(options, ['checksum', 'deviceAssetId', 'deviceId', 'id', 'libraryId']); builder.andWhere(_.omitBy(id, _.isUndefined)); + if (options.userIds) { + builder.andWhere(`${builder.alias}.ownerId IN (:...userIds)`, { userIds: options.userIds }); + } + const path = _.pick(options, ['encodedVideoPath', 'originalFileName', 'originalPath', 'resizePath', 'webpPath']); builder.andWhere(_.omitBy(path, _.isUndefined)); const status = _.pick(options, ['isExternal', 'isFavorite', 'isOffline', 'isReadOnly', 'isVisible', 'type']); - const { isArchived, isEncoded, isMotion, withArchived } = options; + const { + isArchived, + isEncoded, + isMotion, + withArchived, + isNotInAlbum, + withFaces, + withPeople, + withSmartInfo, + personIds, + withExif, + withStacked, + trashedAfter, + trashedBefore, + } = options; builder.andWhere( _.omitBy( { ...status, - isArchived: isArchived ?? withArchived, + isArchived: isArchived ?? (withArchived ? undefined : false), encodedVideoPath: isEncoded ? Not(IsNull()) : undefined, livePhotoVideoId: isMotion ? Not(IsNull()) : undefined, }, @@ -164,23 +191,38 @@ export function searchAssetBuilder( ), ); - if (options.withExif) { - builder.leftJoinAndSelect(`${builder.alias}.exifInfo`, 'exifInfo'); + if (isNotInAlbum) { + builder + .leftJoin(`${builder.alias}.albums`, 'albums') + .andWhere('albums.id IS NULL') + .andWhere(`${builder.alias}.isVisible = true`); } - if (options.withFaces || options.withPeople) { + if (withFaces || withPeople) { builder.leftJoinAndSelect(`${builder.alias}.faces`, 'faces'); } - if (options.withPeople) { + if (withPeople) { builder.leftJoinAndSelect(`${builder.alias}.person`, 'person'); } - if (options.withSmartInfo) { + if (withSmartInfo) { builder.leftJoinAndSelect(`${builder.alias}.smartInfo`, 'smartInfo'); } - if (options.withStacked) { + if (personIds && personIds.length > 0) { + builder + .leftJoin(`${builder.alias}.faces`, 'faces') + .andWhere('faces.personId IN (:...personIds)', { personIds }) + .addGroupBy(`${builder.alias}.id`) + .having('COUNT(DISTINCT faces.personId) = :personCount', { personCount: personIds.length }); + + if (withExif) { + builder.addGroupBy('exifInfo.assetId'); + } + } + + if (withStacked) { builder .leftJoinAndSelect(`${builder.alias}.stack`, 'stack') .leftJoinAndSelect('stack.assets', 'stackedAssets') @@ -189,8 +231,7 @@ export function searchAssetBuilder( ); } - const withDeleted = - options.withDeleted ?? (options.trashedAfter !== undefined || options.trashedBefore !== undefined); + const withDeleted = options.withDeleted ?? (trashedAfter !== undefined || trashedBefore !== undefined); if (withDeleted) { builder.withDeleted(); } diff --git a/server/src/infra/migrations/1707000751533-AddVectorsToSearchPath.ts b/server/src/infra/migrations/1707000751533-AddVectorsToSearchPath.ts index e83e4b4fb0..11c84cf970 100644 --- a/server/src/infra/migrations/1707000751533-AddVectorsToSearchPath.ts +++ b/server/src/infra/migrations/1707000751533-AddVectorsToSearchPath.ts @@ -4,11 +4,11 @@ export class AddVectorsToSearchPath1707000751533 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { const res = await queryRunner.query(`SELECT current_database() as db`); const databaseName = res[0]['db']; - await queryRunner.query(`ALTER DATABASE ${databaseName} SET search_path TO "$user", public, vectors`); + await queryRunner.query(`ALTER DATABASE "${databaseName}" SET search_path TO "$user", public, vectors`); } public async down(queryRunner: QueryRunner): Promise { const databaseName = await queryRunner.query(`SELECT current_database()`); - await queryRunner.query(`ALTER DATABASE ${databaseName} SET search_path TO "$user", public`); + await queryRunner.query(`ALTER DATABASE "${databaseName}" SET search_path TO "$user", public`); } } diff --git a/server/src/infra/migrations/1708059341865-GeodataLocationSearch.ts b/server/src/infra/migrations/1708059341865-GeodataLocationSearch.ts new file mode 100644 index 0000000000..136ca2598d --- /dev/null +++ b/server/src/infra/migrations/1708059341865-GeodataLocationSearch.ts @@ -0,0 +1,152 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class GeodataLocationSearch1708059341865 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS pg_trgm`); + await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS unaccent`); + + // https://stackoverflow.com/a/11007216 + await queryRunner.query(` + CREATE OR REPLACE FUNCTION f_unaccent(text) + RETURNS text + LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT + RETURN unaccent('unaccent', $1)`); + + await queryRunner.query(`ALTER TABLE geodata_places ADD COLUMN "admin1Name" varchar`); + await queryRunner.query(`ALTER TABLE geodata_places ADD COLUMN "admin2Name" varchar`); + + await queryRunner.query(` + UPDATE geodata_places + SET "admin1Name" = admin1.name + FROM geodata_admin1 admin1 + WHERE admin1.key = "admin1Key"`); + + await queryRunner.query(` + UPDATE geodata_places + SET "admin2Name" = admin2.name + FROM geodata_admin2 admin2 + WHERE admin2.key = "admin2Key"`); + + await queryRunner.query(`DROP TABLE geodata_admin1 CASCADE`); + await queryRunner.query(`DROP TABLE geodata_admin2 CASCADE`); + + await queryRunner.query(` + ALTER TABLE geodata_places + DROP COLUMN "admin1Key", + DROP COLUMN "admin2Key"`); + + await queryRunner.query(` + CREATE INDEX idx_geodata_places_name + ON geodata_places + USING gin (f_unaccent(name) gin_trgm_ops)`); + + await queryRunner.query(` + CREATE INDEX idx_geodata_places_admin1_name + ON geodata_places + USING gin (f_unaccent("admin1Name") gin_trgm_ops)`); + + await queryRunner.query(` + CREATE INDEX idx_geodata_places_admin2_name + ON geodata_places + USING gin (f_unaccent("admin2Name") gin_trgm_ops)`); + + await queryRunner.query( + ` + DELETE FROM "typeorm_metadata" + WHERE + "type" = $1 AND + "name" = $2 AND + "database" = $3 AND + "schema" = $4 AND + "table" = $5`, + ['GENERATED_COLUMN', 'admin1Key', 'immich', 'public', 'geodata_places'], + ); + + await queryRunner.query( + ` + DELETE FROM "typeorm_metadata" + WHERE + "type" = $1 AND + "name" = $2 AND + "database" = $3 AND + "schema" = $4 AND + "table" = $5`, + ['GENERATED_COLUMN', 'admin2Key', 'immich', 'public', 'geodata_places'], + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TABLE "geodata_admin1" ( + "key" character varying NOT NULL, + "name" character varying NOT NULL, + CONSTRAINT "PK_3fe3a89c5aac789d365871cb172" PRIMARY KEY ("key") + )`); + + await queryRunner.query(` + CREATE TABLE "geodata_admin2" ( + "key" character varying NOT NULL, + "name" character varying NOT NULL, + CONSTRAINT "PK_1e3886455dbb684d6f6b4756726" PRIMARY KEY ("key") + )`); + + await queryRunner.query(` + ALTER TABLE geodata_places + ADD COLUMN "admin1Key" character varying + GENERATED ALWAYS AS ("countryCode" || '.' || "admin1Code") STORED, + ADD COLUMN "admin2Key" character varying + GENERATED ALWAYS AS ("countryCode" || '.' || "admin1Code" || '.' || "admin2Code") STORED`); + + await queryRunner.query( + ` + INSERT INTO "geodata_admin1" + SELECT DISTINCT + "admin1Key" AS "key", + "admin1Name" AS "name" + FROM geodata_places + WHERE "admin1Name" IS NOT NULL`, + ); + + await queryRunner.query( + ` + INSERT INTO "geodata_admin2" + SELECT DISTINCT + "admin2Key" AS "key", + "admin2Name" AS "name" + FROM geodata_places + WHERE "admin2Name" IS NOT NULL`, + ); + + await queryRunner.query(` + UPDATE geodata_places + SET "admin1Name" = admin1.name + FROM geodata_admin1 admin1 + WHERE admin1.key = "admin1Key"`); + + await queryRunner.query(` + UPDATE geodata_places + SET "admin2Name" = admin2.name + FROM geodata_admin2 admin2 + WHERE admin2.key = "admin2Key";`); + + await queryRunner.query( + ` + INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") + VALUES ($1, $2, $3, $4, $5, $6)`, + ['immich', 'public', 'geodata_places', 'GENERATED_COLUMN', 'admin1Key', '"countryCode" || \'.\' || "admin1Code"'], + ); + + await queryRunner.query( + `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") + VALUES ($1, $2, $3, $4, $5, $6)`, + [ + 'immich', + 'public', + 'geodata_places', + 'GENERATED_COLUMN', + 'admin2Key', + '"countryCode" || \'.\' || "admin1Code" || \'.\' || "admin2Code"', + ], + ); + } +} diff --git a/server/src/infra/migrations/1708116312820-GeonamesEnhancement.ts b/server/src/infra/migrations/1708116312820-GeonamesEnhancement.ts new file mode 100644 index 0000000000..0cea9a0411 --- /dev/null +++ b/server/src/infra/migrations/1708116312820-GeonamesEnhancement.ts @@ -0,0 +1,18 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class GeonamesEnhancement1708116312820 implements MigrationInterface { + name = 'GeonamesEnhancement1708116312820' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE geodata_places ADD COLUMN "alternateNames" varchar`); + await queryRunner.query(` + CREATE INDEX idx_geodata_places_admin2_alternate_names + ON geodata_places + USING gin (f_unaccent("alternateNames") gin_trgm_ops)`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE geodata_places DROP COLUMN "alternateNames"`); + } + +} diff --git a/server/src/infra/migrations/1708227417898-AddFileCreatedAtIndex.ts b/server/src/infra/migrations/1708227417898-AddFileCreatedAtIndex.ts new file mode 100644 index 0000000000..f7ca40cd46 --- /dev/null +++ b/server/src/infra/migrations/1708227417898-AddFileCreatedAtIndex.ts @@ -0,0 +1,12 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddFileCreatedAtIndex1708227417898 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE INDEX idx_asset_file_created_at ON assets ("fileCreatedAt")`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP INDEX idx_asset_file_created_at`); + } +} diff --git a/server/src/infra/migrations/1708425975121-RemoveExternalPath.ts b/server/src/infra/migrations/1708425975121-RemoveExternalPath.ts new file mode 100644 index 0000000000..6c43e351f9 --- /dev/null +++ b/server/src/infra/migrations/1708425975121-RemoveExternalPath.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RemoveExternalPath1708425975121 implements MigrationInterface { + name = 'RemoveExternalPath1708425975121'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "externalPath"`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "users" ADD "externalPath" character varying`); + } +} diff --git a/server/src/infra/migrations/1709150004123-RemoveLibraryWatchPollingOption.ts b/server/src/infra/migrations/1709150004123-RemoveLibraryWatchPollingOption.ts new file mode 100644 index 0000000000..8b7ff3a675 --- /dev/null +++ b/server/src/infra/migrations/1709150004123-RemoveLibraryWatchPollingOption.ts @@ -0,0 +1,12 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RemoveLibraryWatchPollingOption1709150004123 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'library.watch.usePolling'`); + await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'library.watch.interval'`); + } + + public async down(): Promise { + // noop + } +} diff --git a/server/src/infra/repositories/asset.repository.ts b/server/src/infra/repositories/asset.repository.ts index 215d280f4c..a31ee2ad44 100644 --- a/server/src/infra/repositories/asset.repository.ts +++ b/server/src/infra/repositories/asset.repository.ts @@ -2,6 +2,7 @@ import { AssetBuilderOptions, AssetCreate, AssetExploreFieldOptions, + AssetSearchOneToOneRelationOptions, AssetSearchOptions, AssetStats, AssetStatsOptions, @@ -175,8 +176,12 @@ export class AssetRepository implements IAssetRepository { }); } - getByUserId(pagination: PaginationOptions, userId: string, options: AssetSearchOptions = {}): Paginated { - return this.getAll(pagination, { ...options, id: userId }); + getByUserId( + pagination: PaginationOptions, + userId: string, + options: Omit = {}, + ): Paginated { + return this.getAll(pagination, { ...options, userIds: [userId] }); } @GenerateSql({ params: [[DummyValue.UUID]] }) @@ -205,6 +210,29 @@ export class AssetRepository implements IAssetRepository { }); } + @GenerateSql({ + params: [ + { skip: 20_000, take: 10_000 }, + { + takenBefore: DummyValue.DATE, + userIds: [DummyValue.UUID], + }, + ], + }) + getAllByFileCreationDate( + pagination: PaginationOptions, + options: AssetSearchOneToOneRelationOptions = {}, + ): Paginated { + let builder = this.repository.createQueryBuilder('asset'); + builder = searchAssetBuilder(builder, options); + builder.orderBy('asset.fileCreatedAt', options.orderDirection ?? 'DESC'); + return paginatedBuilder(builder, { + mode: PaginationMode.LIMIT_OFFSET, + skip: pagination.skip, + take: pagination.take, + }); + } + /** * Get assets by device's Id on the database * @param ownerId @@ -472,7 +500,7 @@ export class AssetRepository implements IAssetRepository { }); } - async getMapMarkers(ownerId: string, options: MapMarkerSearchOptions = {}): Promise { + async getMapMarkers(ownerIds: string[], options: MapMarkerSearchOptions = {}): Promise { const { isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore } = options; const assets = await this.repository.find({ @@ -484,7 +512,7 @@ export class AssetRepository implements IAssetRepository { }, }, where: { - ownerId, + ownerId: In([...ownerIds]), isVisible: true, isArchived, exifInfo: { diff --git a/server/src/infra/repositories/filesystem.provider.ts b/server/src/infra/repositories/filesystem.provider.ts index ed009da76f..3ffcd8111f 100644 --- a/server/src/infra/repositories/filesystem.provider.ts +++ b/server/src/infra/repositories/filesystem.provider.ts @@ -12,12 +12,24 @@ import archiver from 'archiver'; import chokidar, { WatchOptions } from 'chokidar'; import { glob } from 'glob'; import { constants, createReadStream, existsSync, mkdirSync } from 'node:fs'; -import fs, { copyFile, readdir, rename, utimes, writeFile } from 'node:fs/promises'; +import fs, { copyFile, readdir, rename, stat, utimes, writeFile } from 'node:fs/promises'; import path from 'node:path'; export class FilesystemProvider implements IStorageRepository { private logger = new ImmichLogger(FilesystemProvider.name); + readdir = readdir; + + copyFile = copyFile; + + stat = stat; + + writeFile = writeFile; + + rename = rename; + + utimes = utimes; + createZipStream(): ImmichZipStream { const archive = archiver('zip', { store: true }); @@ -50,14 +62,6 @@ export class FilesystemProvider implements IStorageRepository { } } - writeFile = writeFile; - - rename = rename; - - copyFile = copyFile; - - utimes = utimes; - async checkFileExists(filepath: string, mode = constants.F_OK): Promise { try { await fs.access(filepath, mode); @@ -79,8 +83,6 @@ export class FilesystemProvider implements IStorageRepository { } } - stat = fs.stat; - async unlinkDir(folder: string, options: { recursive?: boolean; force?: boolean }) { await fs.rm(folder, options); } @@ -146,6 +148,4 @@ export class FilesystemProvider implements IStorageRepository { return () => watcher.close(); } - - readdir = readdir; } diff --git a/server/src/infra/repositories/media.repository.ts b/server/src/infra/repositories/media.repository.ts index bb65dd25c8..1f9395ff21 100644 --- a/server/src/infra/repositories/media.repository.ts +++ b/server/src/infra/repositories/media.repository.ts @@ -60,6 +60,7 @@ export class MediaRepository implements IMediaRepository { frameCount: Number.parseInt(stream.nb_frames ?? '0'), rotation: Number.parseInt(`${stream.rotation ?? 0}`), isHDR: stream.color_transfer === 'smpte2084' || stream.color_transfer === 'arib-std-b67', + bitrate: Number.parseInt(stream.bit_rate ?? '0'), })), audioStreams: results.streams .filter((stream) => stream.codec_type === 'audio') @@ -75,18 +76,7 @@ export class MediaRepository implements IMediaRepository { transcode(input: string, output: string | Writable, options: TranscodeOptions): Promise { if (!options.twoPass) { return new Promise((resolve, reject) => { - const oldLdLibraryPath = process.env.LD_LIBRARY_PATH; - if (options.ldLibraryPath) { - // fluent ffmpeg does not allow to set environment variables, so we do it manually - process.env.LD_LIBRARY_PATH = this.chainPath(oldLdLibraryPath || '', options.ldLibraryPath); - } - try { - this.configureFfmpegCall(input, output, options).on('error', reject).on('end', resolve).run(); - } finally { - if (options.ldLibraryPath) { - process.env.LD_LIBRARY_PATH = oldLdLibraryPath; - } - } + this.configureFfmpegCall(input, output, options).on('error', reject).on('end', resolve).run(); }); } @@ -120,7 +110,6 @@ export class MediaRepository implements IMediaRepository { configureFfmpegCall(input: string, output: string | Writable, options: TranscodeOptions) { return ffmpeg(input, { niceness: 10 }) - .setFfmpegPath(options.ffmpegPath || 'ffmpeg') .inputOptions(options.inputOptions) .outputOptions(options.outputOptions) .output(output) diff --git a/server/src/infra/repositories/metadata.repository.ts b/server/src/infra/repositories/metadata.repository.ts index 6a90ad1081..4abfe0eace 100644 --- a/server/src/infra/repositories/metadata.repository.ts +++ b/server/src/infra/repositories/metadata.repository.ts @@ -2,7 +2,7 @@ import { citiesFile, geodataAdmin1Path, geodataAdmin2Path, - geodataCitites500Path, + geodataCities500Path, geodataDatePath, GeoPoint, IMetadataRepository, @@ -10,13 +10,7 @@ import { ISystemMetadataRepository, ReverseGeocodeResult, } from '@app/domain'; -import { - ExifEntity, - GeodataAdmin1Entity, - GeodataAdmin2Entity, - GeodataPlacesEntity, - SystemMetadataKey, -} from '@app/infra/entities'; +import { ExifEntity, GeodataPlacesEntity, SystemMetadataKey } from '@app/infra/entities'; import { ImmichLogger } from '@app/infra/logger'; import { Inject } from '@nestjs/common'; import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; @@ -26,19 +20,16 @@ import { getName } from 'i18n-iso-countries'; import { createReadStream, existsSync } from 'node:fs'; import { readFile } from 'node:fs/promises'; import * as readLine from 'node:readline'; -import { DataSource, DeepPartial, QueryRunner, Repository } from 'typeorm'; +import { DataSource, QueryRunner, Repository } from 'typeorm'; +import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js'; import { DummyValue, GenerateSql } from '../infra.util'; -type GeoEntity = GeodataPlacesEntity | GeodataAdmin1Entity | GeodataAdmin2Entity; -type GeoEntityClass = typeof GeodataPlacesEntity | typeof GeodataAdmin1Entity | typeof GeodataAdmin2Entity; - export class MetadataRepository implements IMetadataRepository { constructor( @InjectRepository(ExifEntity) private exifRepository: Repository, @InjectRepository(GeodataPlacesEntity) private readonly geodataPlacesRepository: Repository, - @InjectRepository(GeodataAdmin1Entity) private readonly geodataAdmin1Repository: Repository, - @InjectRepository(GeodataAdmin2Entity) private readonly geodataAdmin2Repository: Repository, - @Inject(ISystemMetadataRepository) private readonly systemMetadataRepository: ISystemMetadataRepository, + @Inject(ISystemMetadataRepository) + private readonly systemMetadataRepository: ISystemMetadataRepository, @InjectDataSource() private dataSource: DataSource, ) {} @@ -54,7 +45,6 @@ export class MetadataRepository implements IMetadataRepository { return; } - this.logger.log('Importing geodata to database from file'); await this.importGeodata(); await this.systemMetadataRepository.set(SystemMetadataKey.REVERSE_GEOCODING_STATE, { @@ -69,12 +59,14 @@ export class MetadataRepository implements IMetadataRepository { const queryRunner = this.dataSource.createQueryRunner(); await queryRunner.connect(); + const admin1 = await this.loadAdmin(geodataAdmin1Path); + const admin2 = await this.loadAdmin(geodataAdmin2Path); + try { await queryRunner.startTransaction(); - await this.loadCities500(queryRunner); - await this.loadAdmin1(queryRunner); - await this.loadAdmin2(queryRunner); + await queryRunner.manager.clear(GeodataPlacesEntity); + await this.loadCities500(queryRunner, admin1, admin2); await queryRunner.commitTransaction(); } catch (error) { @@ -86,76 +78,73 @@ export class MetadataRepository implements IMetadataRepository { } } - private async loadGeodataToTableFromFile( + private async loadGeodataToTableFromFile( queryRunner: QueryRunner, - lineToEntityMapper: (lineSplit: string[]) => T, + lineToEntityMapper: (lineSplit: string[]) => GeodataPlacesEntity, filePath: string, - entity: GeoEntityClass, ) { if (!existsSync(filePath)) { this.logger.error(`Geodata file ${filePath} not found`); throw new Error(`Geodata file ${filePath} not found`); } - await queryRunner.manager.clear(entity); const input = createReadStream(filePath); - let buffer: DeepPartial[] = []; - const lineReader = readLine.createInterface({ input: input }); + let bufferGeodata: QueryDeepPartialEntity[] = []; + const lineReader = readLine.createInterface({ input }); for await (const line of lineReader) { const lineSplit = line.split('\t'); - buffer.push(lineToEntityMapper(lineSplit)); - if (buffer.length > 1000) { - await queryRunner.manager.save(buffer); - buffer = []; + const geoData = lineToEntityMapper(lineSplit); + bufferGeodata.push(geoData); + if (bufferGeodata.length > 1000) { + await queryRunner.manager.upsert(GeodataPlacesEntity, bufferGeodata, ['id']); + bufferGeodata = []; } } - await queryRunner.manager.save(buffer); + await queryRunner.manager.upsert(GeodataPlacesEntity, bufferGeodata, ['id']); } - private async loadCities500(queryRunner: QueryRunner) { - await this.loadGeodataToTableFromFile( + private async loadCities500( + queryRunner: QueryRunner, + admin1Map: Map, + admin2Map: Map, + ) { + await this.loadGeodataToTableFromFile( queryRunner, (lineSplit: string[]) => this.geodataPlacesRepository.create({ id: Number.parseInt(lineSplit[0]), name: lineSplit[1], + alternateNames: lineSplit[3], latitude: Number.parseFloat(lineSplit[4]), longitude: Number.parseFloat(lineSplit[5]), countryCode: lineSplit[8], admin1Code: lineSplit[10], admin2Code: lineSplit[11], modificationDate: lineSplit[18], + admin1Name: admin1Map.get(`${lineSplit[8]}.${lineSplit[10]}`), + admin2Name: admin2Map.get(`${lineSplit[8]}.${lineSplit[10]}.${lineSplit[11]}`), }), - geodataCitites500Path, - GeodataPlacesEntity, + geodataCities500Path, ); } - private async loadAdmin1(queryRunner: QueryRunner) { - await this.loadGeodataToTableFromFile( - queryRunner, - (lineSplit: string[]) => - this.geodataAdmin1Repository.create({ - key: lineSplit[0], - name: lineSplit[1], - }), - geodataAdmin1Path, - GeodataAdmin1Entity, - ); - } + private async loadAdmin(filePath: string) { + if (!existsSync(filePath)) { + this.logger.error(`Geodata file ${filePath} not found`); + throw new Error(`Geodata file ${filePath} not found`); + } - private async loadAdmin2(queryRunner: QueryRunner) { - await this.loadGeodataToTableFromFile( - queryRunner, - (lineSplit: string[]) => - this.geodataAdmin2Repository.create({ - key: lineSplit[0], - name: lineSplit[1], - }), - geodataAdmin2Path, - GeodataAdmin2Entity, - ); + const input = createReadStream(filePath); + const lineReader = readLine.createInterface({ input: input }); + + const adminMap = new Map(); + for await (const line of lineReader) { + const lineSplit = line.split('\t'); + adminMap.set(lineSplit[0], lineSplit[1]); + } + + return adminMap; } async teardown() { @@ -167,8 +156,6 @@ export class MetadataRepository implements IMetadataRepository { const response = await this.geodataPlacesRepository .createQueryBuilder('geoplaces') - .leftJoinAndSelect('geoplaces.admin1', 'admin1') - .leftJoinAndSelect('geoplaces.admin2', 'admin2') .where('earth_box(ll_to_earth(:latitude, :longitude), 25000) @> "earthCoord"', point) .orderBy('earth_distance(ll_to_earth(:latitude, :longitude), "earthCoord")') .limit(1) @@ -183,9 +170,9 @@ export class MetadataRepository implements IMetadataRepository { this.logger.verbose(`Raw: ${JSON.stringify(response, null, 2)}`); - const { countryCode, name: city, admin1, admin2 } = response; + const { countryCode, name: city, admin1Name, admin2Name } = response; const country = getName(countryCode, 'en') ?? null; - const stateParts = [admin2?.name, admin1?.name].filter((name) => !!name); + const stateParts = [admin2Name, admin1Name].filter((name) => !!name); const state = stateParts.length > 0 ? stateParts.join(', ') : null; return { country, state, city }; diff --git a/server/src/infra/repositories/person.repository.ts b/server/src/infra/repositories/person.repository.ts index 85423b74dd..3a7ec29466 100644 --- a/server/src/infra/repositories/person.repository.ts +++ b/server/src/infra/repositories/person.repository.ts @@ -3,6 +3,7 @@ import { IPersonRepository, Paginated, PaginationOptions, + PeopleStatistics, PersonNameSearchOptions, PersonSearchOptions, PersonStatistics, @@ -39,11 +40,11 @@ export class PersonRepository implements IPersonRepository { } async deleteAll(): Promise { - await this.personRepository.delete({}); + await this.personRepository.clear(); } async deleteAllFaces(): Promise { - await this.assetFaceRepository.delete({}); + await this.assetFaceRepository.query('TRUNCATE TABLE asset_faces CASCADE'); } getAllFaces( @@ -69,6 +70,7 @@ export class PersonRepository implements IPersonRepository { .addOrderBy("NULLIF(person.name, '') IS NULL", 'ASC') .addOrderBy('COUNT(face.assetId)', 'DESC') .addOrderBy("NULLIF(person.name, '')", 'ASC', 'NULLS LAST') + .andWhere("person.thumbnailPath != ''") .having("person.name != '' OR COUNT(face.assetId) >= :faces", { faces: options?.minimumFaceCount || 1 }) .groupBy('person.id') .limit(500); @@ -207,15 +209,25 @@ export class PersonRepository implements IPersonRepository { } @GenerateSql({ params: [DummyValue.UUID] }) - async getNumberOfPeople(userId: string): Promise { - return this.personRepository + async getNumberOfPeople(userId: string): Promise { + const items = await this.personRepository .createQueryBuilder('person') .leftJoin('person.faces', 'face') .where('person.ownerId = :userId', { userId }) + .innerJoin('face.asset', 'asset') + .andWhere('asset.isArchived = false') + .andWhere("person.thumbnailPath != ''") + .select('COUNT(DISTINCT(person.id))', 'total') + .addSelect('COUNT(DISTINCT(person.id)) FILTER (WHERE person.isHidden = true)', 'hidden') .having('COUNT(face.assetId) != 0') - .groupBy('person.id') - .withDeleted() - .getCount(); + .getRawOne(); + + const result: PeopleStatistics = { + total: items ? Number.parseInt(items.total) : 0, + hidden: items ? Number.parseInt(items.hidden) : 0, + }; + + return result; } create(entity: Partial): Promise { diff --git a/server/src/infra/repositories/search.repository.ts b/server/src/infra/repositories/search.repository.ts index 7d0421b05d..0ff26a4f5f 100644 --- a/server/src/infra/repositories/search.repository.ts +++ b/server/src/infra/repositories/search.repository.ts @@ -12,11 +12,17 @@ import { SmartSearchOptions, } from '@app/domain'; import { getCLIPModelInfo } from '@app/domain/smart-info/smart-info.constant'; -import { AssetEntity, AssetFaceEntity, SmartInfoEntity, SmartSearchEntity } from '@app/infra/entities'; +import { + AssetEntity, + AssetFaceEntity, + GeodataPlacesEntity, + SmartInfoEntity, + SmartSearchEntity, +} from '@app/infra/entities'; import { ImmichLogger } from '@app/infra/logger'; import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; +import { Repository, SelectQueryBuilder } from 'typeorm'; import { vectorExt } from '../database.config'; import { DummyValue, GenerateSql } from '../infra.util'; import { asVector, isValidInteger, paginatedBuilder, searchAssetBuilder } from '../infra.utils'; @@ -31,6 +37,7 @@ export class SearchRepository implements ISearchRepository { @InjectRepository(AssetEntity) private assetRepository: Repository, @InjectRepository(AssetFaceEntity) private assetFaceRepository: Repository, @InjectRepository(SmartSearchEntity) private smartSearchRepository: Repository, + @InjectRepository(GeodataPlacesEntity) private readonly geodataPlacesRepository: Repository, ) { this.faceColumns = this.assetFaceRepository.manager.connection .getMetadata(AssetFaceEntity) @@ -58,6 +65,7 @@ export class SearchRepository implements ISearchRepository { ownerId: DummyValue.UUID, withStacked: true, isFavorite: true, + ownerIds: [DummyValue.UUID], }, ], }) @@ -66,7 +74,6 @@ export class SearchRepository implements ISearchRepository { builder = searchAssetBuilder(builder, options); builder.orderBy('asset.fileCreatedAt', options.orderDirection ?? 'DESC'); - return paginatedBuilder(builder, { mode: PaginationMode.SKIP_TAKE, skip: (pagination.page - 1) * pagination.size, @@ -74,6 +81,14 @@ export class SearchRepository implements ISearchRepository { }); } + private createPersonFilter(builder: SelectQueryBuilder, personIds: string[]) { + return builder + .select(`${builder.alias}."assetId"`) + .where(`${builder.alias}."personId" IN (:...personIds)`, { personIds }) + .groupBy(`${builder.alias}."assetId"`) + .having(`COUNT(DISTINCT ${builder.alias}."personId") = :personCount`, { personCount: personIds.length }); + } + @GenerateSql({ params: [ { page: 1, size: 100 }, @@ -89,12 +104,21 @@ export class SearchRepository implements ISearchRepository { }) async searchSmart( pagination: SearchPaginationOptions, - { embedding, userIds, ...options }: SmartSearchOptions, + { embedding, userIds, personIds, ...options }: SmartSearchOptions, ): Paginated { let results: PaginationResult = { items: [], hasNextPage: false }; await this.assetRepository.manager.transaction(async (manager) => { let builder = manager.createQueryBuilder(AssetEntity, 'asset'); + + if (personIds?.length) { + const assetFaceBuilder = manager.createQueryBuilder(AssetFaceEntity, 'asset_face'); + const cte = this.createPersonFilter(assetFaceBuilder, personIds); + builder + .addCommonTableExpression(cte, 'asset_face_ids') + .innerJoin('asset_face_ids', 'a', 'a."assetId" = asset.id'); + } + builder = searchAssetBuilder(builder, options); builder .innerJoin('asset.smartSearch', 'search') @@ -172,6 +196,27 @@ export class SearchRepository implements ISearchRepository { })); } + @GenerateSql({ params: [DummyValue.STRING] }) + async searchPlaces(placeName: string): Promise { + return await this.geodataPlacesRepository + .createQueryBuilder('geoplaces') + .where(`f_unaccent(name) %>> f_unaccent(:placeName)`) + .orWhere(`f_unaccent("admin2Name") %>> f_unaccent(:placeName)`) + .orWhere(`f_unaccent("admin1Name") %>> f_unaccent(:placeName)`) + .orWhere(`f_unaccent("alternateNames") %>> f_unaccent(:placeName)`) + .orderBy( + ` + COALESCE(f_unaccent(name) <->>> f_unaccent(:placeName), 0) + + COALESCE(f_unaccent("admin2Name") <->>> f_unaccent(:placeName), 0) + + COALESCE(f_unaccent("admin1Name") <->>> f_unaccent(:placeName), 0) + + COALESCE(f_unaccent("alternateNames") <->>> f_unaccent(:placeName), 0) + `, + ) + .setParameters({ placeName }) + .limit(20) + .getMany(); + } + async upsert(smartInfo: Partial, embedding?: Embedding): Promise { await this.repository.upsert(smartInfo, { conflictPaths: ['assetId'] }); if (!smartInfo.assetId || !embedding) { @@ -201,25 +246,17 @@ export class SearchRepository implements ISearchRepository { this.logger.log(`Updating database CLIP dimension size to ${dimSize}.`); await this.smartSearchRepository.manager.transaction(async (manager) => { - await manager.query(`DROP TABLE smart_search`); - - await manager.query(` - CREATE TABLE smart_search ( - "assetId" uuid PRIMARY KEY REFERENCES assets(id) ON DELETE CASCADE, - embedding vector(${dimSize}) NOT NULL )`); - - await manager.query(` - CREATE INDEX clip_index ON smart_search - USING vectors (embedding vector_cos_ops) WITH (options = $$ - [indexing.hnsw] - m = 16 - ef_construction = 300 - $$)`); + await manager.clear(SmartSearchEntity); + await manager.query(`ALTER TABLE smart_search ALTER COLUMN embedding SET DATA TYPE vector(${dimSize})`); }); this.logger.log(`Successfully updated database CLIP dimension size from ${curDimSize} to ${dimSize}.`); } + deleteAllSearchEmbeddings(): Promise { + return this.smartSearchRepository.clear(); + } + private async getDimSize(): Promise { const res = await this.smartSearchRepository.manager.query(` SELECT atttypmod as dimsize diff --git a/server/src/infra/sql/album.repository.sql b/server/src/infra/sql/album.repository.sql index 5e104bc1a2..3997dd1a22 100644 --- a/server/src/infra/sql/album.repository.sql +++ b/server/src/infra/sql/album.repository.sql @@ -21,7 +21,6 @@ FROM "AlbumEntity__AlbumEntity_owner"."isAdmin" AS "AlbumEntity__AlbumEntity_owner_isAdmin", "AlbumEntity__AlbumEntity_owner"."email" AS "AlbumEntity__AlbumEntity_owner_email", "AlbumEntity__AlbumEntity_owner"."storageLabel" AS "AlbumEntity__AlbumEntity_owner_storageLabel", - "AlbumEntity__AlbumEntity_owner"."externalPath" AS "AlbumEntity__AlbumEntity_owner_externalPath", "AlbumEntity__AlbumEntity_owner"."oauthId" AS "AlbumEntity__AlbumEntity_owner_oauthId", "AlbumEntity__AlbumEntity_owner"."profileImagePath" AS "AlbumEntity__AlbumEntity_owner_profileImagePath", "AlbumEntity__AlbumEntity_owner"."shouldChangePassword" AS "AlbumEntity__AlbumEntity_owner_shouldChangePassword", @@ -37,7 +36,6 @@ FROM "AlbumEntity__AlbumEntity_sharedUsers"."isAdmin" AS "AlbumEntity__AlbumEntity_sharedUsers_isAdmin", "AlbumEntity__AlbumEntity_sharedUsers"."email" AS "AlbumEntity__AlbumEntity_sharedUsers_email", "AlbumEntity__AlbumEntity_sharedUsers"."storageLabel" AS "AlbumEntity__AlbumEntity_sharedUsers_storageLabel", - "AlbumEntity__AlbumEntity_sharedUsers"."externalPath" AS "AlbumEntity__AlbumEntity_sharedUsers_externalPath", "AlbumEntity__AlbumEntity_sharedUsers"."oauthId" AS "AlbumEntity__AlbumEntity_sharedUsers_oauthId", "AlbumEntity__AlbumEntity_sharedUsers"."profileImagePath" AS "AlbumEntity__AlbumEntity_sharedUsers_profileImagePath", "AlbumEntity__AlbumEntity_sharedUsers"."shouldChangePassword" AS "AlbumEntity__AlbumEntity_sharedUsers_shouldChangePassword", @@ -97,7 +95,6 @@ SELECT "AlbumEntity__AlbumEntity_owner"."isAdmin" AS "AlbumEntity__AlbumEntity_owner_isAdmin", "AlbumEntity__AlbumEntity_owner"."email" AS "AlbumEntity__AlbumEntity_owner_email", "AlbumEntity__AlbumEntity_owner"."storageLabel" AS "AlbumEntity__AlbumEntity_owner_storageLabel", - "AlbumEntity__AlbumEntity_owner"."externalPath" AS "AlbumEntity__AlbumEntity_owner_externalPath", "AlbumEntity__AlbumEntity_owner"."oauthId" AS "AlbumEntity__AlbumEntity_owner_oauthId", "AlbumEntity__AlbumEntity_owner"."profileImagePath" AS "AlbumEntity__AlbumEntity_owner_profileImagePath", "AlbumEntity__AlbumEntity_owner"."shouldChangePassword" AS "AlbumEntity__AlbumEntity_owner_shouldChangePassword", @@ -113,7 +110,6 @@ SELECT "AlbumEntity__AlbumEntity_sharedUsers"."isAdmin" AS "AlbumEntity__AlbumEntity_sharedUsers_isAdmin", "AlbumEntity__AlbumEntity_sharedUsers"."email" AS "AlbumEntity__AlbumEntity_sharedUsers_email", "AlbumEntity__AlbumEntity_sharedUsers"."storageLabel" AS "AlbumEntity__AlbumEntity_sharedUsers_storageLabel", - "AlbumEntity__AlbumEntity_sharedUsers"."externalPath" AS "AlbumEntity__AlbumEntity_sharedUsers_externalPath", "AlbumEntity__AlbumEntity_sharedUsers"."oauthId" AS "AlbumEntity__AlbumEntity_sharedUsers_oauthId", "AlbumEntity__AlbumEntity_sharedUsers"."profileImagePath" AS "AlbumEntity__AlbumEntity_sharedUsers_profileImagePath", "AlbumEntity__AlbumEntity_sharedUsers"."shouldChangePassword" AS "AlbumEntity__AlbumEntity_sharedUsers_shouldChangePassword", @@ -155,7 +151,6 @@ SELECT "AlbumEntity__AlbumEntity_owner"."isAdmin" AS "AlbumEntity__AlbumEntity_owner_isAdmin", "AlbumEntity__AlbumEntity_owner"."email" AS "AlbumEntity__AlbumEntity_owner_email", "AlbumEntity__AlbumEntity_owner"."storageLabel" AS "AlbumEntity__AlbumEntity_owner_storageLabel", - "AlbumEntity__AlbumEntity_owner"."externalPath" AS "AlbumEntity__AlbumEntity_owner_externalPath", "AlbumEntity__AlbumEntity_owner"."oauthId" AS "AlbumEntity__AlbumEntity_owner_oauthId", "AlbumEntity__AlbumEntity_owner"."profileImagePath" AS "AlbumEntity__AlbumEntity_owner_profileImagePath", "AlbumEntity__AlbumEntity_owner"."shouldChangePassword" AS "AlbumEntity__AlbumEntity_owner_shouldChangePassword", @@ -171,7 +166,6 @@ SELECT "AlbumEntity__AlbumEntity_sharedUsers"."isAdmin" AS "AlbumEntity__AlbumEntity_sharedUsers_isAdmin", "AlbumEntity__AlbumEntity_sharedUsers"."email" AS "AlbumEntity__AlbumEntity_sharedUsers_email", "AlbumEntity__AlbumEntity_sharedUsers"."storageLabel" AS "AlbumEntity__AlbumEntity_sharedUsers_storageLabel", - "AlbumEntity__AlbumEntity_sharedUsers"."externalPath" AS "AlbumEntity__AlbumEntity_sharedUsers_externalPath", "AlbumEntity__AlbumEntity_sharedUsers"."oauthId" AS "AlbumEntity__AlbumEntity_sharedUsers_oauthId", "AlbumEntity__AlbumEntity_sharedUsers"."profileImagePath" AS "AlbumEntity__AlbumEntity_sharedUsers_profileImagePath", "AlbumEntity__AlbumEntity_sharedUsers"."shouldChangePassword" AS "AlbumEntity__AlbumEntity_sharedUsers_shouldChangePassword", @@ -285,7 +279,6 @@ SELECT "AlbumEntity__AlbumEntity_sharedUsers"."isAdmin" AS "AlbumEntity__AlbumEntity_sharedUsers_isAdmin", "AlbumEntity__AlbumEntity_sharedUsers"."email" AS "AlbumEntity__AlbumEntity_sharedUsers_email", "AlbumEntity__AlbumEntity_sharedUsers"."storageLabel" AS "AlbumEntity__AlbumEntity_sharedUsers_storageLabel", - "AlbumEntity__AlbumEntity_sharedUsers"."externalPath" AS "AlbumEntity__AlbumEntity_sharedUsers_externalPath", "AlbumEntity__AlbumEntity_sharedUsers"."oauthId" AS "AlbumEntity__AlbumEntity_sharedUsers_oauthId", "AlbumEntity__AlbumEntity_sharedUsers"."profileImagePath" AS "AlbumEntity__AlbumEntity_sharedUsers_profileImagePath", "AlbumEntity__AlbumEntity_sharedUsers"."shouldChangePassword" AS "AlbumEntity__AlbumEntity_sharedUsers_shouldChangePassword", @@ -313,7 +306,6 @@ SELECT "AlbumEntity__AlbumEntity_owner"."isAdmin" AS "AlbumEntity__AlbumEntity_owner_isAdmin", "AlbumEntity__AlbumEntity_owner"."email" AS "AlbumEntity__AlbumEntity_owner_email", "AlbumEntity__AlbumEntity_owner"."storageLabel" AS "AlbumEntity__AlbumEntity_owner_storageLabel", - "AlbumEntity__AlbumEntity_owner"."externalPath" AS "AlbumEntity__AlbumEntity_owner_externalPath", "AlbumEntity__AlbumEntity_owner"."oauthId" AS "AlbumEntity__AlbumEntity_owner_oauthId", "AlbumEntity__AlbumEntity_owner"."profileImagePath" AS "AlbumEntity__AlbumEntity_owner_profileImagePath", "AlbumEntity__AlbumEntity_owner"."shouldChangePassword" AS "AlbumEntity__AlbumEntity_owner_shouldChangePassword", @@ -358,7 +350,6 @@ SELECT "AlbumEntity__AlbumEntity_sharedUsers"."isAdmin" AS "AlbumEntity__AlbumEntity_sharedUsers_isAdmin", "AlbumEntity__AlbumEntity_sharedUsers"."email" AS "AlbumEntity__AlbumEntity_sharedUsers_email", "AlbumEntity__AlbumEntity_sharedUsers"."storageLabel" AS "AlbumEntity__AlbumEntity_sharedUsers_storageLabel", - "AlbumEntity__AlbumEntity_sharedUsers"."externalPath" AS "AlbumEntity__AlbumEntity_sharedUsers_externalPath", "AlbumEntity__AlbumEntity_sharedUsers"."oauthId" AS "AlbumEntity__AlbumEntity_sharedUsers_oauthId", "AlbumEntity__AlbumEntity_sharedUsers"."profileImagePath" AS "AlbumEntity__AlbumEntity_sharedUsers_profileImagePath", "AlbumEntity__AlbumEntity_sharedUsers"."shouldChangePassword" AS "AlbumEntity__AlbumEntity_sharedUsers_shouldChangePassword", @@ -386,7 +377,6 @@ SELECT "AlbumEntity__AlbumEntity_owner"."isAdmin" AS "AlbumEntity__AlbumEntity_owner_isAdmin", "AlbumEntity__AlbumEntity_owner"."email" AS "AlbumEntity__AlbumEntity_owner_email", "AlbumEntity__AlbumEntity_owner"."storageLabel" AS "AlbumEntity__AlbumEntity_owner_storageLabel", - "AlbumEntity__AlbumEntity_owner"."externalPath" AS "AlbumEntity__AlbumEntity_owner_externalPath", "AlbumEntity__AlbumEntity_owner"."oauthId" AS "AlbumEntity__AlbumEntity_owner_oauthId", "AlbumEntity__AlbumEntity_owner"."profileImagePath" AS "AlbumEntity__AlbumEntity_owner_profileImagePath", "AlbumEntity__AlbumEntity_owner"."shouldChangePassword" AS "AlbumEntity__AlbumEntity_owner_shouldChangePassword", @@ -468,7 +458,6 @@ SELECT "AlbumEntity__AlbumEntity_sharedUsers"."isAdmin" AS "AlbumEntity__AlbumEntity_sharedUsers_isAdmin", "AlbumEntity__AlbumEntity_sharedUsers"."email" AS "AlbumEntity__AlbumEntity_sharedUsers_email", "AlbumEntity__AlbumEntity_sharedUsers"."storageLabel" AS "AlbumEntity__AlbumEntity_sharedUsers_storageLabel", - "AlbumEntity__AlbumEntity_sharedUsers"."externalPath" AS "AlbumEntity__AlbumEntity_sharedUsers_externalPath", "AlbumEntity__AlbumEntity_sharedUsers"."oauthId" AS "AlbumEntity__AlbumEntity_sharedUsers_oauthId", "AlbumEntity__AlbumEntity_sharedUsers"."profileImagePath" AS "AlbumEntity__AlbumEntity_sharedUsers_profileImagePath", "AlbumEntity__AlbumEntity_sharedUsers"."shouldChangePassword" AS "AlbumEntity__AlbumEntity_sharedUsers_shouldChangePassword", @@ -496,7 +485,6 @@ SELECT "AlbumEntity__AlbumEntity_owner"."isAdmin" AS "AlbumEntity__AlbumEntity_owner_isAdmin", "AlbumEntity__AlbumEntity_owner"."email" AS "AlbumEntity__AlbumEntity_owner_email", "AlbumEntity__AlbumEntity_owner"."storageLabel" AS "AlbumEntity__AlbumEntity_owner_storageLabel", - "AlbumEntity__AlbumEntity_owner"."externalPath" AS "AlbumEntity__AlbumEntity_owner_externalPath", "AlbumEntity__AlbumEntity_owner"."oauthId" AS "AlbumEntity__AlbumEntity_owner_oauthId", "AlbumEntity__AlbumEntity_owner"."profileImagePath" AS "AlbumEntity__AlbumEntity_owner_profileImagePath", "AlbumEntity__AlbumEntity_owner"."shouldChangePassword" AS "AlbumEntity__AlbumEntity_owner_shouldChangePassword", @@ -559,7 +547,6 @@ SELECT "AlbumEntity__AlbumEntity_owner"."isAdmin" AS "AlbumEntity__AlbumEntity_owner_isAdmin", "AlbumEntity__AlbumEntity_owner"."email" AS "AlbumEntity__AlbumEntity_owner_email", "AlbumEntity__AlbumEntity_owner"."storageLabel" AS "AlbumEntity__AlbumEntity_owner_storageLabel", - "AlbumEntity__AlbumEntity_owner"."externalPath" AS "AlbumEntity__AlbumEntity_owner_externalPath", "AlbumEntity__AlbumEntity_owner"."oauthId" AS "AlbumEntity__AlbumEntity_owner_oauthId", "AlbumEntity__AlbumEntity_owner"."profileImagePath" AS "AlbumEntity__AlbumEntity_owner_profileImagePath", "AlbumEntity__AlbumEntity_owner"."shouldChangePassword" AS "AlbumEntity__AlbumEntity_owner_shouldChangePassword", diff --git a/server/src/infra/sql/api.key.repository.sql b/server/src/infra/sql/api.key.repository.sql index 78eab13b30..3f6b207ce1 100644 --- a/server/src/infra/sql/api.key.repository.sql +++ b/server/src/infra/sql/api.key.repository.sql @@ -15,7 +15,6 @@ FROM "APIKeyEntity__APIKeyEntity_user"."isAdmin" AS "APIKeyEntity__APIKeyEntity_user_isAdmin", "APIKeyEntity__APIKeyEntity_user"."email" AS "APIKeyEntity__APIKeyEntity_user_email", "APIKeyEntity__APIKeyEntity_user"."storageLabel" AS "APIKeyEntity__APIKeyEntity_user_storageLabel", - "APIKeyEntity__APIKeyEntity_user"."externalPath" AS "APIKeyEntity__APIKeyEntity_user_externalPath", "APIKeyEntity__APIKeyEntity_user"."oauthId" AS "APIKeyEntity__APIKeyEntity_user_oauthId", "APIKeyEntity__APIKeyEntity_user"."profileImagePath" AS "APIKeyEntity__APIKeyEntity_user_profileImagePath", "APIKeyEntity__APIKeyEntity_user"."shouldChangePassword" AS "APIKeyEntity__APIKeyEntity_user_shouldChangePassword", diff --git a/server/src/infra/sql/asset.repository.sql b/server/src/infra/sql/asset.repository.sql index 88f17eb920..e5cf6771fd 100644 --- a/server/src/infra/sql/asset.repository.sql +++ b/server/src/infra/sql/asset.repository.sql @@ -395,6 +395,55 @@ ORDER BY LIMIT 1 +-- AssetRepository.getAllByFileCreationDate +SELECT + "asset"."id" AS "asset_id", + "asset"."deviceAssetId" AS "asset_deviceAssetId", + "asset"."ownerId" AS "asset_ownerId", + "asset"."libraryId" AS "asset_libraryId", + "asset"."deviceId" AS "asset_deviceId", + "asset"."type" AS "asset_type", + "asset"."originalPath" AS "asset_originalPath", + "asset"."resizePath" AS "asset_resizePath", + "asset"."webpPath" AS "asset_webpPath", + "asset"."thumbhash" AS "asset_thumbhash", + "asset"."encodedVideoPath" AS "asset_encodedVideoPath", + "asset"."createdAt" AS "asset_createdAt", + "asset"."updatedAt" AS "asset_updatedAt", + "asset"."deletedAt" AS "asset_deletedAt", + "asset"."fileCreatedAt" AS "asset_fileCreatedAt", + "asset"."localDateTime" AS "asset_localDateTime", + "asset"."fileModifiedAt" AS "asset_fileModifiedAt", + "asset"."isFavorite" AS "asset_isFavorite", + "asset"."isArchived" AS "asset_isArchived", + "asset"."isExternal" AS "asset_isExternal", + "asset"."isReadOnly" AS "asset_isReadOnly", + "asset"."isOffline" AS "asset_isOffline", + "asset"."checksum" AS "asset_checksum", + "asset"."duration" AS "asset_duration", + "asset"."isVisible" AS "asset_isVisible", + "asset"."livePhotoVideoId" AS "asset_livePhotoVideoId", + "asset"."originalFileName" AS "asset_originalFileName", + "asset"."sidecarPath" AS "asset_sidecarPath", + "asset"."stackId" AS "asset_stackId" +FROM + "assets" "asset" +WHERE + ( + "asset"."fileCreatedAt" <= $1 + AND 1 = 1 + AND "asset"."ownerId" IN ($2) + AND 1 = 1 + AND "asset"."isArchived" = $3 + ) + AND ("asset"."deletedAt" IS NULL) +ORDER BY + "asset"."fileCreatedAt" DESC +LIMIT + 10001 +OFFSET + 20000 + -- AssetRepository.getAllByDeviceId SELECT "AssetEntity"."deviceAssetId" AS "AssetEntity_deviceAssetId", diff --git a/server/src/infra/sql/library.repository.sql b/server/src/infra/sql/library.repository.sql index c791b2c8a5..433ab6fbac 100644 --- a/server/src/infra/sql/library.repository.sql +++ b/server/src/infra/sql/library.repository.sql @@ -23,7 +23,6 @@ FROM "LibraryEntity__LibraryEntity_owner"."isAdmin" AS "LibraryEntity__LibraryEntity_owner_isAdmin", "LibraryEntity__LibraryEntity_owner"."email" AS "LibraryEntity__LibraryEntity_owner_email", "LibraryEntity__LibraryEntity_owner"."storageLabel" AS "LibraryEntity__LibraryEntity_owner_storageLabel", - "LibraryEntity__LibraryEntity_owner"."externalPath" AS "LibraryEntity__LibraryEntity_owner_externalPath", "LibraryEntity__LibraryEntity_owner"."oauthId" AS "LibraryEntity__LibraryEntity_owner_oauthId", "LibraryEntity__LibraryEntity_owner"."profileImagePath" AS "LibraryEntity__LibraryEntity_owner_profileImagePath", "LibraryEntity__LibraryEntity_owner"."shouldChangePassword" AS "LibraryEntity__LibraryEntity_owner_shouldChangePassword", @@ -139,7 +138,6 @@ SELECT "LibraryEntity__LibraryEntity_owner"."isAdmin" AS "LibraryEntity__LibraryEntity_owner_isAdmin", "LibraryEntity__LibraryEntity_owner"."email" AS "LibraryEntity__LibraryEntity_owner_email", "LibraryEntity__LibraryEntity_owner"."storageLabel" AS "LibraryEntity__LibraryEntity_owner_storageLabel", - "LibraryEntity__LibraryEntity_owner"."externalPath" AS "LibraryEntity__LibraryEntity_owner_externalPath", "LibraryEntity__LibraryEntity_owner"."oauthId" AS "LibraryEntity__LibraryEntity_owner_oauthId", "LibraryEntity__LibraryEntity_owner"."profileImagePath" AS "LibraryEntity__LibraryEntity_owner_profileImagePath", "LibraryEntity__LibraryEntity_owner"."shouldChangePassword" AS "LibraryEntity__LibraryEntity_owner_shouldChangePassword", @@ -185,7 +183,6 @@ SELECT "LibraryEntity__LibraryEntity_owner"."isAdmin" AS "LibraryEntity__LibraryEntity_owner_isAdmin", "LibraryEntity__LibraryEntity_owner"."email" AS "LibraryEntity__LibraryEntity_owner_email", "LibraryEntity__LibraryEntity_owner"."storageLabel" AS "LibraryEntity__LibraryEntity_owner_storageLabel", - "LibraryEntity__LibraryEntity_owner"."externalPath" AS "LibraryEntity__LibraryEntity_owner_externalPath", "LibraryEntity__LibraryEntity_owner"."oauthId" AS "LibraryEntity__LibraryEntity_owner_oauthId", "LibraryEntity__LibraryEntity_owner"."profileImagePath" AS "LibraryEntity__LibraryEntity_owner_profileImagePath", "LibraryEntity__LibraryEntity_owner"."shouldChangePassword" AS "LibraryEntity__LibraryEntity_owner_shouldChangePassword", @@ -225,7 +222,6 @@ SELECT "LibraryEntity__LibraryEntity_owner"."isAdmin" AS "LibraryEntity__LibraryEntity_owner_isAdmin", "LibraryEntity__LibraryEntity_owner"."email" AS "LibraryEntity__LibraryEntity_owner_email", "LibraryEntity__LibraryEntity_owner"."storageLabel" AS "LibraryEntity__LibraryEntity_owner_storageLabel", - "LibraryEntity__LibraryEntity_owner"."externalPath" AS "LibraryEntity__LibraryEntity_owner_externalPath", "LibraryEntity__LibraryEntity_owner"."oauthId" AS "LibraryEntity__LibraryEntity_owner_oauthId", "LibraryEntity__LibraryEntity_owner"."profileImagePath" AS "LibraryEntity__LibraryEntity_owner_profileImagePath", "LibraryEntity__LibraryEntity_owner"."shouldChangePassword" AS "LibraryEntity__LibraryEntity_owner_shouldChangePassword", diff --git a/server/src/infra/sql/person.repository.sql b/server/src/infra/sql/person.repository.sql index bd4a523e86..c2cc45ee88 100644 --- a/server/src/infra/sql/person.repository.sql +++ b/server/src/infra/sql/person.repository.sql @@ -26,6 +26,7 @@ FROM WHERE "person"."ownerId" = $1 AND "asset"."isArchived" = false + AND "person"."thumbnailPath" != '' AND "person"."isHidden" = false GROUP BY "person"."id" @@ -344,12 +345,20 @@ LIMIT -- PersonRepository.getNumberOfPeople SELECT - COUNT(DISTINCT ("person"."id")) AS "cnt" + COUNT(DISTINCT ("person"."id")) AS "total", + COUNT(DISTINCT ("person"."id")) FILTER ( + WHERE + "person"."isHidden" = true + ) AS "hidden" FROM "person" "person" LEFT JOIN "asset_faces" "face" ON "face"."personId" = "person"."id" + INNER JOIN "assets" "asset" ON "asset"."id" = "face"."assetId" + AND ("asset"."deletedAt" IS NULL) WHERE "person"."ownerId" = $1 + AND "asset"."isArchived" = false + AND "person"."thumbnailPath" != '' HAVING COUNT("face"."assetId") != 0 diff --git a/server/src/infra/sql/search.repository.sql b/server/src/infra/sql/search.repository.sql index 538a854094..c45d90a7a3 100644 --- a/server/src/infra/sql/search.repository.sql +++ b/server/src/infra/sql/search.repository.sql @@ -77,9 +77,12 @@ FROM ( "asset"."fileCreatedAt" >= $1 AND "exifInfo"."lensModel" = $2 - AND "asset"."ownerId" = $3 AND 1 = 1 - AND "asset"."isFavorite" = $4 + AND 1 = 1 + AND ( + "asset"."isFavorite" = $3 + AND "asset"."isArchived" = $4 + ) AND ( "stack"."primaryAssetId" = "asset"."id" OR "asset"."stackId" IS NULL @@ -177,16 +180,19 @@ WHERE AND "exifInfo"."lensModel" = $2 AND 1 = 1 AND 1 = 1 - AND "asset"."isFavorite" = $3 + AND ( + "asset"."isFavorite" = $3 + AND "asset"."isArchived" = $4 + ) AND ( "stack"."primaryAssetId" = "asset"."id" OR "asset"."stackId" IS NULL ) - AND "asset"."ownerId" IN ($4) + AND "asset"."ownerId" IN ($5) ) AND ("asset"."deletedAt" IS NULL) ORDER BY - "search"."embedding" <= > $5 ASC + "search"."embedding" <= > $6 ASC LIMIT 101 COMMIT @@ -232,3 +238,37 @@ FROM WHERE res.distance <= $3 COMMIT + +-- SearchRepository.searchPlaces +SELECT + "geoplaces"."id" AS "geoplaces_id", + "geoplaces"."name" AS "geoplaces_name", + "geoplaces"."longitude" AS "geoplaces_longitude", + "geoplaces"."latitude" AS "geoplaces_latitude", + "geoplaces"."countryCode" AS "geoplaces_countryCode", + "geoplaces"."admin1Code" AS "geoplaces_admin1Code", + "geoplaces"."admin2Code" AS "geoplaces_admin2Code", + "geoplaces"."admin1Name" AS "geoplaces_admin1Name", + "geoplaces"."admin2Name" AS "geoplaces_admin2Name", + "geoplaces"."alternateNames" AS "geoplaces_alternateNames", + "geoplaces"."modificationDate" AS "geoplaces_modificationDate" +FROM + "geodata_places" "geoplaces" +WHERE + f_unaccent (name) %>> f_unaccent ($1) + OR f_unaccent ("admin2Name") %>> f_unaccent ($1) + OR f_unaccent ("admin1Name") %>> f_unaccent ($1) + OR f_unaccent ("alternateNames") %>> f_unaccent ($1) +ORDER BY + COALESCE(f_unaccent (name) <->>> f_unaccent ($1), 0) + COALESCE( + f_unaccent ("admin2Name") <->>> f_unaccent ($1), + 0 + ) + COALESCE( + f_unaccent ("admin1Name") <->>> f_unaccent ($1), + 0 + ) + COALESCE( + f_unaccent ("alternateNames") <->>> f_unaccent ($1), + 0 + ) ASC +LIMIT + 20 diff --git a/server/src/infra/sql/shared.link.repository.sql b/server/src/infra/sql/shared.link.repository.sql index 5f43210685..6cac1c44fc 100644 --- a/server/src/infra/sql/shared.link.repository.sql +++ b/server/src/infra/sql/shared.link.repository.sql @@ -150,7 +150,6 @@ FROM "6d7fd45329a05fd86b3dbcacde87fe76e33a422d"."isAdmin" AS "6d7fd45329a05fd86b3dbcacde87fe76e33a422d_isAdmin", "6d7fd45329a05fd86b3dbcacde87fe76e33a422d"."email" AS "6d7fd45329a05fd86b3dbcacde87fe76e33a422d_email", "6d7fd45329a05fd86b3dbcacde87fe76e33a422d"."storageLabel" AS "6d7fd45329a05fd86b3dbcacde87fe76e33a422d_storageLabel", - "6d7fd45329a05fd86b3dbcacde87fe76e33a422d"."externalPath" AS "6d7fd45329a05fd86b3dbcacde87fe76e33a422d_externalPath", "6d7fd45329a05fd86b3dbcacde87fe76e33a422d"."oauthId" AS "6d7fd45329a05fd86b3dbcacde87fe76e33a422d_oauthId", "6d7fd45329a05fd86b3dbcacde87fe76e33a422d"."profileImagePath" AS "6d7fd45329a05fd86b3dbcacde87fe76e33a422d_profileImagePath", "6d7fd45329a05fd86b3dbcacde87fe76e33a422d"."shouldChangePassword" AS "6d7fd45329a05fd86b3dbcacde87fe76e33a422d_shouldChangePassword", @@ -254,7 +253,6 @@ SELECT "6d7fd45329a05fd86b3dbcacde87fe76e33a422d"."isAdmin" AS "6d7fd45329a05fd86b3dbcacde87fe76e33a422d_isAdmin", "6d7fd45329a05fd86b3dbcacde87fe76e33a422d"."email" AS "6d7fd45329a05fd86b3dbcacde87fe76e33a422d_email", "6d7fd45329a05fd86b3dbcacde87fe76e33a422d"."storageLabel" AS "6d7fd45329a05fd86b3dbcacde87fe76e33a422d_storageLabel", - "6d7fd45329a05fd86b3dbcacde87fe76e33a422d"."externalPath" AS "6d7fd45329a05fd86b3dbcacde87fe76e33a422d_externalPath", "6d7fd45329a05fd86b3dbcacde87fe76e33a422d"."oauthId" AS "6d7fd45329a05fd86b3dbcacde87fe76e33a422d_oauthId", "6d7fd45329a05fd86b3dbcacde87fe76e33a422d"."profileImagePath" AS "6d7fd45329a05fd86b3dbcacde87fe76e33a422d_profileImagePath", "6d7fd45329a05fd86b3dbcacde87fe76e33a422d"."shouldChangePassword" AS "6d7fd45329a05fd86b3dbcacde87fe76e33a422d_shouldChangePassword", @@ -308,7 +306,6 @@ FROM "SharedLinkEntity__SharedLinkEntity_user"."isAdmin" AS "SharedLinkEntity__SharedLinkEntity_user_isAdmin", "SharedLinkEntity__SharedLinkEntity_user"."email" AS "SharedLinkEntity__SharedLinkEntity_user_email", "SharedLinkEntity__SharedLinkEntity_user"."storageLabel" AS "SharedLinkEntity__SharedLinkEntity_user_storageLabel", - "SharedLinkEntity__SharedLinkEntity_user"."externalPath" AS "SharedLinkEntity__SharedLinkEntity_user_externalPath", "SharedLinkEntity__SharedLinkEntity_user"."oauthId" AS "SharedLinkEntity__SharedLinkEntity_user_oauthId", "SharedLinkEntity__SharedLinkEntity_user"."profileImagePath" AS "SharedLinkEntity__SharedLinkEntity_user_profileImagePath", "SharedLinkEntity__SharedLinkEntity_user"."shouldChangePassword" AS "SharedLinkEntity__SharedLinkEntity_user_shouldChangePassword", diff --git a/server/src/infra/sql/user.repository.sql b/server/src/infra/sql/user.repository.sql index 29a2ee5301..e4c7d3a314 100644 --- a/server/src/infra/sql/user.repository.sql +++ b/server/src/infra/sql/user.repository.sql @@ -8,7 +8,6 @@ SELECT "UserEntity"."isAdmin" AS "UserEntity_isAdmin", "UserEntity"."email" AS "UserEntity_email", "UserEntity"."storageLabel" AS "UserEntity_storageLabel", - "UserEntity"."externalPath" AS "UserEntity_externalPath", "UserEntity"."oauthId" AS "UserEntity_oauthId", "UserEntity"."profileImagePath" AS "UserEntity_profileImagePath", "UserEntity"."shouldChangePassword" AS "UserEntity_shouldChangePassword", @@ -55,7 +54,6 @@ SELECT "user"."isAdmin" AS "user_isAdmin", "user"."email" AS "user_email", "user"."storageLabel" AS "user_storageLabel", - "user"."externalPath" AS "user_externalPath", "user"."oauthId" AS "user_oauthId", "user"."profileImagePath" AS "user_profileImagePath", "user"."shouldChangePassword" AS "user_shouldChangePassword", @@ -79,7 +77,6 @@ SELECT "UserEntity"."isAdmin" AS "UserEntity_isAdmin", "UserEntity"."email" AS "UserEntity_email", "UserEntity"."storageLabel" AS "UserEntity_storageLabel", - "UserEntity"."externalPath" AS "UserEntity_externalPath", "UserEntity"."oauthId" AS "UserEntity_oauthId", "UserEntity"."profileImagePath" AS "UserEntity_profileImagePath", "UserEntity"."shouldChangePassword" AS "UserEntity_shouldChangePassword", @@ -105,7 +102,6 @@ SELECT "UserEntity"."isAdmin" AS "UserEntity_isAdmin", "UserEntity"."email" AS "UserEntity_email", "UserEntity"."storageLabel" AS "UserEntity_storageLabel", - "UserEntity"."externalPath" AS "UserEntity_externalPath", "UserEntity"."oauthId" AS "UserEntity_oauthId", "UserEntity"."profileImagePath" AS "UserEntity_profileImagePath", "UserEntity"."shouldChangePassword" AS "UserEntity_shouldChangePassword", diff --git a/server/src/infra/sql/user.token.repository.sql b/server/src/infra/sql/user.token.repository.sql index 0f496504f5..b51e53106e 100644 --- a/server/src/infra/sql/user.token.repository.sql +++ b/server/src/infra/sql/user.token.repository.sql @@ -18,7 +18,6 @@ FROM "UserTokenEntity__UserTokenEntity_user"."isAdmin" AS "UserTokenEntity__UserTokenEntity_user_isAdmin", "UserTokenEntity__UserTokenEntity_user"."email" AS "UserTokenEntity__UserTokenEntity_user_email", "UserTokenEntity__UserTokenEntity_user"."storageLabel" AS "UserTokenEntity__UserTokenEntity_user_storageLabel", - "UserTokenEntity__UserTokenEntity_user"."externalPath" AS "UserTokenEntity__UserTokenEntity_user_externalPath", "UserTokenEntity__UserTokenEntity_user"."oauthId" AS "UserTokenEntity__UserTokenEntity_user_oauthId", "UserTokenEntity__UserTokenEntity_user"."profileImagePath" AS "UserTokenEntity__UserTokenEntity_user_profileImagePath", "UserTokenEntity__UserTokenEntity_user"."shouldChangePassword" AS "UserTokenEntity__UserTokenEntity_user_shouldChangePassword", diff --git a/server/src/test-utils/utils.ts b/server/src/test-utils/utils.ts index 077239d8a3..e5566f95de 100644 --- a/server/src/test-utils/utils.ts +++ b/server/src/test-utils/utils.ts @@ -6,7 +6,8 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import { DateTime } from 'luxon'; import * as fs from 'node:fs'; -import path from 'node:path'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; import { EventEmitter } from 'node:stream'; import { Server } from 'node:tls'; import { EntityTarget, ObjectLiteral } from 'typeorm'; @@ -14,7 +15,7 @@ import { AppService } from '../immich/app.service'; import { AppService as MicroAppService } from '../microservices/app.service'; export const IMMICH_TEST_ASSET_PATH = process.env.IMMICH_TEST_ASSET_PATH as string; -export const IMMICH_TEST_ASSET_TEMP_PATH = path.normalize(`${IMMICH_TEST_ASSET_PATH}/temp/`); +export const IMMICH_TEST_ASSET_TEMP_PATH = join(tmpdir(), 'immich'); export const today = DateTime.fromObject({ year: 2023, month: 11, day: 3 }); export const yesterday = today.minus({ days: 1 }); diff --git a/server/start-microservices.sh b/server/start-microservices.sh index efdafaac02..c9e2cb42fb 100755 --- a/server/start-microservices.sh +++ b/server/start-microservices.sh @@ -1,2 +1,3 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash + ./start.sh microservices diff --git a/server/start-server.sh b/server/start-server.sh index acbbb82b99..7ef959f63c 100755 --- a/server/start-server.sh +++ b/server/start-server.sh @@ -1,2 +1,3 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash + ./start.sh immich diff --git a/server/start.sh b/server/start.sh index 7aa0bc20dd..9c9b4754a0 100755 --- a/server/start.sh +++ b/server/start.sh @@ -1,35 +1,20 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash -export LD_PRELOAD=/usr/lib/$(arch)-linux-gnu/libmimalloc.so.2 +lib_path="/usr/lib/$(arch)-linux-gnu/libmimalloc.so.2" +export LD_PRELOAD="$lib_path" -if [ "$DB_URL_FILE" ]; then - export DB_URL=$(cat $DB_URL_FILE) - unset DB_URL_FILE -fi +read_file_and_export() { + if [ -n "${!1}" ]; then + content="$(cat "${!1}")" + export "$2"="${content}" + unset "$1" + fi +} +read_file_and_export "DB_URL_FILE" "DB_URL" +read_file_and_export "DB_HOSTNAME_FILE" "DB_HOSTNAME" +read_file_and_export "DB_DATABASE_NAME_FILE" "DB_DATABASE_NAME" +read_file_and_export "DB_USERNAME_FILE" "DB_USERNAME" +read_file_and_export "DB_PASSWORD_FILE" "DB_PASSWORD" +read_file_and_export "REDIS_PASSWORD_FILE" "REDIS_PASSWORD" -if [ "$DB_HOSTNAME_FILE" ]; then - export DB_HOSTNAME=$(cat $DB_HOSTNAME_FILE) - unset DB_HOSTNAME_FILE -fi - -if [ "$DB_DATABASE_NAME_FILE" ]; then - export DB_DATABASE_NAME=$(cat $DB_DATABASE_NAME_FILE) - unset DB_DATABASE_NAME_FILE -fi - -if [ "$DB_USERNAME_FILE" ]; then - export DB_USERNAME=$(cat $DB_USERNAME_FILE) - unset DB_USERNAME_FILE -fi - -if [ "$DB_PASSWORD_FILE" ]; then - export DB_PASSWORD=$(cat $DB_PASSWORD_FILE) - unset DB_PASSWORD_FILE -fi - -if [ "$REDIS_PASSWORD_FILE" ]; then - export REDIS_PASSWORD=$(cat $REDIS_PASSWORD_FILE) - unset REDIS_PASSWORD_FILE -fi - -exec node /usr/src/app/dist/main $@ +exec node /usr/src/app/dist/main "$@" diff --git a/server/test/fixtures/auth.stub.ts b/server/test/fixtures/auth.stub.ts index 4b0c06baf3..79993a4da2 100644 --- a/server/test/fixtures/auth.stub.ts +++ b/server/test/fixtures/auth.stub.ts @@ -19,11 +19,6 @@ export const loginStub = { }, }; -export const changePasswordStub = { - password: 'Password123', - newPassword: 'Password1234', -}; - export const authStub = { admin: Object.freeze({ user: { @@ -57,7 +52,6 @@ export const authStub = { id: 'user-id', email: 'immich@test.com', isAdmin: false, - externalPath: '/data/user1', } as UserEntity, userToken: { id: 'token-id', diff --git a/server/test/fixtures/device.stub.ts b/server/test/fixtures/device.stub.ts deleted file mode 100644 index c7f2d21c57..0000000000 --- a/server/test/fixtures/device.stub.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const deviceStub = { - current: { - id: expect.any(String), - createdAt: expect.any(String), - updatedAt: expect.any(String), - current: true, - deviceOS: '', - deviceType: '', - }, -}; diff --git a/server/test/fixtures/index.ts b/server/test/fixtures/index.ts index ab09d83077..7a25c159a3 100644 --- a/server/test/fixtures/index.ts +++ b/server/test/fixtures/index.ts @@ -3,7 +3,6 @@ export * from './api-key.stub'; export * from './asset.stub'; export * from './audit.stub'; export * from './auth.stub'; -export * from './device.stub'; export * from './error.stub'; export * from './face.stub'; export * from './file.stub'; diff --git a/server/test/fixtures/library.stub.ts b/server/test/fixtures/library.stub.ts index 3a203ce87c..ec8e196835 100644 --- a/server/test/fixtures/library.stub.ts +++ b/server/test/fixtures/library.stub.ts @@ -20,8 +20,8 @@ export const libraryStub = { id: 'library-id', name: 'test_library', assets: [], - owner: userStub.externalPath1, - ownerId: 'user-id', + owner: userStub.admin, + ownerId: 'admin_id', type: LibraryType.EXTERNAL, importPaths: [], createdAt: new Date('2023-01-01'), @@ -34,8 +34,8 @@ export const libraryStub = { id: 'library-id2', name: 'test_library2', assets: [], - owner: userStub.externalPath1, - ownerId: 'user-id', + owner: userStub.admin, + ownerId: 'admin_id', type: LibraryType.EXTERNAL, importPaths: [], createdAt: new Date('2021-01-01'), @@ -48,8 +48,8 @@ export const libraryStub = { id: 'library-id-with-paths1', name: 'library-with-import-paths1', assets: [], - owner: userStub.externalPath1, - ownerId: 'user-id', + owner: userStub.admin, + ownerId: 'admin_id', type: LibraryType.EXTERNAL, importPaths: ['/foo', '/bar'], createdAt: new Date('2023-01-01'), @@ -62,8 +62,8 @@ export const libraryStub = { id: 'library-id-with-paths2', name: 'library-with-import-paths2', assets: [], - owner: userStub.externalPath1, - ownerId: 'user-id', + owner: userStub.admin, + ownerId: 'admin_id', type: LibraryType.EXTERNAL, importPaths: ['/xyz', '/asdf'], createdAt: new Date('2023-01-01'), @@ -76,7 +76,7 @@ export const libraryStub = { id: 'library-id', name: 'test_library', assets: [], - owner: userStub.externalPath1, + owner: userStub.admin, ownerId: 'user-id', type: LibraryType.EXTERNAL, importPaths: [], @@ -90,7 +90,7 @@ export const libraryStub = { id: 'library-id1337', name: 'importpath-exclusion-library1', assets: [], - owner: userStub.externalPath1, + owner: userStub.admin, ownerId: 'user-id', type: LibraryType.EXTERNAL, importPaths: ['/xyz', '/asdf'], diff --git a/server/test/fixtures/media.stub.ts b/server/test/fixtures/media.stub.ts index 30dfec1669..ad9a4baf72 100644 --- a/server/test/fixtures/media.stub.ts +++ b/server/test/fixtures/media.stub.ts @@ -13,16 +13,14 @@ const probeStubDefaultVideoStream: VideoStreamInfo[] = [ height: 1080, width: 1920, codecName: 'hevc', - codecType: 'video', frameCount: 100, rotation: 0, isHDR: false, + bitrate: 0, }, ]; -const probeStubDefaultAudioStream: AudioStreamInfo[] = [ - { index: 1, codecName: 'aac', codecType: 'audio', frameCount: 100 }, -]; +const probeStubDefaultAudioStream: AudioStreamInfo[] = [{ index: 1, codecName: 'mp3', frameCount: 100 }]; const probeStubDefault: VideoInfo = { format: probeStubDefaultFormat, @@ -41,20 +39,20 @@ export const probeStub = { height: 1080, width: 400, codecName: 'hevc', - codecType: 'video', frameCount: 100, rotation: 0, isHDR: false, + bitrate: 0, }, { index: 1, height: 1080, width: 400, codecName: 'h7000', - codecType: 'video', frameCount: 99, rotation: 0, isHDR: false, + bitrate: 0, }, ], }), @@ -66,10 +64,10 @@ export const probeStub = { height: 0, width: 400, codecName: 'hevc', - codecType: 'video', frameCount: 100, rotation: 0, isHDR: false, + bitrate: 0, }, ], }), @@ -81,21 +79,16 @@ export const probeStub = { height: 2160, width: 3840, codecName: 'h264', - codecType: 'video', frameCount: 100, rotation: 0, isHDR: false, + bitrate: 0, }, ], }), videoStream40Mbps: Object.freeze({ ...probeStubDefault, - format: { - formatName: 'mov,mp4,m4a,3gp,3g2,mj2', - formatLongName: 'QuickTime / MOV', - duration: 0, - bitrate: 40_000_000, - }, + videoStreams: [{ ...probeStubDefaultVideoStream[0], bitrate: 40_000_000 }], }), videoStreamHDR: Object.freeze({ ...probeStubDefault, @@ -105,10 +98,10 @@ export const probeStub = { height: 480, width: 480, codecName: 'h264', - codecType: 'video', frameCount: 100, rotation: 0, isHDR: true, + bitrate: 0, }, ], }), @@ -120,10 +113,10 @@ export const probeStub = { height: 2160, width: 3840, codecName: 'h264', - codecType: 'video', frameCount: 100, rotation: 90, isHDR: false, + bitrate: 0, }, ], }), @@ -135,10 +128,10 @@ export const probeStub = { height: 355, width: 1586, codecName: 'h264', - codecType: 'video', frameCount: 100, rotation: 0, isHDR: false, + bitrate: 0, }, ], }), @@ -150,16 +143,16 @@ export const probeStub = { height: 1586, width: 355, codecName: 'h264', - codecType: 'video', frameCount: 100, rotation: 0, isHDR: false, + bitrate: 0, }, ], }), - audioStreamMp3: Object.freeze({ + audioStreamAac: Object.freeze({ ...probeStubDefault, - audioStreams: [{ index: 1, codecType: 'audio', codecName: 'aac', frameCount: 100 }], + audioStreams: [{ index: 1, codecName: 'aac', frameCount: 100 }], }), matroskaContainer: Object.freeze({ ...probeStubDefault, @@ -170,4 +163,14 @@ export const probeStub = { bitrate: 0, }, }), + videoStreamVp9: Object.freeze({ + ...probeStubDefault, + videoStreams: [{ ...probeStubDefaultVideoStream[0], codecName: 'vp9' }], + format: { + formatName: 'matroska,webm', + formatLongName: 'Matroska / WebM', + duration: 0, + bitrate: 0, + }, + }), }; diff --git a/server/test/fixtures/system-config.stub.ts b/server/test/fixtures/system-config.stub.ts index 721bb181c4..0e99fb07a2 100644 --- a/server/test/fixtures/system-config.stub.ts +++ b/server/test/fixtures/system-config.stub.ts @@ -22,6 +22,11 @@ export const systemConfigStub: Record = { { key: SystemConfigKey.OAUTH_MOBILE_REDIRECT_URI, value: 'http://mobile-redirect' }, { key: SystemConfigKey.OAUTH_BUTTON_TEXT, value: 'OAuth' }, ], + withDefaultStorageQuota: [ + { key: SystemConfigKey.OAUTH_ENABLED, value: true }, + { key: SystemConfigKey.OAUTH_AUTO_REGISTER, value: true }, + { key: SystemConfigKey.OAUTH_DEFAULT_STORAGE_QUOTA, value: 1 }, + ], libraryWatchEnabled: [{ key: SystemConfigKey.LIBRARY_WATCH_ENABLED, value: true }], libraryWatchDisabled: [{ key: SystemConfigKey.LIBRARY_WATCH_ENABLED, value: false }], }; diff --git a/server/test/fixtures/user.stub.ts b/server/test/fixtures/user.stub.ts index 770e615306..e0d9113c65 100644 --- a/server/test/fixtures/user.stub.ts +++ b/server/test/fixtures/user.stub.ts @@ -31,7 +31,6 @@ export const userStub = { password: 'admin_password', name: 'admin_name', storageLabel: 'admin', - externalPath: null, oauthId: '', shouldChangePassword: false, profileImagePath: '', @@ -50,7 +49,6 @@ export const userStub = { password: 'immich_password', name: 'immich_name', storageLabel: null, - externalPath: null, oauthId: '', shouldChangePassword: false, profileImagePath: '', @@ -69,7 +67,6 @@ export const userStub = { password: 'immich_password', name: 'immich_name', storageLabel: null, - externalPath: null, oauthId: '', shouldChangePassword: false, profileImagePath: '', @@ -88,7 +85,6 @@ export const userStub = { password: 'immich_password', name: 'immich_name', storageLabel: 'label-1', - externalPath: null, oauthId: '', shouldChangePassword: false, profileImagePath: '', @@ -102,31 +98,11 @@ export const userStub = { quotaSizeInBytes: null, quotaUsageInBytes: 0, }), - externalPath1: Object.freeze({ + externalPathRoot: Object.freeze({ ...authStub.user1.user, password: 'immich_password', name: 'immich_name', storageLabel: 'label-1', - externalPath: '/data/user1', - oauthId: '', - shouldChangePassword: false, - profileImagePath: '', - createdAt: new Date('2021-01-01'), - deletedAt: null, - updatedAt: new Date('2021-01-01'), - tags: [], - assets: [], - memoriesEnabled: true, - avatarColor: UserAvatarColor.PRIMARY, - quotaSizeInBytes: null, - quotaUsageInBytes: 0, - }), - externalPath2: Object.freeze({ - ...authStub.user1.user, - password: 'immich_password', - name: 'immich_name', - storageLabel: 'label-1', - externalPath: '/data/user2', oauthId: '', shouldChangePassword: false, profileImagePath: '', @@ -145,7 +121,6 @@ export const userStub = { password: 'immich_password', name: 'immich_name', storageLabel: 'label-1', - externalPath: null, oauthId: '', shouldChangePassword: false, profileImagePath: '/path/to/profile.jpg', diff --git a/server/test/repositories/asset.repository.mock.ts b/server/test/repositories/asset.repository.mock.ts index 1c98b78c9e..0be384b3ae 100644 --- a/server/test/repositories/asset.repository.mock.ts +++ b/server/test/repositories/asset.repository.mock.ts @@ -18,6 +18,7 @@ export const newAssetRepositoryMock = (): jest.Mocked => { getFirstAssetForAlbumId: jest.fn(), getLastUpdatedAssetForAlbumId: jest.fn(), getAll: jest.fn().mockResolvedValue({ items: [], hasNextPage: false }), + getAllByFileCreationDate: jest.fn(), getAllByDeviceId: jest.fn(), updateAll: jest.fn(), getByLibraryId: jest.fn(), diff --git a/server/test/repositories/library.repository.mock.ts b/server/test/repositories/library.repository.mock.ts index 264acc08ef..740f4c4837 100644 --- a/server/test/repositories/library.repository.mock.ts +++ b/server/test/repositories/library.repository.mock.ts @@ -4,7 +4,6 @@ export const newLibraryRepositoryMock = (): jest.Mocked => { return { get: jest.fn(), getCountForUser: jest.fn(), - getAllByUserId: jest.fn(), create: jest.fn(), delete: jest.fn(), softDelete: jest.fn(), @@ -12,9 +11,7 @@ export const newLibraryRepositoryMock = (): jest.Mocked => { getStatistics: jest.fn(), getDefaultUploadLibrary: jest.fn(), getUploadLibraryCount: jest.fn(), - getOnlineAssetPaths: jest.fn(), getAssetIds: jest.fn(), - existsByName: jest.fn(), getAllDeleted: jest.fn(), getAll: jest.fn(), }; diff --git a/server/test/repositories/search.repository.mock.ts b/server/test/repositories/search.repository.mock.ts index e0bdab269a..5912d77451 100644 --- a/server/test/repositories/search.repository.mock.ts +++ b/server/test/repositories/search.repository.mock.ts @@ -7,5 +7,7 @@ export const newSearchRepositoryMock = (): jest.Mocked => { searchSmart: jest.fn(), searchFaces: jest.fn(), upsert: jest.fn(), + searchPlaces: jest.fn(), + deleteAllSearchEmbeddings: jest.fn(), }; }; diff --git a/web/.eslintrc.cjs b/web/.eslintrc.cjs index de62060e0f..535765f115 100644 --- a/web/.eslintrc.cjs +++ b/web/.eslintrc.cjs @@ -13,6 +13,8 @@ module.exports = { sourceType: 'module', ecmaVersion: 2022, extraFileExtensions: ['.svelte'], + tsconfigRootDir: __dirname, + project: ['./tsconfig.json'], }, env: { browser: true, @@ -32,12 +34,6 @@ module.exports = { NodeJS: true, }, rules: { - 'unicorn/no-useless-undefined': 'off', - 'unicorn/prefer-spread': 'off', - 'unicorn/no-null': 'off', - 'unicorn/prevent-abbreviations': 'off', - 'unicorn/no-nested-ternary': 'off', - 'unicorn/consistent-function-scoping': 'off', '@typescript-eslint/no-unused-vars': [ 'warn', { @@ -47,5 +43,17 @@ module.exports = { }, ], curly: 2, + 'unicorn/no-useless-undefined': 'off', + 'unicorn/prefer-spread': 'off', + 'unicorn/no-null': 'off', + 'unicorn/prevent-abbreviations': 'off', + 'unicorn/no-nested-ternary': 'off', + 'unicorn/consistent-function-scoping': 'off', + 'unicorn/prefer-top-level-await': 'off', + // TODO: set recommended-type-checked and remove these rules + '@typescript-eslint/await-thenable': 'error', + '@typescript-eslint/no-floating-promises': 'error', + '@typescript-eslint/no-misused-promises': 'error', + '@typescript-eslint/require-await': 'error', }, }; diff --git a/web/.prettierrc b/web/.prettierrc index 33325428a2..92fabc32da 100644 --- a/web/.prettierrc +++ b/web/.prettierrc @@ -3,6 +3,7 @@ "trailingComma": "all", "printWidth": 120, "semi": true, - "plugins": ["prettier-plugin-svelte"], + "plugins": ["prettier-plugin-organize-imports", "prettier-plugin-svelte"], + "organizeImportsSkipDestructiveCodeActions": true, "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] } diff --git a/web/Dockerfile b/web/Dockerfile index 16ce7ac975..27d206e92a 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -1,5 +1,6 @@ -FROM node:iron-alpine3.18 +FROM node:iron-alpine3.18@sha256:a02826c7340c37a29179152723190bcc3044f933c925f3c2d78abb20f794de3f +RUN apk add --no-cache tini USER node WORKDIR /usr/src/app COPY --chown=node:node package*.json ./ @@ -8,3 +9,4 @@ COPY --chown=node:node . . ENV CHOKIDAR_USEPOLLING=true EXPOSE 24678 EXPOSE 3000 +ENTRYPOINT ["/sbin/tini", "--", "/bin/sh"] diff --git a/web/bin/immich-web b/web/bin/immich-web index 3d3b3dc46a..4d3b1e01a7 100755 --- a/web/bin/immich-web +++ b/web/bin/immich-web @@ -2,12 +2,7 @@ TYPESCRIPT_SDK=/usr/src/open-api/typescript-sdk -if [ ! -d "$TYPESCRIPT_SDK/build" ]; then - echo "$TYPESCRIPT_SDK/build does not exist, building" - npm --prefix "$TYPESCRIPT_SDK" install - npm --prefix "$TYPESCRIPT_SDK" run build -else - echo "$TYPESCRIPT_SDK/build exists, skipping" -fi +npm --prefix "$TYPESCRIPT_SDK" install +npm --prefix "$TYPESCRIPT_SDK" run build node ./node_modules/.bin/vite dev --host 0.0.0.0 --port 3000 diff --git a/web/package-lock.json b/web/package-lock.json index 539479618d..023705f7e7 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,65 +1,64 @@ { "name": "immich-web", - "version": "1.0.0", + "version": "1.97.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-web", - "version": "1.0.0", + "version": "1.97.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@immich/sdk": "file:../open-api/typescript-sdk", - "@mdi/js": "^7.3.67", - "@photo-sphere-viewer/core": "^5.7.0", - "@zoom-image/svelte": "^0.2.0", - "axios": "^1.6.7", + "@mdi/js": "^7.4.47", + "@photo-sphere-viewer/core": "^5.7.1", + "@zoom-image/svelte": "^0.2.6", "buffer": "^6.0.3", "copy-image-clipboard": "^2.1.2", "dom-to-image": "^2.6.0", - "handlebars": "^4.7.7", + "handlebars": "^4.7.8", "justified-layout": "^4.1.0", "lodash-es": "^4.17.21", - "luxon": "^3.2.1", - "socket.io-client": "^4.6.1", - "svelte-local-storage-store": "^0.6.0", - "svelte-maplibre": "^0.7.0", + "luxon": "^3.4.4", + "socket.io-client": "^4.7.4", + "svelte-local-storage-store": "^0.6.4", + "svelte-maplibre": "^0.8.1", "thumbhash": "^0.1.1" }, "devDependencies": { - "@faker-js/faker": "^8.0.0", - "@floating-ui/dom": "^1.5.1", + "@faker-js/faker": "^8.4.1", + "@socket.io/component-emitter": "^3.1.0", "@sveltejs/adapter-static": "^3.0.1", - "@sveltejs/kit": "^2.0.6", - "@sveltejs/vite-plugin-svelte": "^3.0.0", - "@testing-library/jest-dom": "^6.1.5", - "@testing-library/svelte": "^4.0.3", - "@types/dom-to-image": "^2.6.4", - "@types/justified-layout": "^4.1.0", - "@types/lodash-es": "^4.17.6", - "@types/luxon": "^3.2.0", - "@typescript-eslint/eslint-plugin": "^6.4.1", - "@typescript-eslint/parser": "^6.4.1", - "@vitest/coverage-v8": "^1.0.4", - "autoprefixer": "^10.4.13", - "eslint": "^8.34.0", + "@sveltejs/enhanced-img": "^0.1.8", + "@sveltejs/kit": "^2.5.2", + "@sveltejs/vite-plugin-svelte": "^3.0.2", + "@testing-library/jest-dom": "^6.4.2", + "@testing-library/svelte": "^4.1.0", + "@types/dom-to-image": "^2.6.7", + "@types/justified-layout": "^4.1.4", + "@types/lodash-es": "^4.17.12", + "@types/luxon": "^3.4.2", + "@typescript-eslint/eslint-plugin": "^7.1.0", + "@typescript-eslint/parser": "^7.1.0", + "@vitest/coverage-v8": "^1.3.1", + "autoprefixer": "^10.4.17", + "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-svelte": "^2.30.0", - "eslint-plugin-unicorn": "^51.0.0", - "factory.ts": "^1.3.0", - "identity-obj-proxy": "^3.0.0", - "postcss": "^8.4.21", - "prettier": "^3.1.0", - "prettier-plugin-svelte": "^3.1.2", + "eslint-plugin-svelte": "^2.35.1", + "eslint-plugin-unicorn": "^51.0.1", + "factory.ts": "^1.4.1", + "postcss": "^8.4.35", + "prettier": "^3.2.5", + "prettier-plugin-organize-imports": "^3.2.4", + "prettier-plugin-svelte": "^3.2.1", "rollup-plugin-visualizer": "^5.12.0", - "svelte": "^4.0.5", - "svelte-check": "^3.4.3", - "svelte-preprocess": "^5.0.3", - "tailwindcss": "^3.2.7", - "tslib": "^2.5.0", + "svelte": "^4.2.12", + "svelte-check": "^3.6.5", + "tailwindcss": "^3.4.1", + "tslib": "^2.6.2", "typescript": "^5.3.3", - "vite": "^5.1.1", - "vitest": "^1.0.4" + "vite": "^5.1.4", + "vitest": "^1.3.1" } }, "../open-api/typescript-sdk": { @@ -70,14 +69,6 @@ "@oazapfts/runtime": "^1.0.0", "@types/node": "^20.11.0", "typescript": "^5.3.3" - }, - "peerDependencies": { - "axios": "^1.6.7" - }, - "peerDependenciesMeta": { - "axios": { - "optional": true - } } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -455,6 +446,16 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@emnapi/runtime": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz", + "integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.19.11", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz", @@ -886,9 +887,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -910,39 +911,14 @@ "npm": ">=6.14.13" } }, - "node_modules/@floating-ui/core": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", - "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", - "dev": true, - "dependencies": { - "@floating-ui/utils": "^0.2.1" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.1.tgz", - "integrity": "sha512-iA8qE43/H5iGozC3W0YSnVSW42Vh522yyM1gj+BqRwVsTNOyr231PsXDaV04yT39PsO0QL2QpbI/M0ZaLUQgRQ==", - "dev": true, - "dependencies": { - "@floating-ui/core": "^1.6.0", - "@floating-ui/utils": "^0.2.1" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", - "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==", - "dev": true - }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -963,11 +939,461 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", "dev": true }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.2.tgz", + "integrity": "sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.1" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.2.tgz", + "integrity": "sha512-/rK/69Rrp9x5kaWBjVN07KixZanRr+W1OiyKdXcbjQD6KbW+obaTeBBtLUAtbBsnlTTmWthw99xqoOS7SsySDg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.1" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.1.tgz", + "integrity": "sha512-kQyrSNd6lmBV7O0BUiyu/OEw9yeNGFbQhbxswS1i6rMDwBBSX+e+rPzu3S+MwAiGU3HdLze3PanQ4Xkfemgzcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "macos": ">=11", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.1.tgz", + "integrity": "sha512-eVU/JYLPVjhhrd8Tk6gosl5pVlvsqiFlt50wotCvdkFGf+mDNBJxMh+bvav+Wt3EBnNZWq8Sp2I7XfSjm8siog==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "macos": ">=10.13", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.1.tgz", + "integrity": "sha512-FtdMvR4R99FTsD53IA3LxYGghQ82t3yt0ZQ93WMZ2xV3dqrb0E8zq4VHaTOuLEAuA83oDawHV3fd+BsAPadHIQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.28", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.1.tgz", + "integrity": "sha512-bnGG+MJjdX70mAQcSLxgeJco11G+MxTz+ebxlz8Y3dxyeb3Nkl7LgLI0mXupoO+u1wRNx/iRj5yHtzA4sde1yA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.1.tgz", + "integrity": "sha512-3+rzfAR1YpMOeA2zZNp+aYEzGNWK4zF3+sdMxuCS3ey9HhDbJ66w6hDSHDMoap32DueFwhhs3vwooAB2MaK4XQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.28", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.1.tgz", + "integrity": "sha512-3NR1mxFsaSgMMzz1bAnnKbSAI+lHXVTqAHgc1bgzjHuXjo4hlscpUxc0vFSAPKI3yuzdzcZOkq7nDPrP2F8Jgw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.1.tgz", + "integrity": "sha512-5aBRcjHDG/T6jwC3Edl3lP8nl9U2Yo8+oTl5drd1dh9Z1EBfzUKAJFUDTDisDjUwc7N4AjnPGfCA3jl3hY8uDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.1.tgz", + "integrity": "sha512-dcT7inI9DBFK6ovfeWRe3hG30h51cBAP5JXlZfx6pzc/Mnf9HFCQDLtYf4MCBjxaaTfjCCjkBxcy3XzOAo5txw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.2.tgz", + "integrity": "sha512-Fndk/4Zq3vAc4G/qyfXASbS3HBZbKrlnKZLEJzPLrXoJuipFNNwTes71+Ki1hwYW5lch26niRYoZFAtZVf3EGA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.28", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.1" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.2.tgz", + "integrity": "sha512-pz0NNo882vVfqJ0yNInuG9YH71smP4gRSdeL09ukC2YLE6ZyZePAlWKEHgAzJGTiOh8Qkaov6mMIMlEhmLdKew==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.1" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.2.tgz", + "integrity": "sha512-MBoInDXDppMfhSzbMmOQtGfloVAflS2rP1qPcUIiITMi36Mm5YR7r0ASND99razjQUpHTzjrU1flO76hKvP5RA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.28", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.1" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.2.tgz", + "integrity": "sha512-xUT82H5IbXewKkeF5aiooajoO1tQV4PnKfS/OZtb5DDdxS/FCI/uXTVZ35GQ97RZXsycojz/AJ0asoz6p2/H/A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.1" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.2.tgz", + "integrity": "sha512-F+0z8JCu/UnMzg8IYW1TMeiViIWBVg7IWP6nE0p5S5EPQxlLd76c8jYemG21X99UzFwgkRo5yz2DS+zbrnxZeA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.1" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.2.tgz", + "integrity": "sha512-+ZLE3SQmSL+Fn1gmSaM8uFusW5Y3J9VOf+wMGNnTtJUMUxFhv+P4UPaYEYT8tqnyYVaOVGgMN/zsOxn9pSsO2A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.1" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.2.tgz", + "integrity": "sha512-fLbTaESVKuQcpm8ffgBD7jLb/CQLcATju/jxtTXR1XCLwbOQt+OL5zPHSDMmp2JZIeq82e18yE0Vv7zh6+6BfQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "optional": true, + "dependencies": { + "@emnapi/runtime": "^0.45.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.2.tgz", + "integrity": "sha512-okBpql96hIGuZ4lN3+nsAjGeggxKm7hIRu9zyec0lnfB8E7Z6p95BuRZzDDXZOl2e8UmR4RhYt631i7mfmKU8g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.2.tgz", + "integrity": "sha512-E4magOks77DK47FwHUIGH0RYWSgRBfGdK56kIHSVeB9uIS4pPFr4N2kIVsXdQQo4LzOsENKV5KAhRlRL7eMAdg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@immich/sdk": { "resolved": "../open-api/typescript-sdk", "link": true @@ -1088,13 +1514,13 @@ } }, "node_modules/@maplibre/maplibre-gl-style-spec": { - "version": "19.3.3", - "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-19.3.3.tgz", - "integrity": "sha512-cOZZOVhDSulgK0meTsTkmNXb1ahVvmTmWmfx9gRBwc6hq98wS9JP35ESIoNq3xqEan+UN+gn8187Z6E4NKhLsw==", + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-20.1.1.tgz", + "integrity": "sha512-z85ARNPCBI2Cs5cPOS3DSbraTN+ue8zrcYVoSWBuNrD/mA+2SKAJ+hIzI22uN7gac6jBMnCdpPKRxS/V0KSZVQ==", "dependencies": { "@mapbox/jsonlint-lines-primitives": "~2.0.2", "@mapbox/unitbezier": "^0.0.1", - "json-stringify-pretty-compact": "^3.0.0", + "json-stringify-pretty-compact": "^4.0.0", "minimist": "^1.2.8", "rw": "^1.3.3", "sort-object": "^3.0.3" @@ -1155,9 +1581,9 @@ } }, "node_modules/@photo-sphere-viewer/core": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.7.0.tgz", - "integrity": "sha512-9IIvs4P8LWr/lTYQLEuhdGvkoK6dGCRLiVD54QybxIG7dt5dudC8Jq/GIQQb8G/QcbjmdW49ezA9LPxHTNYIgg==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.7.1.tgz", + "integrity": "sha512-2wwErCpRiCee8mv9k5YAr4itMrAh2DL9dHriaOysS84xRJcmEZBMZXfhTuTFEdBvEq0o8Iv/gKheQvCrbLyv5w==", "dependencies": { "three": "^0.161.0" } @@ -1168,6 +1594,34 @@ "integrity": "sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==", "dev": true }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.5.tgz", @@ -1357,10 +1811,21 @@ "@sveltejs/kit": "^2.0.0" } }, + "node_modules/@sveltejs/enhanced-img": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@sveltejs/enhanced-img/-/enhanced-img-0.1.8.tgz", + "integrity": "sha512-0cLVR9KiO0/t3VVm64OM7bPHTkdaT2aaz1rwoAhao+EBXR3vMvLoYXLHvz8o9/552PSV8G844RkH7qkGc3YAiQ==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.5", + "svelte-parse-markup": "^0.1.2", + "vite-imagetools": "^6.2.8" + } + }, "node_modules/@sveltejs/kit": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.0.tgz", - "integrity": "sha512-1uyXvzC2Lu1FZa30T4y5jUAC21R309ZMRG0TPt+PPPbNUoDpy8zSmSNVWYaBWxYDqLGQ5oPNWvjvvF2IjJ1jmA==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.2.tgz", + "integrity": "sha512-1Pm2lsBYURQsjnLyZa+jw75eVD4gYHxGRwPyFe4DAmB3FjTVR8vRNWGeuDLGFcKMh/B1ij6FTUrc9GrerogCng==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -1683,9 +2148,17 @@ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" }, "node_modules/@types/geojson": { - "version": "7946.0.13", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.13.tgz", - "integrity": "sha512-bmrNrgKMOhM3WsafmbGmC+6dsF2Z308vLFsQ3a/bT8X8Sv5clVYpPars/UPq+sAaJP+5OoLAYgwbkS5QEJdLUQ==" + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==" + }, + "node_modules/@types/geojson-vt": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@types/geojson-vt/-/geojson-vt-3.2.5.tgz", + "integrity": "sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==", + "dependencies": { + "@types/geojson": "*" + } }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", @@ -1705,6 +2178,14 @@ "integrity": "sha512-q2ybP0u0NVj87oMnGZOGxY2iUN8ddr48zPOBHBdbOLpsMTA/keGj+93ou+OMCnJk0xewzlNIaVEkxM6VBD3E2w==", "dev": true }, + "node_modules/@types/leaflet": { + "version": "1.9.8", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.8.tgz", + "integrity": "sha512-EXdsL4EhoUtGm2GC2ZYtXn+Fzc6pluVgagvo2VC1RHWToLGlTRwVYoDpqS/7QXa01rmDyBjJk3Catpf60VMkwg==", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@types/lodash": { "version": "4.14.199", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.199.tgz", @@ -1767,9 +2248,9 @@ "dev": true }, "node_modules/@types/semver": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.7.tgz", - "integrity": "sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "node_modules/@types/supercluster": { @@ -1781,16 +2262,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.1.0.tgz", + "integrity": "sha512-j6vT/kCulhG5wBmGtstKeiVr1rdXE4nk+DT1k6trYkwlrvW9eOF5ZbgKnd/YR6PcM4uTEXa0h6Fcvf6X7Dxl0w==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/scope-manager": "7.1.0", + "@typescript-eslint/type-utils": "7.1.0", + "@typescript-eslint/utils": "7.1.0", + "@typescript-eslint/visitor-keys": "7.1.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -1806,8 +2287,8 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -1849,15 +2330,15 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.1.0.tgz", + "integrity": "sha512-V1EknKUubZ1gWFjiOZhDSNToOjs63/9O0puCgGS8aDOgpZY326fzFu15QAUjwaXzRZjf/qdsdBrckYdv9YxB8w==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/scope-manager": "7.1.0", + "@typescript-eslint/types": "7.1.0", + "@typescript-eslint/typescript-estree": "7.1.0", + "@typescript-eslint/visitor-keys": "7.1.0", "debug": "^4.3.4" }, "engines": { @@ -1868,7 +2349,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -1877,13 +2358,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.1.0.tgz", + "integrity": "sha512-6TmN4OJiohHfoOdGZ3huuLhpiUgOGTpgXNUPJgeZOZR3DnIpdSgtt83RS35OYNNXxM4TScVlpVKC9jyQSETR1A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" + "@typescript-eslint/types": "7.1.0", + "@typescript-eslint/visitor-keys": "7.1.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1894,13 +2375,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.1.0.tgz", + "integrity": "sha512-UZIhv8G+5b5skkcuhgvxYWHjk7FW7/JP5lPASMEUoliAPwIH/rxoUSQPia2cuOj9AmDZmwUl1usKm85t5VUMew==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/typescript-estree": "7.1.0", + "@typescript-eslint/utils": "7.1.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -1912,7 +2393,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -1921,9 +2402,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.1.0.tgz", + "integrity": "sha512-qTWjWieJ1tRJkxgZYXx6WUYtWlBc48YRxgY2JN1aGeVpkhmnopq+SUC8UEVGNXIvWH7XyuTjwALfG6bFEgCkQA==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1934,13 +2415,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.1.0.tgz", + "integrity": "sha512-k7MyrbD6E463CBbSpcOnwa8oXRdHzH1WiVzOipK3L5KSML92ZKgUBrTlehdi7PEIMT8k0bQixHUGXggPAlKnOQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/types": "7.1.0", + "@typescript-eslint/visitor-keys": "7.1.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2019,17 +2500,17 @@ "dev": true }, "node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.1.0.tgz", + "integrity": "sha512-WUFba6PZC5OCGEmbweGpnNJytJiLG7ZvDBJJoUcX4qZYf1mGZ97mO2Mps6O2efxJcJdRNpqweCistDbZMwIVHw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/scope-manager": "7.1.0", + "@typescript-eslint/types": "7.1.0", + "@typescript-eslint/typescript-estree": "7.1.0", "semver": "^7.5.4" }, "engines": { @@ -2040,7 +2521,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" } }, "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { @@ -2077,12 +2558,12 @@ "dev": true }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.1.0.tgz", + "integrity": "sha512-FhUqNWluiGNzlvnDZiXad4mZRhtghdoKW6e98GoEOYSu5cND+E39rG5KwJMUzeENwm1ztYBRqof8wMLP+wNPIA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/types": "7.1.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -2100,24 +2581,23 @@ "dev": true }, "node_modules/@vitest/browser": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-1.0.4.tgz", - "integrity": "sha512-qMT1NhClex73eA2sOwnlwLcSIVCW8B7NFVzIKuXLKxSJD3LsNq8PCKhwOkBxklbSAcZdkOgL/d3/gzQT7k9eng==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-1.3.1.tgz", + "integrity": "sha512-pRof8G8nqRWwg3ouyIctyhfIVk5jXgF056uF//sqdi37+pVtDz9kBI/RMu0xlc8tgCyJ2aEMfbgJZPUydlEVaQ==", "dev": true, "optional": true, "peer": true, "dependencies": { - "estree-walker": "^3.0.3", + "@vitest/utils": "1.3.1", "magic-string": "^0.30.5", - "sirv": "^2.0.3" + "sirv": "^2.0.4" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "playwright": "*", - "safaridriver": "*", - "vitest": "^1.0.0", + "vitest": "1.3.1", "webdriverio": "*" }, "peerDependenciesMeta": { @@ -2133,9 +2613,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.2.2.tgz", - "integrity": "sha512-IHyKnDz18SFclIEEAHb9Y4Uxx0sPKC2VO1kdDCs1BF6Ip4S8rQprs971zIsooLUn7Afs71GRxWMWpkCGZpRMhw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.3.1.tgz", + "integrity": "sha512-UuBnkSJUNE9rdHjDCPyJ4fYuMkoMtnghes1XohYa4At0MS3OQSAo97FrbwSLRshYsXThMZy1+ybD/byK5llyIg==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.1", @@ -2156,17 +2636,17 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "^1.0.0" + "vitest": "1.3.1" } }, "node_modules/@vitest/expect": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.2.2.tgz", - "integrity": "sha512-3jpcdPAD7LwHUUiT2pZTj2U82I2Tcgg2oVPvKxhn6mDI2On6tfvPQTjAI4628GUGDZrCm4Zna9iQHm5cEexOAg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.3.1.tgz", + "integrity": "sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==", "dev": true, "dependencies": { - "@vitest/spy": "1.2.2", - "@vitest/utils": "1.2.2", + "@vitest/spy": "1.3.1", + "@vitest/utils": "1.3.1", "chai": "^4.3.10" }, "funding": { @@ -2174,12 +2654,12 @@ } }, "node_modules/@vitest/runner": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.2.2.tgz", - "integrity": "sha512-JctG7QZ4LSDXr5CsUweFgcpEvrcxOV1Gft7uHrvkQ+fsAVylmWQvnaAr/HDp3LAH1fztGMQZugIheTWjaGzYIg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.3.1.tgz", + "integrity": "sha512-5FzF9c3jG/z5bgCnjr8j9LNq/9OxV2uEBAITOXfoe3rdZJTdO7jzThth7FXv/6b+kdY65tpRQB7WaKhNZwX+Kg==", "dev": true, "dependencies": { - "@vitest/utils": "1.2.2", + "@vitest/utils": "1.3.1", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -2215,9 +2695,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.2.2.tgz", - "integrity": "sha512-SmGY4saEw1+bwE1th6S/cZmPxz/Q4JWsl7LvbQIky2tKE35US4gd0Mjzqfr84/4OD0tikGWaWdMja/nWL5NIPA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.3.1.tgz", + "integrity": "sha512-EF++BZbt6RZmOlE3SuTPu/NfwBF6q4ABS37HHXzs2LUVPBLx2QoY/K0fKpRChSo8eLiuxcbCVfqKgx/dplCDuQ==", "dev": true, "dependencies": { "magic-string": "^0.30.5", @@ -2261,9 +2741,9 @@ "dev": true }, "node_modules/@vitest/spy": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.2.2.tgz", - "integrity": "sha512-k9Gcahssw8d7X3pSLq3e3XEu/0L78mUkCjivUqCQeXJm9clfXR/Td8+AP+VC1O6fKPIDLcHDTAmBOINVuv6+7g==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.3.1.tgz", + "integrity": "sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig==", "dev": true, "dependencies": { "tinyspy": "^2.2.0" @@ -2273,9 +2753,9 @@ } }, "node_modules/@vitest/utils": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.2.2.tgz", - "integrity": "sha512-WKITBHLsBHlpjnDQahr+XK6RE7MiAsgrIkr0pGhQ9ygoxBfUeG0lUG5iLlzqjmKSlBv3+j5EGsriBzh+C3Tq9g==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.3.1.tgz", + "integrity": "sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==", "dev": true, "dependencies": { "diff-sequences": "^29.6.3", @@ -2320,9 +2800,9 @@ "dev": true }, "node_modules/@zoom-image/core": { - "version": "0.32.1", - "resolved": "https://registry.npmjs.org/@zoom-image/core/-/core-0.32.1.tgz", - "integrity": "sha512-R56D749Ck+/1yLWlEJ2FctxjdpTQEje3jPhOAbeEZGzLndIumskO42UqRNixcER6sAzCi01oYopmqnCpDElF0g==", + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@zoom-image/core/-/core-0.33.0.tgz", + "integrity": "sha512-wkMV8+aE7PeknLFhpIb/6vwRl09Z2gWM4UqKdnXO6Mb0pP9BiuDLcLvGGGB4o++uAPINgDwmNn+Loo641XSjDA==", "dependencies": { "@namnode/store": "^0.1.0" }, @@ -2332,11 +2812,11 @@ } }, "node_modules/@zoom-image/svelte": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@zoom-image/svelte/-/svelte-0.2.4.tgz", - "integrity": "sha512-rgfgn7Q60VrwmE4MPBzDWaFplc+411Lxg1nMdAnq/UTv4HTWSpiwm1IOg8gQZjRp92a8RXcRmUYXU+wFKEMjSg==", + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@zoom-image/svelte/-/svelte-0.2.6.tgz", + "integrity": "sha512-dEpA/egmTjVcptwhtcKHvkhVMTzQCpH17erfcXuJByt+nn5Oo4LnZOxE8gwSVEdPp65Ns6Y/byYD0GSQ/vv+DQ==", "dependencies": { - "@zoom-image/core": "0.32.1" + "@zoom-image/core": "0.33.0" }, "funding": { "type": "github", @@ -2515,7 +2995,10 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "optional": true, + "peer": true }, "node_modules/autoprefixer": { "version": "10.4.17", @@ -2566,16 +3049,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", - "dependencies": { - "follow-redirects": "^1.15.4", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, "node_modules/axobject-query": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz", @@ -2949,6 +3422,19 @@ "periscopic": "^3.1.0" } }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -2964,10 +3450,41 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "optional": true, + "peer": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -3242,6 +3759,9 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.4.0" } @@ -3263,6 +3783,15 @@ "node": ">=8" } }, + "node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/devalue": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/devalue/-/devalue-4.3.2.tgz", @@ -3488,16 +4017,16 @@ } }, "node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -4025,9 +4554,9 @@ } }, "node_modules/fflate": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.1.tgz", - "integrity": "sha512-/exOvEuc+/iaUm105QIiOt4LpBdMTWsXxqR0HDF35vx3fmaKzw7354gTilCh5rkzEt8WYyG//ku3h3nRmd7CHQ==" + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" }, "node_modules/file-entry-cache": { "version": "6.0.1", @@ -4089,25 +4618,6 @@ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, - "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -4121,6 +4631,9 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "optional": true, + "peer": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -4394,12 +4907,6 @@ "uglify-js": "^3.1.4" } }, - "node_modules/harmony-reflect": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", - "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==", - "dev": true - }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -4560,18 +5067,6 @@ "node": ">=0.10.0" } }, - "node_modules/identity-obj-proxy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", - "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", - "dev": true, - "dependencies": { - "harmony-reflect": "^1.4.6" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -4600,6 +5095,18 @@ "node": ">= 4" } }, + "node_modules/imagetools-core": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/imagetools-core/-/imagetools-core-6.0.4.tgz", + "integrity": "sha512-N1qs5qn7u9nR3kboISkYuvJm8MohiphCfBa+wx1UOropVaFis9/mh6wuDPLHJNhl6/64C7q2Pch5NASVKAaSrg==", + "dev": true, + "dependencies": { + "sharp": "^0.33.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -5274,9 +5781,9 @@ "dev": true }, "node_modules/json-stringify-pretty-compact": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-3.0.0.tgz", - "integrity": "sha512-Rc2suX5meI0S3bfdZuA7JMFBGkJ875ApfVyq2WHELjBiiG22My/l7/8zPpH/CfFVQHuVLd8NLR0nv6vi0BYYKA==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz", + "integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==" }, "node_modules/json5": { "version": "2.2.3", @@ -5539,9 +6046,9 @@ "dev": true }, "node_modules/maplibre-gl": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-3.6.2.tgz", - "integrity": "sha512-krg2KFIdOpLPngONDhP6ixCoWl5kbdMINP0moMSJFVX7wX1Clm2M9hlNKXS8vBGlVWwR5R3ZfI6IPrYz7c+aCQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-4.0.1.tgz", + "integrity": "sha512-UF+wI2utIciFXNg6+gYaMe7IGa9fMLzAZM3vdlGilqyWYmuibjcN40yGVgkz2r28//aOLphvtli3TbDEjEqHww==", "dependencies": { "@mapbox/geojson-rewind": "^0.5.2", "@mapbox/jsonlint-lines-primitives": "^2.0.2", @@ -5550,8 +6057,9 @@ "@mapbox/unitbezier": "^0.0.1", "@mapbox/vector-tile": "^1.3.1", "@mapbox/whoots-js": "^3.1.0", - "@maplibre/maplibre-gl-style-spec": "^19.3.3", - "@types/geojson": "^7946.0.13", + "@maplibre/maplibre-gl-style-spec": "^20.1.1", + "@types/geojson": "^7946.0.14", + "@types/geojson-vt": "3.2.5", "@types/mapbox__point-geometry": "^0.1.4", "@types/mapbox__vector-tile": "^1.3.4", "@types/pbf": "^3.0.5", @@ -5614,6 +6122,9 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">= 0.6" } @@ -5622,6 +6133,9 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "optional": true, + "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -6189,10 +6703,11 @@ } }, "node_modules/pmtiles": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/pmtiles/-/pmtiles-2.11.0.tgz", - "integrity": "sha512-dU9SzzaqmCGpdEuTnIba6bDHT6j09ZJFIXxwGpvkiEnce3ZnBB1VKt6+EOmJGueriweaZLAMTUmKVElU2CBe0g==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pmtiles/-/pmtiles-3.0.3.tgz", + "integrity": "sha512-tj4l3HHJd6/qf9VefzlPK2eYEQgbf+4uXPzNlrj3k7hHvLtibYSxfp51TF6ALt4YezM8MCdiOminnHvdAyqyGg==", "dependencies": { + "@types/leaflet": "^1.9.8", "fflate": "^0.8.0" } }, @@ -6398,10 +6913,30 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prettier-plugin-organize-imports": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.2.4.tgz", + "integrity": "sha512-6m8WBhIp0dfwu0SkgfOxJqh+HpdyfqSSLfKKRZSFbDuEQXDDndb8fTpRWkUrX/uBenkex3MgnVk0J3b3Y5byog==", + "dev": true, + "peerDependencies": { + "@volar/vue-language-plugin-pug": "^1.0.4", + "@volar/vue-typescript": "^1.0.4", + "prettier": ">=2.0", + "typescript": ">=2.9" + }, + "peerDependenciesMeta": { + "@volar/vue-language-plugin-pug": { + "optional": true + }, + "@volar/vue-typescript": { + "optional": true + } + } + }, "node_modules/prettier-plugin-svelte": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.1.2.tgz", - "integrity": "sha512-7xfMZtwgAWHMT0iZc8jN4o65zgbAQ3+O32V6W7pXrqNvKnHnkoyQCGCbKeUyXKZLbYE0YhFRnamfxfkEGxm8qA==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.2.1.tgz", + "integrity": "sha512-ENAPbIxASf2R79IZwgkG5sBdeNA9kLRlXVvKKmTXh79zWTy0KKoT86XO2pHrTitUPINd+iXWy12MRmgzKGVckA==", "dev": true, "peerDependencies": { "prettier": "^3.0.0", @@ -6439,11 +6974,6 @@ "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==" }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -6974,6 +7504,79 @@ "node": ">=8" } }, + "node_modules/sharp": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.2.tgz", + "integrity": "sha512-WlYOPyyPDiiM07j/UO+E720ju6gtNtHjEGg5vovUk1Lgxyjm2LFO+37Nt/UI3MMh2l6hxTWQWi7qk3cXJTutcQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.2", + "semver": "^7.5.4" + }, + "engines": { + "libvips": ">=8.15.1", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.2", + "@img/sharp-darwin-x64": "0.33.2", + "@img/sharp-libvips-darwin-arm64": "1.0.1", + "@img/sharp-libvips-darwin-x64": "1.0.1", + "@img/sharp-libvips-linux-arm": "1.0.1", + "@img/sharp-libvips-linux-arm64": "1.0.1", + "@img/sharp-libvips-linux-s390x": "1.0.1", + "@img/sharp-libvips-linux-x64": "1.0.1", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.1", + "@img/sharp-libvips-linuxmusl-x64": "1.0.1", + "@img/sharp-linux-arm": "0.33.2", + "@img/sharp-linux-arm64": "0.33.2", + "@img/sharp-linux-s390x": "0.33.2", + "@img/sharp-linux-x64": "0.33.2", + "@img/sharp-linuxmusl-arm64": "0.33.2", + "@img/sharp-linuxmusl-x64": "0.33.2", + "@img/sharp-wasm32": "0.33.2", + "@img/sharp-win32-ia32": "0.33.2", + "@img/sharp-win32-x64": "0.33.2" + } + }, + "node_modules/sharp/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -7027,6 +7630,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + }, "node_modules/sirv": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", @@ -7302,17 +7920,23 @@ } }, "node_modules/strip-literal": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.3.0.tgz", - "integrity": "sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.0.0.tgz", + "integrity": "sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==", "dev": true, "dependencies": { - "acorn": "^8.10.0" + "js-tokens": "^8.0.2" }, "funding": { "url": "https://github.com/sponsors/antfu" } }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-8.0.3.tgz", + "integrity": "sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==", + "dev": true + }, "node_modules/sucrase": { "version": "3.34.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", @@ -7388,9 +8012,9 @@ } }, "node_modules/svelte": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.10.tgz", - "integrity": "sha512-Ep06yCaCdgG1Mafb/Rx8sJ1QS3RW2I2BxGp2Ui9LBHSZ2/tO/aGLc5WqPjgiAP6KAnLJGaIr/zzwQlOo1b8MxA==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.12.tgz", + "integrity": "sha512-d8+wsh5TfPwqVzbm4/HCXC783/KPHV60NvwitJnyTA5lWn1elhXMNWhXGCJ7PwPa8qFUnyJNIyuIRt2mT0WMug==", "dependencies": { "@ampproject/remapping": "^2.2.1", "@jridgewell/sourcemap-codec": "^1.4.15", @@ -7412,9 +8036,9 @@ } }, "node_modules/svelte-check": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.6.4.tgz", - "integrity": "sha512-mY/dqucqm46p72M8yZmn81WPZx9mN6uuw8UVfR3ZKQeLxQg5HDGO3HHm5AZuWZPYNMLJ+TRMn+TeN53HfQ/vsw==", + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.6.5.tgz", + "integrity": "sha512-5aLgoQEdadvp8ypvKQ2avhnQ+V9YPQQaWrTFlXFw5g/v8xIQBvo+X/WqxTyD+V/ItDqXg3+abUA53rdDHgUjCA==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.17", @@ -7484,15 +8108,15 @@ } }, "node_modules/svelte-maplibre": { - "version": "0.7.7", - "resolved": "https://registry.npmjs.org/svelte-maplibre/-/svelte-maplibre-0.7.7.tgz", - "integrity": "sha512-fnv8L3tA4EMePp9BGKAc8AvXCsg34z56NBMGjYkz6qkl90qSTY4vUhIu1KXbwjGfQmHBmPkIl9VSdnnHCMnaRA==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/svelte-maplibre/-/svelte-maplibre-0.8.1.tgz", + "integrity": "sha512-CTm/s0+mJzBHSoO5zPKBo3ORmUyiWS3Ex4xvVdNgVg+sDesHasEAJ0N1/NUrd56S33zgRdFZGzRnRguCnKFAzw==", "dependencies": { "d3-geo": "^3.1.0", "just-compare": "^2.3.0", "just-flush": "^2.3.0", - "maplibre-gl": "^3.5.0", - "pmtiles": "^2.10.0" + "maplibre-gl": "^4.0.0", + "pmtiles": "^3.0.3" }, "peerDependencies": { "@deck.gl/core": "^8.8.0", @@ -7512,6 +8136,18 @@ } } }, + "node_modules/svelte-parse-markup": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/svelte-parse-markup/-/svelte-parse-markup-0.1.2.tgz", + "integrity": "sha512-DycY7DJr7VqofiJ63ut1/NEG92HrWWL56VWITn/cJCu+LlZhMoBkBXT4opUitPEEwbq1nMQbv4vTKUfbOqIW1g==", + "dev": true, + "funding": { + "url": "https://bjornlu.com/sponsor" + }, + "peerDependencies": { + "svelte": "^3.0.0 || ^4.0.0" + } + }, "node_modules/svelte-preprocess": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.3.tgz", @@ -7755,9 +8391,9 @@ "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==" }, "node_modules/tinyspy": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.0.tgz", - "integrity": "sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", "dev": true, "engines": { "node": ">=14.0.0" @@ -8032,9 +8668,9 @@ } }, "node_modules/vite": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.1.tgz", - "integrity": "sha512-wclpAgY3F1tR7t9LL5CcHC41YPkQIpKUGeIuT8MdNwNZr6OqOTLs7JX5vIHAtzqLWXts0T+GDrh9pN2arneKqg==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.4.tgz", + "integrity": "sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==", "dev": true, "dependencies": { "esbuild": "^0.19.3", @@ -8086,10 +8722,23 @@ } } }, + "node_modules/vite-imagetools": { + "version": "6.2.9", + "resolved": "https://registry.npmjs.org/vite-imagetools/-/vite-imagetools-6.2.9.tgz", + "integrity": "sha512-C4ZYhgj2vAj43/TpZ06XlDNP0p/7LIeYbgUYr+xG44nM++4HGX6YZBKAYpiBNgiCFUTJ6eXkRppWBrfPMevgmg==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.5", + "imagetools-core": "^6.0.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/vite-node": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.2.2.tgz", - "integrity": "sha512-1as4rDTgVWJO3n1uHmUYqq7nsFgINQ9u+mRcXpjeOMJUmviqNKjcZB7UfRZrlM7MjYXMKpuWp5oGkjaFLnjawg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.3.1.tgz", + "integrity": "sha512-azbRrqRxlWTJEVbzInZCTchx0X69M/XPTCz4H+TLvlTcR/xH/3hkRqhOakT41fMJCMzXTu4UvegkZiEoJAWvng==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -8123,18 +8772,17 @@ } }, "node_modules/vitest": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.2.2.tgz", - "integrity": "sha512-d5Ouvrnms3GD9USIK36KG8OZ5bEvKEkITFtnGv56HFaSlbItJuYr7hv2Lkn903+AvRAgSixiamozUVfORUekjw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.3.1.tgz", + "integrity": "sha512-/1QJqXs8YbCrfv/GPQ05wAZf2eakUPLPa18vkJAKE7RXOKfVHqMZZ1WlTjiwl6Gcn65M5vpNUB6EFLnEdRdEXQ==", "dev": true, "dependencies": { - "@vitest/expect": "1.2.2", - "@vitest/runner": "1.2.2", - "@vitest/snapshot": "1.2.2", - "@vitest/spy": "1.2.2", - "@vitest/utils": "1.2.2", + "@vitest/expect": "1.3.1", + "@vitest/runner": "1.3.1", + "@vitest/snapshot": "1.3.1", + "@vitest/spy": "1.3.1", + "@vitest/utils": "1.3.1", "acorn-walk": "^8.3.2", - "cac": "^6.7.14", "chai": "^4.3.10", "debug": "^4.3.4", "execa": "^8.0.1", @@ -8143,11 +8791,11 @@ "pathe": "^1.1.1", "picocolors": "^1.0.0", "std-env": "^3.5.0", - "strip-literal": "^1.3.0", + "strip-literal": "^2.0.0", "tinybench": "^2.5.1", "tinypool": "^0.8.2", "vite": "^5.0.0", - "vite-node": "1.2.2", + "vite-node": "1.3.1", "why-is-node-running": "^2.2.2" }, "bin": { @@ -8162,8 +8810,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "^1.0.0", - "@vitest/ui": "^1.0.0", + "@vitest/browser": "1.3.1", + "@vitest/ui": "1.3.1", "happy-dom": "*", "jsdom": "*" }, diff --git a/web/package.json b/web/package.json index eaafeef25e..b82b3bfc4e 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "immich-web", - "version": "1.0.0", + "version": "1.97.0", "license": "GNU Affero General Public License version 3", "scripts": { "dev": "vite dev --host 0.0.0.0 --port 3000", @@ -22,57 +22,56 @@ "prepare": "svelte-kit sync" }, "devDependencies": { - "@faker-js/faker": "^8.0.0", - "@floating-ui/dom": "^1.5.1", + "@faker-js/faker": "^8.4.1", + "@socket.io/component-emitter": "^3.1.0", "@sveltejs/adapter-static": "^3.0.1", - "@sveltejs/kit": "^2.0.6", - "@sveltejs/vite-plugin-svelte": "^3.0.0", - "@testing-library/jest-dom": "^6.1.5", - "@testing-library/svelte": "^4.0.3", - "@types/dom-to-image": "^2.6.4", - "@types/justified-layout": "^4.1.0", - "@types/lodash-es": "^4.17.6", - "@types/luxon": "^3.2.0", - "@typescript-eslint/eslint-plugin": "^6.4.1", - "@typescript-eslint/parser": "^6.4.1", - "@vitest/coverage-v8": "^1.0.4", - "autoprefixer": "^10.4.13", - "eslint": "^8.34.0", + "@sveltejs/enhanced-img": "^0.1.8", + "@sveltejs/kit": "^2.5.2", + "@sveltejs/vite-plugin-svelte": "^3.0.2", + "@testing-library/jest-dom": "^6.4.2", + "@testing-library/svelte": "^4.1.0", + "@types/dom-to-image": "^2.6.7", + "@types/justified-layout": "^4.1.4", + "@types/lodash-es": "^4.17.12", + "@types/luxon": "^3.4.2", + "@typescript-eslint/eslint-plugin": "^7.1.0", + "@typescript-eslint/parser": "^7.1.0", + "@vitest/coverage-v8": "^1.3.1", + "autoprefixer": "^10.4.17", + "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-svelte": "^2.30.0", - "eslint-plugin-unicorn": "^51.0.0", - "factory.ts": "^1.3.0", - "identity-obj-proxy": "^3.0.0", - "postcss": "^8.4.21", - "prettier": "^3.1.0", - "prettier-plugin-svelte": "^3.1.2", + "eslint-plugin-svelte": "^2.35.1", + "eslint-plugin-unicorn": "^51.0.1", + "factory.ts": "^1.4.1", + "postcss": "^8.4.35", + "prettier": "^3.2.5", + "prettier-plugin-organize-imports": "^3.2.4", + "prettier-plugin-svelte": "^3.2.1", "rollup-plugin-visualizer": "^5.12.0", - "svelte": "^4.0.5", - "svelte-check": "^3.4.3", - "svelte-preprocess": "^5.0.3", - "tailwindcss": "^3.2.7", - "tslib": "^2.5.0", + "svelte": "^4.2.12", + "svelte-check": "^3.6.5", + "tailwindcss": "^3.4.1", + "tslib": "^2.6.2", "typescript": "^5.3.3", - "vite": "^5.1.1", - "vitest": "^1.0.4" + "vite": "^5.1.4", + "vitest": "^1.3.1" }, "type": "module", "dependencies": { "@immich/sdk": "file:../open-api/typescript-sdk", - "@mdi/js": "^7.3.67", - "@photo-sphere-viewer/core": "^5.7.0", - "@zoom-image/svelte": "^0.2.0", - "axios": "^1.6.7", + "@mdi/js": "^7.4.47", + "@photo-sphere-viewer/core": "^5.7.1", + "@zoom-image/svelte": "^0.2.6", "buffer": "^6.0.3", "copy-image-clipboard": "^2.1.2", "dom-to-image": "^2.6.0", - "handlebars": "^4.7.7", + "handlebars": "^4.7.8", "justified-layout": "^4.1.0", "lodash-es": "^4.17.21", - "luxon": "^3.2.1", - "socket.io-client": "^4.6.1", - "svelte-local-storage-store": "^0.6.0", - "svelte-maplibre": "^0.7.0", + "luxon": "^3.4.4", + "socket.io-client": "^4.7.4", + "svelte-local-storage-store": "^0.6.4", + "svelte-maplibre": "^0.8.1", "thumbhash": "^0.1.1" } } diff --git a/web/src/api/api.ts b/web/src/api/api.ts deleted file mode 100644 index c11ac66534..0000000000 --- a/web/src/api/api.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { - AssetApi, - AssetApiFp, - AssetJobName, - DownloadApi, - JobName, - PersonApi, - SearchApi, - SharedLinkApi, - UserApiFp, - base, - common, - configuration, -} from '@immich/sdk/axios'; -import type { ApiParams as ApiParameters } from './types'; - -class ImmichApi { - public downloadApi: DownloadApi; - public assetApi: AssetApi; - public searchApi: SearchApi; - public sharedLinkApi: SharedLinkApi; - public personApi: PersonApi; - - private config: configuration.Configuration; - private key?: string; - - get isSharedLink() { - return !!this.key; - } - - constructor(parameters: configuration.ConfigurationParameters) { - this.config = new configuration.Configuration(parameters); - - this.downloadApi = new DownloadApi(this.config); - this.assetApi = new AssetApi(this.config); - this.searchApi = new SearchApi(this.config); - this.sharedLinkApi = new SharedLinkApi(this.config); - this.personApi = new PersonApi(this.config); - } - - private createUrl(path: string, parameters?: Record) { - const searchParameters = new URLSearchParams(); - for (const key in parameters) { - const value = parameters[key]; - if (value !== undefined && value !== null) { - searchParameters.set(key, value.toString()); - } - } - - const url = new URL(path, common.DUMMY_BASE_URL); - url.search = searchParameters.toString(); - - return (this.config.basePath || base.BASE_PATH) + common.toPathString(url); - } - - public setKey(key: string) { - this.key = key; - } - - public getKey(): string | undefined { - return this.key; - } - - public setAccessToken(accessToken: string) { - this.config.accessToken = accessToken; - } - - public removeAccessToken() { - this.config.accessToken = undefined; - } - - public setBaseUrl(baseUrl: string) { - this.config.basePath = baseUrl; - } - - public getAssetFileUrl(...[assetId, isThumb, isWeb]: ApiParameters) { - const path = `/asset/file/${assetId}`; - return this.createUrl(path, { isThumb, isWeb, key: this.getKey() }); - } - - public getAssetThumbnailUrl(...[assetId, format]: ApiParameters) { - const path = `/asset/thumbnail/${assetId}`; - return this.createUrl(path, { format, key: this.getKey() }); - } - - public getProfileImageUrl(...[userId]: ApiParameters) { - const path = `/user/profile-image/${userId}`; - return this.createUrl(path); - } - - public getPeopleThumbnailUrl(personId: string) { - const path = `/person/${personId}/thumbnail`; - return this.createUrl(path); - } - - public getJobName(jobName: JobName) { - const names: Record = { - [JobName.ThumbnailGeneration]: 'Generate Thumbnails', - [JobName.MetadataExtraction]: 'Extract Metadata', - [JobName.Sidecar]: 'Sidecar Metadata', - [JobName.SmartSearch]: 'Smart Search', - [JobName.FaceDetection]: 'Face Detection', - [JobName.FacialRecognition]: 'Facial Recognition', - [JobName.VideoConversion]: 'Transcode Videos', - [JobName.StorageTemplateMigration]: 'Storage Template Migration', - [JobName.Migration]: 'Migration', - [JobName.BackgroundTask]: 'Background Tasks', - [JobName.Search]: 'Search', - [JobName.Library]: 'Library', - }; - - return names[jobName]; - } - - public getAssetJobName(job: AssetJobName) { - const names: Record = { - [AssetJobName.RefreshMetadata]: 'Refresh metadata', - [AssetJobName.RegenerateThumbnail]: 'Refresh thumbnails', - [AssetJobName.TranscodeVideo]: 'Refresh encoded videos', - }; - - return names[job]; - } - - public getAssetJobMessage(job: AssetJobName) { - const messages: Record = { - [AssetJobName.RefreshMetadata]: 'Refreshing metadata', - [AssetJobName.RegenerateThumbnail]: `Regenerating thumbnails`, - [AssetJobName.TranscodeVideo]: `Refreshing encoded video`, - }; - - return messages[job]; - } -} - -export const api = new ImmichApi({ basePath: '/api' }); diff --git a/web/src/api/index.ts b/web/src/api/index.ts deleted file mode 100644 index 886f439d8e..0000000000 --- a/web/src/api/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './api'; -export * from '@immich/sdk/axios'; -export * from './utils'; diff --git a/web/src/api/types.ts b/web/src/api/types.ts deleted file mode 100644 index 7a2ae17f67..0000000000 --- a/web/src/api/types.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { Configuration } from '@immich/sdk/axios'; - -/* eslint-disable @typescript-eslint/no-explicit-any */ -export type ApiFp = (configuration: Configuration) => Record any>; - -export type OmitLast = T extends readonly [...infer U, any?] ? U : [...T]; - -export type ApiParams> = OmitLast[K]>>; diff --git a/web/src/api/utils.ts b/web/src/api/utils.ts deleted file mode 100644 index 0367e7905a..0000000000 --- a/web/src/api/utils.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { finishOAuth, linkOAuthAccount, startOAuth, unlinkOAuthAccount } from '@immich/sdk'; -import type { UserResponseDto } from '@immich/sdk/axios'; -import type { AxiosError } from 'axios'; -import { - NotificationType, - notificationController, -} from '../lib/components/shared-components/notification/notification'; -import { handleError } from '../lib/utils/handle-error'; - -export type ApiError = AxiosError<{ message: string }>; - -export const copyToClipboard = async (secret: string) => { - try { - await navigator.clipboard.writeText(secret); - notificationController.show({ message: 'Copied to clipboard!', type: NotificationType.Info }); - } catch (error) { - handleError(error, 'Cannot copy to clipboard, make sure you are accessing the page through https'); - } -}; - -export const makeSharedLinkUrl = (externalDomain: string, key: string) => { - let url = externalDomain || window.location.origin; - if (!url.endsWith('/')) { - url += '/'; - } - return `${url}share/${key}`; -}; - -export const oauth = { - isCallback: (location: Location) => { - const search = location.search; - return search.includes('code=') || search.includes('error='); - }, - isAutoLaunchDisabled: (location: Location) => { - const values = ['autoLaunch=0', 'password=1', 'password=true']; - for (const value of values) { - if (location.search.includes(value)) { - return true; - } - } - return false; - }, - authorize: async (location: Location) => { - try { - const redirectUri = location.href.split('?')[0]; - const { url } = await startOAuth({ oAuthConfigDto: { redirectUri } }); - window.location.href = url; - return true; - } catch (error) { - handleError(error, 'Unable to login with OAuth'); - return false; - } - }, - login: (location: Location) => { - return finishOAuth({ oAuthCallbackDto: { url: location.href } }); - }, - link: (location: Location): Promise => { - return linkOAuthAccount({ oAuthCallbackDto: { url: location.href } }); - }, - unlink: () => { - return unlinkOAuthAccount(); - }, -}; diff --git a/web/src/hooks.client.ts b/web/src/hooks.client.ts index 1e29371fa9..f30e3ee8ca 100644 --- a/web/src/hooks.client.ts +++ b/web/src/hooks.client.ts @@ -1,40 +1,27 @@ +import { isHttpError } from '@immich/sdk'; import type { HandleClientError } from '@sveltejs/kit'; -import type { AxiosError, AxiosResponse } from 'axios'; -const LOG_PREFIX = '[hooks.client.ts]'; const DEFAULT_MESSAGE = 'Hmm, not sure about that. Check the logs or open a ticket?'; const parseError = (error: unknown) => { - const httpError = error as AxiosError; - const request = httpError?.request as Request & { path: string }; - const response = httpError?.response as AxiosResponse<{ - message: string; - statusCode: number; - error: string; - }>; + const httpError = isHttpError(error) ? error : undefined; + const statusCode = httpError?.status || httpError?.data?.statusCode || 500; + const message = httpError?.data?.message || (httpError?.data && String(httpError.data)) || httpError?.message; - let code = response?.data?.statusCode || response?.status || httpError.code || '500'; - if (response) { - code += ` - ${response.data?.error || response.statusText}`; - } - - if (request && response) { - console.log({ - status: response.status, - url: `${request.method} ${request.path}`, - response: response.data || 'No data', - }); - } + console.log({ + status: statusCode, + response: httpError?.data || 'No data', + }); return { - message: response?.data?.message || httpError?.message || DEFAULT_MESSAGE, - code, + message: message || DEFAULT_MESSAGE, + code: statusCode, stack: httpError?.stack, }; }; export const handleError: HandleClientError = ({ error }) => { const result = parseError(error); - console.error(`${LOG_PREFIX}:handleError ${result.message}`); + console.error(`[hooks.client.ts]:handleError ${result.message}`); return result; }; diff --git a/web/src/lib/components/admin-page/jobs/job-tile.svelte b/web/src/lib/components/admin-page/jobs/job-tile.svelte index 75d8ab6b81..9e47386692 100644 --- a/web/src/lib/components/admin-page/jobs/job-tile.svelte +++ b/web/src/lib/components/admin-page/jobs/job-tile.svelte @@ -1,12 +1,9 @@ Apply the current -Storage template to previously uploaded assets diff --git a/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte b/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte index 2011aba676..ac3512c6c2 100644 --- a/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte +++ b/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte @@ -1,10 +1,10 @@ @@ -41,7 +45,7 @@ - import type { SystemConfigDto } from '@api'; + import type { SystemConfigDto } from '@immich/sdk'; import { isEqual } from 'lodash-es'; import { createEventDispatcher } from 'svelte'; import { fade } from 'svelte/transition'; import type { SettingsEventType } from '../admin-settings'; - import SettingAccordion from '../setting-accordion.svelte'; - import SettingButtonsRow from '../setting-buttons-row.svelte'; - import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte'; - import SettingSwitch from '../setting-switch.svelte'; + import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte'; + import SettingInputField, { + SettingInputFieldType, + } from '$lib/components/shared-components/settings/setting-input-field.svelte'; + import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte'; + import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte'; export let savedConfig: SystemConfigDto; export let defaultConfig: SystemConfigDto; @@ -40,28 +42,6 @@ subtitle="Watch external libraries for file changes" bind:checked={config.library.watch.enabled} /> - - - - - -

- Interval of filesystem polling, in milliseconds. Lower values will result in higher CPU usage. -

-
-
diff --git a/web/src/lib/components/admin-page/settings/logging-settings/logging-settings.svelte b/web/src/lib/components/admin-page/settings/logging-settings/logging-settings.svelte index 33b8b272bc..97ae006816 100644 --- a/web/src/lib/components/admin-page/settings/logging-settings/logging-settings.svelte +++ b/web/src/lib/components/admin-page/settings/logging-settings/logging-settings.svelte @@ -1,12 +1,12 @@ - - - -
-
- - {#if required} -
*
- {/if} - - {#if isEdited} -
- Unsaved change -
- {/if} -
- - {#if desc} -

- {desc} -

- {:else} - - {/if} - - -
diff --git a/web/src/lib/components/admin-page/settings/setting-switch.svelte b/web/src/lib/components/admin-page/settings/setting-switch.svelte deleted file mode 100644 index 6797423a55..0000000000 --- a/web/src/lib/components/admin-page/settings/setting-switch.svelte +++ /dev/null @@ -1,97 +0,0 @@ - - -
-
-
- - {#if isEdited} -
- Unsaved change -
- {/if} -
- -

{subtitle}

-
- - -
- - diff --git a/web/src/lib/components/admin-page/settings/storage-template/storage-template-settings.svelte b/web/src/lib/components/admin-page/settings/storage-template/storage-template-settings.svelte index 475c3a65cb..11e07d0029 100644 --- a/web/src/lib/components/admin-page/settings/storage-template/storage-template-settings.svelte +++ b/web/src/lib/components/admin-page/settings/storage-template/storage-template-settings.svelte @@ -13,11 +13,13 @@ import { createEventDispatcher } from 'svelte'; import { fade } from 'svelte/transition'; import type { SettingsEventType } from '../admin-settings'; - import SettingButtonsRow from '../setting-buttons-row.svelte'; - import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte'; - import SettingSwitch from '../setting-switch.svelte'; import SupportedDatetimePanel from './supported-datetime-panel.svelte'; import SupportedVariablesPanel from './supported-variables-panel.svelte'; + import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte'; + import SettingInputField, { + SettingInputFieldType, + } from '$lib/components/shared-components/settings/setting-input-field.svelte'; + import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte'; export let savedConfig: SystemConfigDto; export let defaultConfig: SystemConfigDto; @@ -82,7 +84,26 @@ }; -
+
+
+

+ For more details about this feature, refer to the Storage Template + + and its + implications + +

+
{#await getTemplateOptions() then}
- import type { SystemConfigTemplateStorageOptionDto } from '@api'; - import * as luxon from 'luxon'; + import { locale } from '$lib/stores/preferences.store'; + import type { SystemConfigTemplateStorageOptionDto } from '@immich/sdk'; + import { DateTime } from 'luxon'; export let options: SystemConfigTemplateStorageOptionDto; const getLuxonExample = (format: string) => { - return luxon.DateTime.fromISO(new Date('2022-09-04T20:03:05.250').toISOString()).toFormat(format); + return DateTime.fromISO('2022-09-04T20:03:05.250Z', { locale: $locale }).toFormat(format); }; diff --git a/web/src/lib/components/admin-page/settings/theme/theme-settings.svelte b/web/src/lib/components/admin-page/settings/theme/theme-settings.svelte index 5cf88cd718..10c52c1361 100644 --- a/web/src/lib/components/admin-page/settings/theme/theme-settings.svelte +++ b/web/src/lib/components/admin-page/settings/theme/theme-settings.svelte @@ -1,11 +1,11 @@ + +{#if isOwned} +