mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
Merge branch 'main' into feat/search-filter-album/web
This commit is contained in:
@@ -131,7 +131,7 @@ jobs:
|
|||||||
tag-suffix: '-rocm'
|
tag-suffix: '-rocm'
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
runner-mapping: '{"linux/amd64": "mich"}'
|
runner-mapping: '{"linux/amd64": "mich"}'
|
||||||
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@094bfb927b8cd75b343abaac27b3241be0fccfe9 # multi-runner-build-workflow-0.1.0
|
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@129aeda75a450666ce96e8bc8126652e717917a7 # multi-runner-build-workflow-0.1.1
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
actions: read
|
actions: read
|
||||||
@@ -154,7 +154,7 @@ jobs:
|
|||||||
name: Build and Push Server
|
name: Build and Push Server
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
||||||
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@094bfb927b8cd75b343abaac27b3241be0fccfe9 # multi-runner-build-workflow-0.1.0
|
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@129aeda75a450666ce96e8bc8126652e717917a7 # multi-runner-build-workflow-0.1.1
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
actions: read
|
actions: read
|
||||||
|
|||||||
Generated
+59
-59
@@ -1365,17 +1365,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz",
|
||||||
"integrity": "sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==",
|
"integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/regexpp": "^4.10.0",
|
"@eslint-community/regexpp": "^4.10.0",
|
||||||
"@typescript-eslint/scope-manager": "8.35.1",
|
"@typescript-eslint/scope-manager": "8.36.0",
|
||||||
"@typescript-eslint/type-utils": "8.35.1",
|
"@typescript-eslint/type-utils": "8.36.0",
|
||||||
"@typescript-eslint/utils": "8.35.1",
|
"@typescript-eslint/utils": "8.36.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.35.1",
|
"@typescript-eslint/visitor-keys": "8.36.0",
|
||||||
"graphemer": "^1.4.0",
|
"graphemer": "^1.4.0",
|
||||||
"ignore": "^7.0.0",
|
"ignore": "^7.0.0",
|
||||||
"natural-compare": "^1.4.0",
|
"natural-compare": "^1.4.0",
|
||||||
@@ -1389,7 +1389,7 @@
|
|||||||
"url": "https://opencollective.com/typescript-eslint"
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@typescript-eslint/parser": "^8.35.1",
|
"@typescript-eslint/parser": "^8.36.0",
|
||||||
"eslint": "^8.57.0 || ^9.0.0",
|
"eslint": "^8.57.0 || ^9.0.0",
|
||||||
"typescript": ">=4.8.4 <5.9.0"
|
"typescript": ">=4.8.4 <5.9.0"
|
||||||
}
|
}
|
||||||
@@ -1405,16 +1405,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz",
|
||||||
"integrity": "sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==",
|
"integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.35.1",
|
"@typescript-eslint/scope-manager": "8.36.0",
|
||||||
"@typescript-eslint/types": "8.35.1",
|
"@typescript-eslint/types": "8.36.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.35.1",
|
"@typescript-eslint/typescript-estree": "8.36.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.35.1",
|
"@typescript-eslint/visitor-keys": "8.36.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1430,14 +1430,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/project-service": {
|
"node_modules/@typescript-eslint/project-service": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz",
|
||||||
"integrity": "sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==",
|
"integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/tsconfig-utils": "^8.35.1",
|
"@typescript-eslint/tsconfig-utils": "^8.36.0",
|
||||||
"@typescript-eslint/types": "^8.35.1",
|
"@typescript-eslint/types": "^8.36.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1452,14 +1452,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/scope-manager": {
|
"node_modules/@typescript-eslint/scope-manager": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz",
|
||||||
"integrity": "sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==",
|
"integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.35.1",
|
"@typescript-eslint/types": "8.36.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.35.1"
|
"@typescript-eslint/visitor-keys": "8.36.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -1470,9 +1470,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz",
|
||||||
"integrity": "sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==",
|
"integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1487,14 +1487,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/type-utils": {
|
"node_modules/@typescript-eslint/type-utils": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz",
|
||||||
"integrity": "sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==",
|
"integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/typescript-estree": "8.35.1",
|
"@typescript-eslint/typescript-estree": "8.36.0",
|
||||||
"@typescript-eslint/utils": "8.35.1",
|
"@typescript-eslint/utils": "8.36.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"ts-api-utils": "^2.1.0"
|
"ts-api-utils": "^2.1.0"
|
||||||
},
|
},
|
||||||
@@ -1511,9 +1511,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/types": {
|
"node_modules/@typescript-eslint/types": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz",
|
||||||
"integrity": "sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==",
|
"integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1525,16 +1525,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree": {
|
"node_modules/@typescript-eslint/typescript-estree": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz",
|
||||||
"integrity": "sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==",
|
"integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/project-service": "8.35.1",
|
"@typescript-eslint/project-service": "8.36.0",
|
||||||
"@typescript-eslint/tsconfig-utils": "8.35.1",
|
"@typescript-eslint/tsconfig-utils": "8.36.0",
|
||||||
"@typescript-eslint/types": "8.35.1",
|
"@typescript-eslint/types": "8.36.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.35.1",
|
"@typescript-eslint/visitor-keys": "8.36.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"fast-glob": "^3.3.2",
|
"fast-glob": "^3.3.2",
|
||||||
"is-glob": "^4.0.3",
|
"is-glob": "^4.0.3",
|
||||||
@@ -1580,16 +1580,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/utils": {
|
"node_modules/@typescript-eslint/utils": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz",
|
||||||
"integrity": "sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==",
|
"integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.7.0",
|
"@eslint-community/eslint-utils": "^4.7.0",
|
||||||
"@typescript-eslint/scope-manager": "8.35.1",
|
"@typescript-eslint/scope-manager": "8.36.0",
|
||||||
"@typescript-eslint/types": "8.35.1",
|
"@typescript-eslint/types": "8.36.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.35.1"
|
"@typescript-eslint/typescript-estree": "8.36.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -1604,13 +1604,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/visitor-keys": {
|
"node_modules/@typescript-eslint/visitor-keys": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz",
|
||||||
"integrity": "sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==",
|
"integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.35.1",
|
"@typescript-eslint/types": "8.36.0",
|
||||||
"eslint-visitor-keys": "^4.2.1"
|
"eslint-visitor-keys": "^4.2.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -4125,15 +4125,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript-eslint": {
|
"node_modules/typescript-eslint": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.36.0.tgz",
|
||||||
"integrity": "sha512-xslJjFzhOmHYQzSB/QTeASAHbjmxOGEP6Coh93TXmUBFQoJ1VU35UHIDmG06Jd6taf3wqqC1ntBnCMeymy5Ovw==",
|
"integrity": "sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "8.35.1",
|
"@typescript-eslint/eslint-plugin": "8.36.0",
|
||||||
"@typescript-eslint/parser": "8.35.1",
|
"@typescript-eslint/parser": "8.36.0",
|
||||||
"@typescript-eslint/utils": "8.35.1"
|
"@typescript-eslint/utils": "8.36.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:8-bookworm@sha256:fec42f399876eb6faf9e008570597741c87ff7662a54185593e74b09ce83d177
|
image: docker.io/valkey/valkey:8-bookworm@sha256:facc1d2c3462975c34e10fccb167bfa92b0e0dbd992fc282c29a61c3243afb11
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:8-bookworm@sha256:fec42f399876eb6faf9e008570597741c87ff7662a54185593e74b09ce83d177
|
image: docker.io/valkey/valkey:8-bookworm@sha256:facc1d2c3462975c34e10fccb167bfa92b0e0dbd992fc282c29a61c3243afb11
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
@@ -83,7 +83,7 @@ services:
|
|||||||
container_name: immich_prometheus
|
container_name: immich_prometheus
|
||||||
ports:
|
ports:
|
||||||
- 9090:9090
|
- 9090:9090
|
||||||
image: prom/prometheus@sha256:7a34573f0b9c952286b33d537f233cd5b708e12263733aa646e50c33f598f16c
|
image: prom/prometheus@sha256:63805ebb8d2b3920190daf1cb14a60871b16fd38bed42b857a3182bc621f4996
|
||||||
volumes:
|
volumes:
|
||||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||||
- prometheus-data:/prometheus
|
- prometheus-data:/prometheus
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:8-bookworm@sha256:fec42f399876eb6faf9e008570597741c87ff7662a54185593e74b09ce83d177
|
image: docker.io/valkey/valkey:8-bookworm@sha256:facc1d2c3462975c34e10fccb167bfa92b0e0dbd992fc282c29a61c3243afb11
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
|
|||||||
Generated
+70
-70
@@ -83,7 +83,7 @@
|
|||||||
"prettier-plugin-organize-imports": "^4.0.0",
|
"prettier-plugin-organize-imports": "^4.0.0",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"typescript-eslint": "^8.28.0",
|
"typescript-eslint": "^8.28.0",
|
||||||
"vite": "^6.0.0",
|
"vite": "^7.0.0",
|
||||||
"vite-tsconfig-paths": "^5.0.0",
|
"vite-tsconfig-paths": "^5.0.0",
|
||||||
"vitest": "^3.0.0",
|
"vitest": "^3.0.0",
|
||||||
"vitest-fetch-mock": "^0.4.0",
|
"vitest-fetch-mock": "^0.4.0",
|
||||||
@@ -2101,17 +2101,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz",
|
||||||
"integrity": "sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==",
|
"integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/regexpp": "^4.10.0",
|
"@eslint-community/regexpp": "^4.10.0",
|
||||||
"@typescript-eslint/scope-manager": "8.35.1",
|
"@typescript-eslint/scope-manager": "8.36.0",
|
||||||
"@typescript-eslint/type-utils": "8.35.1",
|
"@typescript-eslint/type-utils": "8.36.0",
|
||||||
"@typescript-eslint/utils": "8.35.1",
|
"@typescript-eslint/utils": "8.36.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.35.1",
|
"@typescript-eslint/visitor-keys": "8.36.0",
|
||||||
"graphemer": "^1.4.0",
|
"graphemer": "^1.4.0",
|
||||||
"ignore": "^7.0.0",
|
"ignore": "^7.0.0",
|
||||||
"natural-compare": "^1.4.0",
|
"natural-compare": "^1.4.0",
|
||||||
@@ -2125,7 +2125,7 @@
|
|||||||
"url": "https://opencollective.com/typescript-eslint"
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@typescript-eslint/parser": "^8.35.1",
|
"@typescript-eslint/parser": "^8.36.0",
|
||||||
"eslint": "^8.57.0 || ^9.0.0",
|
"eslint": "^8.57.0 || ^9.0.0",
|
||||||
"typescript": ">=4.8.4 <5.9.0"
|
"typescript": ">=4.8.4 <5.9.0"
|
||||||
}
|
}
|
||||||
@@ -2141,16 +2141,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz",
|
||||||
"integrity": "sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==",
|
"integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.35.1",
|
"@typescript-eslint/scope-manager": "8.36.0",
|
||||||
"@typescript-eslint/types": "8.35.1",
|
"@typescript-eslint/types": "8.36.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.35.1",
|
"@typescript-eslint/typescript-estree": "8.36.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.35.1",
|
"@typescript-eslint/visitor-keys": "8.36.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -2166,14 +2166,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/project-service": {
|
"node_modules/@typescript-eslint/project-service": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz",
|
||||||
"integrity": "sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==",
|
"integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/tsconfig-utils": "^8.35.1",
|
"@typescript-eslint/tsconfig-utils": "^8.36.0",
|
||||||
"@typescript-eslint/types": "^8.35.1",
|
"@typescript-eslint/types": "^8.36.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -2188,14 +2188,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/scope-manager": {
|
"node_modules/@typescript-eslint/scope-manager": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz",
|
||||||
"integrity": "sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==",
|
"integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.35.1",
|
"@typescript-eslint/types": "8.36.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.35.1"
|
"@typescript-eslint/visitor-keys": "8.36.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -2206,9 +2206,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz",
|
||||||
"integrity": "sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==",
|
"integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -2223,14 +2223,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/type-utils": {
|
"node_modules/@typescript-eslint/type-utils": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz",
|
||||||
"integrity": "sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==",
|
"integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/typescript-estree": "8.35.1",
|
"@typescript-eslint/typescript-estree": "8.36.0",
|
||||||
"@typescript-eslint/utils": "8.35.1",
|
"@typescript-eslint/utils": "8.36.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"ts-api-utils": "^2.1.0"
|
"ts-api-utils": "^2.1.0"
|
||||||
},
|
},
|
||||||
@@ -2247,9 +2247,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/types": {
|
"node_modules/@typescript-eslint/types": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz",
|
||||||
"integrity": "sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==",
|
"integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -2261,16 +2261,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree": {
|
"node_modules/@typescript-eslint/typescript-estree": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz",
|
||||||
"integrity": "sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==",
|
"integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/project-service": "8.35.1",
|
"@typescript-eslint/project-service": "8.36.0",
|
||||||
"@typescript-eslint/tsconfig-utils": "8.35.1",
|
"@typescript-eslint/tsconfig-utils": "8.36.0",
|
||||||
"@typescript-eslint/types": "8.35.1",
|
"@typescript-eslint/types": "8.36.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.35.1",
|
"@typescript-eslint/visitor-keys": "8.36.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"fast-glob": "^3.3.2",
|
"fast-glob": "^3.3.2",
|
||||||
"is-glob": "^4.0.3",
|
"is-glob": "^4.0.3",
|
||||||
@@ -2316,16 +2316,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/utils": {
|
"node_modules/@typescript-eslint/utils": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz",
|
||||||
"integrity": "sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==",
|
"integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.7.0",
|
"@eslint-community/eslint-utils": "^4.7.0",
|
||||||
"@typescript-eslint/scope-manager": "8.35.1",
|
"@typescript-eslint/scope-manager": "8.36.0",
|
||||||
"@typescript-eslint/types": "8.35.1",
|
"@typescript-eslint/types": "8.36.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.35.1"
|
"@typescript-eslint/typescript-estree": "8.36.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -2340,13 +2340,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/visitor-keys": {
|
"node_modules/@typescript-eslint/visitor-keys": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz",
|
||||||
"integrity": "sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==",
|
"integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.35.1",
|
"@typescript-eslint/types": "8.36.0",
|
||||||
"eslint-visitor-keys": "^4.2.1"
|
"eslint-visitor-keys": "^4.2.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -4812,9 +4812,9 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/luxon": {
|
"node_modules/luxon": {
|
||||||
"version": "3.6.1",
|
"version": "3.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.1.tgz",
|
||||||
"integrity": "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==",
|
"integrity": "sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -6430,9 +6430,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/superagent": {
|
"node_modules/superagent": {
|
||||||
"version": "10.2.1",
|
"version": "10.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.2.tgz",
|
||||||
"integrity": "sha512-O+PCv11lgTNJUzy49teNAWLjBZfc+A1enOwTpLlH6/rsvKcTwcdTT8m9azGkVqM7HBl5jpyZ7KTPhHweokBcdg==",
|
"integrity": "sha512-vWMq11OwWCC84pQaFPzF/VO3BrjkCeewuvJgt1jfV0499Z1QSAWN4EqfMM5WlFDDX9/oP8JjlDKpblrmEoyu4Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -6451,14 +6451,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/supertest": {
|
"node_modules/supertest": {
|
||||||
"version": "7.1.1",
|
"version": "7.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.3.tgz",
|
||||||
"integrity": "sha512-aI59HBTlG9e2wTjxGJV+DygfNLgnWbGdZxiA/sgrnNNikIW8lbDvCtF6RnhZoJ82nU7qv7ZLjrvWqCEm52fAmw==",
|
"integrity": "sha512-ORY0gPa6ojmg/C74P/bDoS21WL6FMXq5I8mawkEz30/zkwdu0gOeqstFy316vHG6OKxqQ+IbGneRemHI8WraEw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"methods": "^1.1.2",
|
"methods": "^1.1.2",
|
||||||
"superagent": "^10.2.1"
|
"superagent": "^10.2.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.18.0"
|
"node": ">=14.18.0"
|
||||||
@@ -6778,15 +6778,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript-eslint": {
|
"node_modules/typescript-eslint": {
|
||||||
"version": "8.35.1",
|
"version": "8.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.36.0.tgz",
|
||||||
"integrity": "sha512-xslJjFzhOmHYQzSB/QTeASAHbjmxOGEP6Coh93TXmUBFQoJ1VU35UHIDmG06Jd6taf3wqqC1ntBnCMeymy5Ovw==",
|
"integrity": "sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "8.35.1",
|
"@typescript-eslint/eslint-plugin": "8.36.0",
|
||||||
"@typescript-eslint/parser": "8.35.1",
|
"@typescript-eslint/parser": "8.36.0",
|
||||||
"@typescript-eslint/utils": "8.35.1"
|
"@typescript-eslint/utils": "8.36.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
|||||||
@@ -4,9 +4,12 @@ import sys
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
port = os.getenv("IMMICH_PORT", 3003)
|
port = os.getenv("IMMICH_PORT", 3003)
|
||||||
|
host = os.getenv("IMMICH_HOST", "0.0.0.0")
|
||||||
|
|
||||||
|
host = "localhost" if host == "0.0.0.0" else host
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = requests.get(f"http://localhost:{port}/ping", timeout=2)
|
response = requests.get(f"http://{host}:{port}/ping", timeout=2)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -3,7 +3,7 @@
|
|||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 77;
|
objectVersion = 54;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
@@ -117,6 +117,8 @@
|
|||||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||||
B2CF7F8C2DDE4EBB00744BF6 /* Sync */ = {
|
B2CF7F8C2DDE4EBB00744BF6 /* Sync */ = {
|
||||||
isa = PBXFileSystemSynchronizedRootGroup;
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
exceptions = (
|
||||||
|
);
|
||||||
path = Sync;
|
path = Sync;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
@@ -471,14 +473,10 @@
|
|||||||
inputFileListPaths = (
|
inputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
);
|
);
|
||||||
inputPaths = (
|
|
||||||
);
|
|
||||||
name = "[CP] Copy Pods Resources";
|
name = "[CP] Copy Pods Resources";
|
||||||
outputFileListPaths = (
|
outputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
);
|
);
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||||
@@ -507,14 +505,10 @@
|
|||||||
inputFileListPaths = (
|
inputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
);
|
);
|
||||||
inputPaths = (
|
|
||||||
);
|
|
||||||
name = "[CP] Embed Pods Frameworks";
|
name = "[CP] Embed Pods Frameworks";
|
||||||
outputFileListPaths = (
|
outputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
);
|
);
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<MacroExpansion>
|
<MacroExpansion>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
@@ -43,6 +44,7 @@
|
|||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||||
disableMainThreadChecker = "YES"
|
disableMainThreadChecker = "YES"
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ sealed class BaseAsset {
|
|||||||
bool get isImage => type == AssetType.image;
|
bool get isImage => type == AssetType.image;
|
||||||
bool get isVideo => type == AssetType.video;
|
bool get isVideo => type == AssetType.video;
|
||||||
|
|
||||||
|
bool get isMotionPhoto => livePhotoVideoId != null;
|
||||||
|
|
||||||
Duration get duration {
|
Duration get duration {
|
||||||
final durationInSeconds = this.durationInSeconds;
|
final durationInSeconds = this.durationInSeconds;
|
||||||
if (durationInSeconds != null) {
|
if (durationInSeconds != null) {
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ class RemoteAsset extends BaseAsset {
|
|||||||
bool? isFavorite,
|
bool? isFavorite,
|
||||||
String? thumbHash,
|
String? thumbHash,
|
||||||
AssetVisibility? visibility,
|
AssetVisibility? visibility,
|
||||||
|
String? livePhotoVideoId,
|
||||||
}) {
|
}) {
|
||||||
return RemoteAsset(
|
return RemoteAsset(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
@@ -110,6 +111,7 @@ class RemoteAsset extends BaseAsset {
|
|||||||
isFavorite: isFavorite ?? this.isFavorite,
|
isFavorite: isFavorite ?? this.isFavorite,
|
||||||
thumbHash: thumbHash ?? this.thumbHash,
|
thumbHash: thumbHash ?? this.thumbHash,
|
||||||
visibility: visibility ?? this.visibility,
|
visibility: visibility ?? this.visibility,
|
||||||
|
livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
|
enum UserMetadataKey {
|
||||||
|
// do not change this order!
|
||||||
|
onboarding,
|
||||||
|
preferences,
|
||||||
|
license,
|
||||||
|
}
|
||||||
|
|
||||||
enum AvatarColor {
|
enum AvatarColor {
|
||||||
// do not change this order or reuse indices for other purposes, adding is OK
|
// do not change this order or reuse indices for other purposes, adding is OK
|
||||||
primary("primary"),
|
primary("primary"),
|
||||||
@@ -31,7 +38,45 @@ enum AvatarColor {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class UserPreferences {
|
class Onboarding {
|
||||||
|
final bool isOnboarded;
|
||||||
|
|
||||||
|
const Onboarding({required this.isOnboarded});
|
||||||
|
|
||||||
|
Onboarding copyWith({bool? isOnboarded}) {
|
||||||
|
return Onboarding(isOnboarded: isOnboarded ?? this.isOnboarded);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object?> toMap() {
|
||||||
|
final onboarding = <String, Object?>{};
|
||||||
|
onboarding["isOnboarded"] = isOnboarded;
|
||||||
|
return onboarding;
|
||||||
|
}
|
||||||
|
|
||||||
|
factory Onboarding.fromMap(Map<String, Object?> map) {
|
||||||
|
return Onboarding(isOnboarded: map["isOnboarded"] as bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return '''Onboarding {
|
||||||
|
isOnboarded: $isOnboarded,
|
||||||
|
}''';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(covariant Onboarding other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
|
return isOnboarded == other.isOnboarded;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => isOnboarded.hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: wait to be overwritten
|
||||||
|
class Preferences {
|
||||||
final bool foldersEnabled;
|
final bool foldersEnabled;
|
||||||
final bool memoriesEnabled;
|
final bool memoriesEnabled;
|
||||||
final bool peopleEnabled;
|
final bool peopleEnabled;
|
||||||
@@ -41,7 +86,7 @@ class UserPreferences {
|
|||||||
final AvatarColor userAvatarColor;
|
final AvatarColor userAvatarColor;
|
||||||
final bool showSupportBadge;
|
final bool showSupportBadge;
|
||||||
|
|
||||||
const UserPreferences({
|
const Preferences({
|
||||||
this.foldersEnabled = false,
|
this.foldersEnabled = false,
|
||||||
this.memoriesEnabled = true,
|
this.memoriesEnabled = true,
|
||||||
this.peopleEnabled = true,
|
this.peopleEnabled = true,
|
||||||
@@ -52,7 +97,7 @@ class UserPreferences {
|
|||||||
this.showSupportBadge = true,
|
this.showSupportBadge = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
UserPreferences copyWith({
|
Preferences copyWith({
|
||||||
bool? foldersEnabled,
|
bool? foldersEnabled,
|
||||||
bool? memoriesEnabled,
|
bool? memoriesEnabled,
|
||||||
bool? peopleEnabled,
|
bool? peopleEnabled,
|
||||||
@@ -62,7 +107,7 @@ class UserPreferences {
|
|||||||
AvatarColor? userAvatarColor,
|
AvatarColor? userAvatarColor,
|
||||||
bool? showSupportBadge,
|
bool? showSupportBadge,
|
||||||
}) {
|
}) {
|
||||||
return UserPreferences(
|
return Preferences(
|
||||||
foldersEnabled: foldersEnabled ?? this.foldersEnabled,
|
foldersEnabled: foldersEnabled ?? this.foldersEnabled,
|
||||||
memoriesEnabled: memoriesEnabled ?? this.memoriesEnabled,
|
memoriesEnabled: memoriesEnabled ?? this.memoriesEnabled,
|
||||||
peopleEnabled: peopleEnabled ?? this.peopleEnabled,
|
peopleEnabled: peopleEnabled ?? this.peopleEnabled,
|
||||||
@@ -87,8 +132,8 @@ class UserPreferences {
|
|||||||
return preferences;
|
return preferences;
|
||||||
}
|
}
|
||||||
|
|
||||||
factory UserPreferences.fromMap(Map<String, Object?> map) {
|
factory Preferences.fromMap(Map<String, Object?> map) {
|
||||||
return UserPreferences(
|
return Preferences(
|
||||||
foldersEnabled: map["folders-Enabled"] as bool? ?? false,
|
foldersEnabled: map["folders-Enabled"] as bool? ?? false,
|
||||||
memoriesEnabled: map["memories-Enabled"] as bool? ?? true,
|
memoriesEnabled: map["memories-Enabled"] as bool? ?? true,
|
||||||
peopleEnabled: map["people-Enabled"] as bool? ?? true,
|
peopleEnabled: map["people-Enabled"] as bool? ?? true,
|
||||||
@@ -102,4 +147,173 @@ class UserPreferences {
|
|||||||
showSupportBadge: map["purchase-ShowSupportBadge"] as bool? ?? true,
|
showSupportBadge: map["purchase-ShowSupportBadge"] as bool? ?? true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return '''Preferences: {
|
||||||
|
foldersEnabled: $foldersEnabled,
|
||||||
|
memoriesEnabled: $memoriesEnabled,
|
||||||
|
peopleEnabled: $peopleEnabled,
|
||||||
|
ratingsEnabled: $ratingsEnabled,
|
||||||
|
sharedLinksEnabled: $sharedLinksEnabled,
|
||||||
|
tagsEnabled: $tagsEnabled,
|
||||||
|
userAvatarColor: $userAvatarColor,
|
||||||
|
showSupportBadge: $showSupportBadge,
|
||||||
|
}''';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(covariant Preferences other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
|
return other.foldersEnabled == foldersEnabled &&
|
||||||
|
other.memoriesEnabled == memoriesEnabled &&
|
||||||
|
other.peopleEnabled == peopleEnabled &&
|
||||||
|
other.ratingsEnabled == ratingsEnabled &&
|
||||||
|
other.sharedLinksEnabled == sharedLinksEnabled &&
|
||||||
|
other.tagsEnabled == tagsEnabled &&
|
||||||
|
other.userAvatarColor == userAvatarColor &&
|
||||||
|
other.showSupportBadge == showSupportBadge;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return foldersEnabled.hashCode ^
|
||||||
|
memoriesEnabled.hashCode ^
|
||||||
|
peopleEnabled.hashCode ^
|
||||||
|
ratingsEnabled.hashCode ^
|
||||||
|
sharedLinksEnabled.hashCode ^
|
||||||
|
tagsEnabled.hashCode ^
|
||||||
|
userAvatarColor.hashCode ^
|
||||||
|
showSupportBadge.hashCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class License {
|
||||||
|
final DateTime activatedAt;
|
||||||
|
final String activationKey;
|
||||||
|
final String licenseKey;
|
||||||
|
|
||||||
|
const License({
|
||||||
|
required this.activatedAt,
|
||||||
|
required this.activationKey,
|
||||||
|
required this.licenseKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
License copyWith({
|
||||||
|
DateTime? activatedAt,
|
||||||
|
String? activationKey,
|
||||||
|
String? licenseKey,
|
||||||
|
}) {
|
||||||
|
return License(
|
||||||
|
activatedAt: activatedAt ?? this.activatedAt,
|
||||||
|
activationKey: activationKey ?? this.activationKey,
|
||||||
|
licenseKey: licenseKey ?? this.licenseKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object?> toMap() {
|
||||||
|
final license = <String, Object?>{};
|
||||||
|
license["activatedAt"] = activatedAt;
|
||||||
|
license["activationKey"] = activationKey;
|
||||||
|
license["licenseKey"] = licenseKey;
|
||||||
|
return license;
|
||||||
|
}
|
||||||
|
|
||||||
|
factory License.fromMap(Map<String, Object?> map) {
|
||||||
|
return License(
|
||||||
|
activatedAt: map["activatedAt"] as DateTime,
|
||||||
|
activationKey: map["activationKey"] as String,
|
||||||
|
licenseKey: map["licenseKey"] as String,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return '''License {
|
||||||
|
activatedAt: $activatedAt,
|
||||||
|
activationKey: $activationKey,
|
||||||
|
licenseKey: $licenseKey,
|
||||||
|
}''';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(covariant License other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
|
return activatedAt == other.activatedAt &&
|
||||||
|
activationKey == other.activationKey &&
|
||||||
|
licenseKey == other.licenseKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
activatedAt.hashCode ^ activationKey.hashCode ^ licenseKey.hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Model for a user metadata stored in the server
|
||||||
|
class UserMetadata {
|
||||||
|
final String userId;
|
||||||
|
final UserMetadataKey key;
|
||||||
|
final Onboarding? onboarding;
|
||||||
|
final Preferences? preferences;
|
||||||
|
final License? license;
|
||||||
|
|
||||||
|
const UserMetadata({
|
||||||
|
required this.userId,
|
||||||
|
required this.key,
|
||||||
|
this.onboarding,
|
||||||
|
this.preferences,
|
||||||
|
this.license,
|
||||||
|
}) : assert(
|
||||||
|
onboarding != null || preferences != null || license != null,
|
||||||
|
'One of onboarding, preferences and license must be provided',
|
||||||
|
);
|
||||||
|
|
||||||
|
UserMetadata copyWith({
|
||||||
|
String? userId,
|
||||||
|
UserMetadataKey? key,
|
||||||
|
Onboarding? onboarding,
|
||||||
|
Preferences? preferences,
|
||||||
|
License? license,
|
||||||
|
}) {
|
||||||
|
return UserMetadata(
|
||||||
|
userId: userId ?? this.userId,
|
||||||
|
key: key ?? this.key,
|
||||||
|
onboarding: onboarding ?? this.onboarding,
|
||||||
|
preferences: preferences ?? this.preferences,
|
||||||
|
license: license ?? this.license,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return '''UserMetadata: {
|
||||||
|
userId: $userId,
|
||||||
|
key: $key,
|
||||||
|
onboarding: ${onboarding ?? "<NA>"},
|
||||||
|
preferences: ${preferences ?? "<NA>"},
|
||||||
|
license: ${license ?? "<NA>"},
|
||||||
|
}''';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(covariant UserMetadata other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
|
return other.userId == userId &&
|
||||||
|
other.key == key &&
|
||||||
|
other.onboarding == onboarding &&
|
||||||
|
other.preferences == preferences &&
|
||||||
|
other.license == license;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return userId.hashCode ^
|
||||||
|
key.hashCode ^
|
||||||
|
onboarding.hashCode ^
|
||||||
|
preferences.hashCode ^
|
||||||
|
license.hashCode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,4 +61,8 @@ class AssetService {
|
|||||||
|
|
||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<(String, String)>> getPlaces() {
|
||||||
|
return _remoteAssetRepository.getPlaces();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,59 @@ class SyncStreamService {
|
|||||||
return _syncApiRepository.streamChanges(_handleEvents);
|
return _syncApiRepository.streamChanges(_handleEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> handleWsAssetUploadReadyV1Batch(List<dynamic> batchData) async {
|
||||||
|
if (batchData.isEmpty) return;
|
||||||
|
|
||||||
|
_logger.info(
|
||||||
|
'Processing batch of ${batchData.length} AssetUploadReadyV1 events',
|
||||||
|
);
|
||||||
|
|
||||||
|
final List<SyncAssetV1> assets = [];
|
||||||
|
final List<SyncAssetExifV1> exifs = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (final data in batchData) {
|
||||||
|
if (data is! Map<String, dynamic>) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final payload = data;
|
||||||
|
final assetData = payload['asset'];
|
||||||
|
final exifData = payload['exif'];
|
||||||
|
|
||||||
|
if (assetData == null || exifData == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final asset = SyncAssetV1.fromJson(assetData);
|
||||||
|
final exif = SyncAssetExifV1.fromJson(exifData);
|
||||||
|
|
||||||
|
if (asset != null && exif != null) {
|
||||||
|
assets.add(asset);
|
||||||
|
exifs.add(exif);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assets.isNotEmpty && exifs.isNotEmpty) {
|
||||||
|
await _syncStreamRepository.updateAssetsV1(
|
||||||
|
assets,
|
||||||
|
debugLabel: 'websocket-batch',
|
||||||
|
);
|
||||||
|
await _syncStreamRepository.updateAssetsExifV1(
|
||||||
|
exifs,
|
||||||
|
debugLabel: 'websocket-batch',
|
||||||
|
);
|
||||||
|
_logger.info('Successfully processed ${assets.length} assets in batch');
|
||||||
|
}
|
||||||
|
} catch (error, stackTrace) {
|
||||||
|
_logger.severe(
|
||||||
|
"Error processing AssetUploadReadyV1 websocket batch events",
|
||||||
|
error,
|
||||||
|
stackTrace,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _handleEvents(List<SyncEvent> events, Function() abort) async {
|
Future<void> _handleEvents(List<SyncEvent> events, Function() abort) async {
|
||||||
List<SyncEvent> items = [];
|
List<SyncEvent> items = [];
|
||||||
for (final event in events) {
|
for (final event in events) {
|
||||||
@@ -179,6 +232,14 @@ class SyncStreamService {
|
|||||||
data.cast(),
|
data.cast(),
|
||||||
debugLabel: 'partner',
|
debugLabel: 'partner',
|
||||||
);
|
);
|
||||||
|
case SyncEntityType.userMetadataV1:
|
||||||
|
return _syncStreamRepository.updateUserMetadatasV1(
|
||||||
|
data.cast(),
|
||||||
|
);
|
||||||
|
case SyncEntityType.userMetadataDeleteV1:
|
||||||
|
return _syncStreamRepository.deleteUserMetadatasV1(
|
||||||
|
data.cast(),
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
_logger.warning("Unknown sync data type: $type");
|
_logger.warning("Unknown sync data type: $type");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,9 @@ class TimelineFactory {
|
|||||||
|
|
||||||
TimelineService video(String userId) =>
|
TimelineService video(String userId) =>
|
||||||
TimelineService(_timelineRepository.video(userId, groupBy));
|
TimelineService(_timelineRepository.video(userId, groupBy));
|
||||||
|
|
||||||
|
TimelineService place(String place) =>
|
||||||
|
TimelineService(_timelineRepository.place(place, groupBy));
|
||||||
}
|
}
|
||||||
|
|
||||||
class TimelineService {
|
class TimelineService {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:worker_manager/worker_manager.dart';
|
|||||||
|
|
||||||
class BackgroundSyncManager {
|
class BackgroundSyncManager {
|
||||||
Cancelable<void>? _syncTask;
|
Cancelable<void>? _syncTask;
|
||||||
|
Cancelable<void>? _syncWebsocketTask;
|
||||||
Cancelable<void>? _deviceAlbumSyncTask;
|
Cancelable<void>? _deviceAlbumSyncTask;
|
||||||
Cancelable<void>? _hashTask;
|
Cancelable<void>? _hashTask;
|
||||||
|
|
||||||
@@ -20,6 +21,12 @@ class BackgroundSyncManager {
|
|||||||
_syncTask?.cancel();
|
_syncTask?.cancel();
|
||||||
_syncTask = null;
|
_syncTask = null;
|
||||||
|
|
||||||
|
if (_syncWebsocketTask != null) {
|
||||||
|
futures.add(_syncWebsocketTask!.future);
|
||||||
|
}
|
||||||
|
_syncWebsocketTask?.cancel();
|
||||||
|
_syncWebsocketTask = null;
|
||||||
|
|
||||||
return Future.wait(futures);
|
return Future.wait(futures);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,4 +79,19 @@ class BackgroundSyncManager {
|
|||||||
_syncTask = null;
|
_syncTask = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> syncWebsocketBatch(List<dynamic> batchData) {
|
||||||
|
if (_syncWebsocketTask != null) {
|
||||||
|
return _syncWebsocketTask!.future;
|
||||||
|
}
|
||||||
|
|
||||||
|
_syncWebsocketTask = runInIsolateGentle(
|
||||||
|
computation: (ref) => ref
|
||||||
|
.read(syncStreamServiceProvider)
|
||||||
|
.handleWsAssetUploadReadyV1Batch(batchData),
|
||||||
|
);
|
||||||
|
return _syncWebsocketTask!.whenComplete(() {
|
||||||
|
_syncWebsocketTask = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ mergedAsset: SELECT * FROM
|
|||||||
rae.thumb_hash,
|
rae.thumb_hash,
|
||||||
rae.checksum,
|
rae.checksum,
|
||||||
rae.owner_id,
|
rae.owner_id,
|
||||||
|
rae.live_photo_video_id,
|
||||||
0 as orientation
|
0 as orientation
|
||||||
FROM
|
FROM
|
||||||
remote_asset_entity rae
|
remote_asset_entity rae
|
||||||
@@ -39,6 +40,7 @@ mergedAsset: SELECT * FROM
|
|||||||
NULL as thumb_hash,
|
NULL as thumb_hash,
|
||||||
lae.checksum,
|
lae.checksum,
|
||||||
NULL as owner_id,
|
NULL as owner_id,
|
||||||
|
NULL as live_photo_video_id,
|
||||||
lae.orientation
|
lae.orientation
|
||||||
FROM
|
FROM
|
||||||
local_asset_entity lae
|
local_asset_entity lae
|
||||||
|
|||||||
+4
-1
@@ -18,7 +18,7 @@ class MergedAssetDrift extends i1.ModularAccessor {
|
|||||||
final generatedlimit = $write(limit, startIndex: $arrayStartIndex);
|
final generatedlimit = $write(limit, startIndex: $arrayStartIndex);
|
||||||
$arrayStartIndex += generatedlimit.amountOfVariables;
|
$arrayStartIndex += generatedlimit.amountOfVariables;
|
||||||
return customSelect(
|
return customSelect(
|
||||||
'SELECT * FROM (SELECT rae.id AS remote_id, lae.id AS local_id, rae.name, rae.type, rae.created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, 0 AS orientation FROM remote_asset_entity AS rae LEFT JOIN local_asset_entity AS lae ON rae.checksum = lae.checksum WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar1) UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, lae.orientation FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum WHERE rae.id IS NULL) ORDER BY created_at DESC ${generatedlimit.sql}',
|
'SELECT * FROM (SELECT rae.id AS remote_id, lae.id AS local_id, rae.name, rae.type, rae.created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation FROM remote_asset_entity AS rae LEFT JOIN local_asset_entity AS lae ON rae.checksum = lae.checksum WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar1) UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum WHERE rae.id IS NULL) ORDER BY created_at DESC ${generatedlimit.sql}',
|
||||||
variables: [
|
variables: [
|
||||||
for (var $ in var1) i0.Variable<String>($),
|
for (var $ in var1) i0.Variable<String>($),
|
||||||
...generatedlimit.introducedVariables
|
...generatedlimit.introducedVariables
|
||||||
@@ -42,6 +42,7 @@ class MergedAssetDrift extends i1.ModularAccessor {
|
|||||||
thumbHash: row.readNullable<String>('thumb_hash'),
|
thumbHash: row.readNullable<String>('thumb_hash'),
|
||||||
checksum: row.readNullable<String>('checksum'),
|
checksum: row.readNullable<String>('checksum'),
|
||||||
ownerId: row.readNullable<String>('owner_id'),
|
ownerId: row.readNullable<String>('owner_id'),
|
||||||
|
livePhotoVideoId: row.readNullable<String>('live_photo_video_id'),
|
||||||
orientation: row.read<int>('orientation'),
|
orientation: row.read<int>('orientation'),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -88,6 +89,7 @@ class MergedAssetResult {
|
|||||||
final String? thumbHash;
|
final String? thumbHash;
|
||||||
final String? checksum;
|
final String? checksum;
|
||||||
final String? ownerId;
|
final String? ownerId;
|
||||||
|
final String? livePhotoVideoId;
|
||||||
final int orientation;
|
final int orientation;
|
||||||
MergedAssetResult({
|
MergedAssetResult({
|
||||||
this.remoteId,
|
this.remoteId,
|
||||||
@@ -103,6 +105,7 @@ class MergedAssetResult {
|
|||||||
this.thumbHash,
|
this.thumbHash,
|
||||||
this.checksum,
|
this.checksum,
|
||||||
this.ownerId,
|
this.ownerId,
|
||||||
|
this.livePhotoVideoId,
|
||||||
required this.orientation,
|
required this.orientation,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ class RemoteAssetEntity extends Table
|
|||||||
|
|
||||||
DateTimeColumn get deletedAt => dateTime().nullable()();
|
DateTimeColumn get deletedAt => dateTime().nullable()();
|
||||||
|
|
||||||
|
TextColumn get livePhotoVideoId => text().nullable()();
|
||||||
|
|
||||||
IntColumn get visibility => intEnum<AssetVisibility>()();
|
IntColumn get visibility => intEnum<AssetVisibility>()();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -51,6 +53,7 @@ extension RemoteAssetEntityDataDomainEx on RemoteAssetEntityData {
|
|||||||
width: width,
|
width: width,
|
||||||
thumbHash: thumbHash,
|
thumbHash: thumbHash,
|
||||||
visibility: visibility,
|
visibility: visibility,
|
||||||
|
livePhotoVideoId: livePhotoVideoId,
|
||||||
localId: null,
|
localId: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ typedef $$RemoteAssetEntityTableCreateCompanionBuilder
|
|||||||
i0.Value<DateTime?> localDateTime,
|
i0.Value<DateTime?> localDateTime,
|
||||||
i0.Value<String?> thumbHash,
|
i0.Value<String?> thumbHash,
|
||||||
i0.Value<DateTime?> deletedAt,
|
i0.Value<DateTime?> deletedAt,
|
||||||
|
i0.Value<String?> livePhotoVideoId,
|
||||||
required i2.AssetVisibility visibility,
|
required i2.AssetVisibility visibility,
|
||||||
});
|
});
|
||||||
typedef $$RemoteAssetEntityTableUpdateCompanionBuilder
|
typedef $$RemoteAssetEntityTableUpdateCompanionBuilder
|
||||||
@@ -45,6 +46,7 @@ typedef $$RemoteAssetEntityTableUpdateCompanionBuilder
|
|||||||
i0.Value<DateTime?> localDateTime,
|
i0.Value<DateTime?> localDateTime,
|
||||||
i0.Value<String?> thumbHash,
|
i0.Value<String?> thumbHash,
|
||||||
i0.Value<DateTime?> deletedAt,
|
i0.Value<DateTime?> deletedAt,
|
||||||
|
i0.Value<String?> livePhotoVideoId,
|
||||||
i0.Value<i2.AssetVisibility> visibility,
|
i0.Value<i2.AssetVisibility> visibility,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -134,6 +136,10 @@ class $$RemoteAssetEntityTableFilterComposer
|
|||||||
i0.ColumnFilters<DateTime> get deletedAt => $composableBuilder(
|
i0.ColumnFilters<DateTime> get deletedAt => $composableBuilder(
|
||||||
column: $table.deletedAt, builder: (column) => i0.ColumnFilters(column));
|
column: $table.deletedAt, builder: (column) => i0.ColumnFilters(column));
|
||||||
|
|
||||||
|
i0.ColumnFilters<String> get livePhotoVideoId => $composableBuilder(
|
||||||
|
column: $table.livePhotoVideoId,
|
||||||
|
builder: (column) => i0.ColumnFilters(column));
|
||||||
|
|
||||||
i0.ColumnWithTypeConverterFilters<i2.AssetVisibility, i2.AssetVisibility, int>
|
i0.ColumnWithTypeConverterFilters<i2.AssetVisibility, i2.AssetVisibility, int>
|
||||||
get visibility => $composableBuilder(
|
get visibility => $composableBuilder(
|
||||||
column: $table.visibility,
|
column: $table.visibility,
|
||||||
@@ -217,6 +223,10 @@ class $$RemoteAssetEntityTableOrderingComposer
|
|||||||
column: $table.deletedAt,
|
column: $table.deletedAt,
|
||||||
builder: (column) => i0.ColumnOrderings(column));
|
builder: (column) => i0.ColumnOrderings(column));
|
||||||
|
|
||||||
|
i0.ColumnOrderings<String> get livePhotoVideoId => $composableBuilder(
|
||||||
|
column: $table.livePhotoVideoId,
|
||||||
|
builder: (column) => i0.ColumnOrderings(column));
|
||||||
|
|
||||||
i0.ColumnOrderings<int> get visibility => $composableBuilder(
|
i0.ColumnOrderings<int> get visibility => $composableBuilder(
|
||||||
column: $table.visibility,
|
column: $table.visibility,
|
||||||
builder: (column) => i0.ColumnOrderings(column));
|
builder: (column) => i0.ColumnOrderings(column));
|
||||||
@@ -292,6 +302,9 @@ class $$RemoteAssetEntityTableAnnotationComposer
|
|||||||
i0.GeneratedColumn<DateTime> get deletedAt =>
|
i0.GeneratedColumn<DateTime> get deletedAt =>
|
||||||
$composableBuilder(column: $table.deletedAt, builder: (column) => column);
|
$composableBuilder(column: $table.deletedAt, builder: (column) => column);
|
||||||
|
|
||||||
|
i0.GeneratedColumn<String> get livePhotoVideoId => $composableBuilder(
|
||||||
|
column: $table.livePhotoVideoId, builder: (column) => column);
|
||||||
|
|
||||||
i0.GeneratedColumnWithTypeConverter<i2.AssetVisibility, int> get visibility =>
|
i0.GeneratedColumnWithTypeConverter<i2.AssetVisibility, int> get visibility =>
|
||||||
$composableBuilder(
|
$composableBuilder(
|
||||||
column: $table.visibility, builder: (column) => column);
|
column: $table.visibility, builder: (column) => column);
|
||||||
@@ -358,6 +371,7 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
|
|||||||
i0.Value<DateTime?> localDateTime = const i0.Value.absent(),
|
i0.Value<DateTime?> localDateTime = const i0.Value.absent(),
|
||||||
i0.Value<String?> thumbHash = const i0.Value.absent(),
|
i0.Value<String?> thumbHash = const i0.Value.absent(),
|
||||||
i0.Value<DateTime?> deletedAt = const i0.Value.absent(),
|
i0.Value<DateTime?> deletedAt = const i0.Value.absent(),
|
||||||
|
i0.Value<String?> livePhotoVideoId = const i0.Value.absent(),
|
||||||
i0.Value<i2.AssetVisibility> visibility = const i0.Value.absent(),
|
i0.Value<i2.AssetVisibility> visibility = const i0.Value.absent(),
|
||||||
}) =>
|
}) =>
|
||||||
i1.RemoteAssetEntityCompanion(
|
i1.RemoteAssetEntityCompanion(
|
||||||
@@ -375,6 +389,7 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
|
|||||||
localDateTime: localDateTime,
|
localDateTime: localDateTime,
|
||||||
thumbHash: thumbHash,
|
thumbHash: thumbHash,
|
||||||
deletedAt: deletedAt,
|
deletedAt: deletedAt,
|
||||||
|
livePhotoVideoId: livePhotoVideoId,
|
||||||
visibility: visibility,
|
visibility: visibility,
|
||||||
),
|
),
|
||||||
createCompanionCallback: ({
|
createCompanionCallback: ({
|
||||||
@@ -392,6 +407,7 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
|
|||||||
i0.Value<DateTime?> localDateTime = const i0.Value.absent(),
|
i0.Value<DateTime?> localDateTime = const i0.Value.absent(),
|
||||||
i0.Value<String?> thumbHash = const i0.Value.absent(),
|
i0.Value<String?> thumbHash = const i0.Value.absent(),
|
||||||
i0.Value<DateTime?> deletedAt = const i0.Value.absent(),
|
i0.Value<DateTime?> deletedAt = const i0.Value.absent(),
|
||||||
|
i0.Value<String?> livePhotoVideoId = const i0.Value.absent(),
|
||||||
required i2.AssetVisibility visibility,
|
required i2.AssetVisibility visibility,
|
||||||
}) =>
|
}) =>
|
||||||
i1.RemoteAssetEntityCompanion.insert(
|
i1.RemoteAssetEntityCompanion.insert(
|
||||||
@@ -409,6 +425,7 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
|
|||||||
localDateTime: localDateTime,
|
localDateTime: localDateTime,
|
||||||
thumbHash: thumbHash,
|
thumbHash: thumbHash,
|
||||||
deletedAt: deletedAt,
|
deletedAt: deletedAt,
|
||||||
|
livePhotoVideoId: livePhotoVideoId,
|
||||||
visibility: visibility,
|
visibility: visibility,
|
||||||
),
|
),
|
||||||
withReferenceMapper: (p0) => p0
|
withReferenceMapper: (p0) => p0
|
||||||
@@ -573,6 +590,12 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
|
|||||||
late final i0.GeneratedColumn<DateTime> deletedAt =
|
late final i0.GeneratedColumn<DateTime> deletedAt =
|
||||||
i0.GeneratedColumn<DateTime>('deleted_at', aliasedName, true,
|
i0.GeneratedColumn<DateTime>('deleted_at', aliasedName, true,
|
||||||
type: i0.DriftSqlType.dateTime, requiredDuringInsert: false);
|
type: i0.DriftSqlType.dateTime, requiredDuringInsert: false);
|
||||||
|
static const i0.VerificationMeta _livePhotoVideoIdMeta =
|
||||||
|
const i0.VerificationMeta('livePhotoVideoId');
|
||||||
|
@override
|
||||||
|
late final i0.GeneratedColumn<String> livePhotoVideoId =
|
||||||
|
i0.GeneratedColumn<String>('live_photo_video_id', aliasedName, true,
|
||||||
|
type: i0.DriftSqlType.string, requiredDuringInsert: false);
|
||||||
@override
|
@override
|
||||||
late final i0.GeneratedColumnWithTypeConverter<i2.AssetVisibility, int>
|
late final i0.GeneratedColumnWithTypeConverter<i2.AssetVisibility, int>
|
||||||
visibility = i0.GeneratedColumn<int>('visibility', aliasedName, false,
|
visibility = i0.GeneratedColumn<int>('visibility', aliasedName, false,
|
||||||
@@ -595,6 +618,7 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
|
|||||||
localDateTime,
|
localDateTime,
|
||||||
thumbHash,
|
thumbHash,
|
||||||
deletedAt,
|
deletedAt,
|
||||||
|
livePhotoVideoId,
|
||||||
visibility
|
visibility
|
||||||
];
|
];
|
||||||
@override
|
@override
|
||||||
@@ -673,6 +697,12 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
|
|||||||
context.handle(_deletedAtMeta,
|
context.handle(_deletedAtMeta,
|
||||||
deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta));
|
deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta));
|
||||||
}
|
}
|
||||||
|
if (data.containsKey('live_photo_video_id')) {
|
||||||
|
context.handle(
|
||||||
|
_livePhotoVideoIdMeta,
|
||||||
|
livePhotoVideoId.isAcceptableOrUnknown(
|
||||||
|
data['live_photo_video_id']!, _livePhotoVideoIdMeta));
|
||||||
|
}
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -712,6 +742,9 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
|
|||||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}thumb_hash']),
|
.read(i0.DriftSqlType.string, data['${effectivePrefix}thumb_hash']),
|
||||||
deletedAt: attachedDatabase.typeMapping
|
deletedAt: attachedDatabase.typeMapping
|
||||||
.read(i0.DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']),
|
.read(i0.DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']),
|
||||||
|
livePhotoVideoId: attachedDatabase.typeMapping.read(
|
||||||
|
i0.DriftSqlType.string,
|
||||||
|
data['${effectivePrefix}live_photo_video_id']),
|
||||||
visibility: i1.$RemoteAssetEntityTable.$convertervisibility.fromSql(
|
visibility: i1.$RemoteAssetEntityTable.$convertervisibility.fromSql(
|
||||||
attachedDatabase.typeMapping.read(
|
attachedDatabase.typeMapping.read(
|
||||||
i0.DriftSqlType.int, data['${effectivePrefix}visibility'])!),
|
i0.DriftSqlType.int, data['${effectivePrefix}visibility'])!),
|
||||||
@@ -750,6 +783,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
final DateTime? localDateTime;
|
final DateTime? localDateTime;
|
||||||
final String? thumbHash;
|
final String? thumbHash;
|
||||||
final DateTime? deletedAt;
|
final DateTime? deletedAt;
|
||||||
|
final String? livePhotoVideoId;
|
||||||
final i2.AssetVisibility visibility;
|
final i2.AssetVisibility visibility;
|
||||||
const RemoteAssetEntityData(
|
const RemoteAssetEntityData(
|
||||||
{required this.name,
|
{required this.name,
|
||||||
@@ -766,6 +800,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
this.localDateTime,
|
this.localDateTime,
|
||||||
this.thumbHash,
|
this.thumbHash,
|
||||||
this.deletedAt,
|
this.deletedAt,
|
||||||
|
this.livePhotoVideoId,
|
||||||
required this.visibility});
|
required this.visibility});
|
||||||
@override
|
@override
|
||||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||||
@@ -799,6 +834,9 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
if (!nullToAbsent || deletedAt != null) {
|
if (!nullToAbsent || deletedAt != null) {
|
||||||
map['deleted_at'] = i0.Variable<DateTime>(deletedAt);
|
map['deleted_at'] = i0.Variable<DateTime>(deletedAt);
|
||||||
}
|
}
|
||||||
|
if (!nullToAbsent || livePhotoVideoId != null) {
|
||||||
|
map['live_photo_video_id'] = i0.Variable<String>(livePhotoVideoId);
|
||||||
|
}
|
||||||
{
|
{
|
||||||
map['visibility'] = i0.Variable<int>(
|
map['visibility'] = i0.Variable<int>(
|
||||||
i1.$RemoteAssetEntityTable.$convertervisibility.toSql(visibility));
|
i1.$RemoteAssetEntityTable.$convertervisibility.toSql(visibility));
|
||||||
@@ -825,6 +863,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
localDateTime: serializer.fromJson<DateTime?>(json['localDateTime']),
|
localDateTime: serializer.fromJson<DateTime?>(json['localDateTime']),
|
||||||
thumbHash: serializer.fromJson<String?>(json['thumbHash']),
|
thumbHash: serializer.fromJson<String?>(json['thumbHash']),
|
||||||
deletedAt: serializer.fromJson<DateTime?>(json['deletedAt']),
|
deletedAt: serializer.fromJson<DateTime?>(json['deletedAt']),
|
||||||
|
livePhotoVideoId: serializer.fromJson<String?>(json['livePhotoVideoId']),
|
||||||
visibility: i1.$RemoteAssetEntityTable.$convertervisibility
|
visibility: i1.$RemoteAssetEntityTable.$convertervisibility
|
||||||
.fromJson(serializer.fromJson<int>(json['visibility'])),
|
.fromJson(serializer.fromJson<int>(json['visibility'])),
|
||||||
);
|
);
|
||||||
@@ -848,6 +887,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
'localDateTime': serializer.toJson<DateTime?>(localDateTime),
|
'localDateTime': serializer.toJson<DateTime?>(localDateTime),
|
||||||
'thumbHash': serializer.toJson<String?>(thumbHash),
|
'thumbHash': serializer.toJson<String?>(thumbHash),
|
||||||
'deletedAt': serializer.toJson<DateTime?>(deletedAt),
|
'deletedAt': serializer.toJson<DateTime?>(deletedAt),
|
||||||
|
'livePhotoVideoId': serializer.toJson<String?>(livePhotoVideoId),
|
||||||
'visibility': serializer.toJson<int>(
|
'visibility': serializer.toJson<int>(
|
||||||
i1.$RemoteAssetEntityTable.$convertervisibility.toJson(visibility)),
|
i1.$RemoteAssetEntityTable.$convertervisibility.toJson(visibility)),
|
||||||
};
|
};
|
||||||
@@ -868,6 +908,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
i0.Value<DateTime?> localDateTime = const i0.Value.absent(),
|
i0.Value<DateTime?> localDateTime = const i0.Value.absent(),
|
||||||
i0.Value<String?> thumbHash = const i0.Value.absent(),
|
i0.Value<String?> thumbHash = const i0.Value.absent(),
|
||||||
i0.Value<DateTime?> deletedAt = const i0.Value.absent(),
|
i0.Value<DateTime?> deletedAt = const i0.Value.absent(),
|
||||||
|
i0.Value<String?> livePhotoVideoId = const i0.Value.absent(),
|
||||||
i2.AssetVisibility? visibility}) =>
|
i2.AssetVisibility? visibility}) =>
|
||||||
i1.RemoteAssetEntityData(
|
i1.RemoteAssetEntityData(
|
||||||
name: name ?? this.name,
|
name: name ?? this.name,
|
||||||
@@ -887,6 +928,9 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
localDateTime.present ? localDateTime.value : this.localDateTime,
|
localDateTime.present ? localDateTime.value : this.localDateTime,
|
||||||
thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash,
|
thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash,
|
||||||
deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt,
|
deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt,
|
||||||
|
livePhotoVideoId: livePhotoVideoId.present
|
||||||
|
? livePhotoVideoId.value
|
||||||
|
: this.livePhotoVideoId,
|
||||||
visibility: visibility ?? this.visibility,
|
visibility: visibility ?? this.visibility,
|
||||||
);
|
);
|
||||||
RemoteAssetEntityData copyWithCompanion(i1.RemoteAssetEntityCompanion data) {
|
RemoteAssetEntityData copyWithCompanion(i1.RemoteAssetEntityCompanion data) {
|
||||||
@@ -910,6 +954,9 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
: this.localDateTime,
|
: this.localDateTime,
|
||||||
thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash,
|
thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash,
|
||||||
deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt,
|
deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt,
|
||||||
|
livePhotoVideoId: data.livePhotoVideoId.present
|
||||||
|
? data.livePhotoVideoId.value
|
||||||
|
: this.livePhotoVideoId,
|
||||||
visibility:
|
visibility:
|
||||||
data.visibility.present ? data.visibility.value : this.visibility,
|
data.visibility.present ? data.visibility.value : this.visibility,
|
||||||
);
|
);
|
||||||
@@ -932,6 +979,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
..write('localDateTime: $localDateTime, ')
|
..write('localDateTime: $localDateTime, ')
|
||||||
..write('thumbHash: $thumbHash, ')
|
..write('thumbHash: $thumbHash, ')
|
||||||
..write('deletedAt: $deletedAt, ')
|
..write('deletedAt: $deletedAt, ')
|
||||||
|
..write('livePhotoVideoId: $livePhotoVideoId, ')
|
||||||
..write('visibility: $visibility')
|
..write('visibility: $visibility')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
@@ -953,6 +1001,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
localDateTime,
|
localDateTime,
|
||||||
thumbHash,
|
thumbHash,
|
||||||
deletedAt,
|
deletedAt,
|
||||||
|
livePhotoVideoId,
|
||||||
visibility);
|
visibility);
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
@@ -972,6 +1021,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
other.localDateTime == this.localDateTime &&
|
other.localDateTime == this.localDateTime &&
|
||||||
other.thumbHash == this.thumbHash &&
|
other.thumbHash == this.thumbHash &&
|
||||||
other.deletedAt == this.deletedAt &&
|
other.deletedAt == this.deletedAt &&
|
||||||
|
other.livePhotoVideoId == this.livePhotoVideoId &&
|
||||||
other.visibility == this.visibility);
|
other.visibility == this.visibility);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -991,6 +1041,7 @@ class RemoteAssetEntityCompanion
|
|||||||
final i0.Value<DateTime?> localDateTime;
|
final i0.Value<DateTime?> localDateTime;
|
||||||
final i0.Value<String?> thumbHash;
|
final i0.Value<String?> thumbHash;
|
||||||
final i0.Value<DateTime?> deletedAt;
|
final i0.Value<DateTime?> deletedAt;
|
||||||
|
final i0.Value<String?> livePhotoVideoId;
|
||||||
final i0.Value<i2.AssetVisibility> visibility;
|
final i0.Value<i2.AssetVisibility> visibility;
|
||||||
const RemoteAssetEntityCompanion({
|
const RemoteAssetEntityCompanion({
|
||||||
this.name = const i0.Value.absent(),
|
this.name = const i0.Value.absent(),
|
||||||
@@ -1007,6 +1058,7 @@ class RemoteAssetEntityCompanion
|
|||||||
this.localDateTime = const i0.Value.absent(),
|
this.localDateTime = const i0.Value.absent(),
|
||||||
this.thumbHash = const i0.Value.absent(),
|
this.thumbHash = const i0.Value.absent(),
|
||||||
this.deletedAt = const i0.Value.absent(),
|
this.deletedAt = const i0.Value.absent(),
|
||||||
|
this.livePhotoVideoId = const i0.Value.absent(),
|
||||||
this.visibility = const i0.Value.absent(),
|
this.visibility = const i0.Value.absent(),
|
||||||
});
|
});
|
||||||
RemoteAssetEntityCompanion.insert({
|
RemoteAssetEntityCompanion.insert({
|
||||||
@@ -1024,6 +1076,7 @@ class RemoteAssetEntityCompanion
|
|||||||
this.localDateTime = const i0.Value.absent(),
|
this.localDateTime = const i0.Value.absent(),
|
||||||
this.thumbHash = const i0.Value.absent(),
|
this.thumbHash = const i0.Value.absent(),
|
||||||
this.deletedAt = const i0.Value.absent(),
|
this.deletedAt = const i0.Value.absent(),
|
||||||
|
this.livePhotoVideoId = const i0.Value.absent(),
|
||||||
required i2.AssetVisibility visibility,
|
required i2.AssetVisibility visibility,
|
||||||
}) : name = i0.Value(name),
|
}) : name = i0.Value(name),
|
||||||
type = i0.Value(type),
|
type = i0.Value(type),
|
||||||
@@ -1046,6 +1099,7 @@ class RemoteAssetEntityCompanion
|
|||||||
i0.Expression<DateTime>? localDateTime,
|
i0.Expression<DateTime>? localDateTime,
|
||||||
i0.Expression<String>? thumbHash,
|
i0.Expression<String>? thumbHash,
|
||||||
i0.Expression<DateTime>? deletedAt,
|
i0.Expression<DateTime>? deletedAt,
|
||||||
|
i0.Expression<String>? livePhotoVideoId,
|
||||||
i0.Expression<int>? visibility,
|
i0.Expression<int>? visibility,
|
||||||
}) {
|
}) {
|
||||||
return i0.RawValuesInsertable({
|
return i0.RawValuesInsertable({
|
||||||
@@ -1063,6 +1117,7 @@ class RemoteAssetEntityCompanion
|
|||||||
if (localDateTime != null) 'local_date_time': localDateTime,
|
if (localDateTime != null) 'local_date_time': localDateTime,
|
||||||
if (thumbHash != null) 'thumb_hash': thumbHash,
|
if (thumbHash != null) 'thumb_hash': thumbHash,
|
||||||
if (deletedAt != null) 'deleted_at': deletedAt,
|
if (deletedAt != null) 'deleted_at': deletedAt,
|
||||||
|
if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId,
|
||||||
if (visibility != null) 'visibility': visibility,
|
if (visibility != null) 'visibility': visibility,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1082,6 +1137,7 @@ class RemoteAssetEntityCompanion
|
|||||||
i0.Value<DateTime?>? localDateTime,
|
i0.Value<DateTime?>? localDateTime,
|
||||||
i0.Value<String?>? thumbHash,
|
i0.Value<String?>? thumbHash,
|
||||||
i0.Value<DateTime?>? deletedAt,
|
i0.Value<DateTime?>? deletedAt,
|
||||||
|
i0.Value<String?>? livePhotoVideoId,
|
||||||
i0.Value<i2.AssetVisibility>? visibility}) {
|
i0.Value<i2.AssetVisibility>? visibility}) {
|
||||||
return i1.RemoteAssetEntityCompanion(
|
return i1.RemoteAssetEntityCompanion(
|
||||||
name: name ?? this.name,
|
name: name ?? this.name,
|
||||||
@@ -1098,6 +1154,7 @@ class RemoteAssetEntityCompanion
|
|||||||
localDateTime: localDateTime ?? this.localDateTime,
|
localDateTime: localDateTime ?? this.localDateTime,
|
||||||
thumbHash: thumbHash ?? this.thumbHash,
|
thumbHash: thumbHash ?? this.thumbHash,
|
||||||
deletedAt: deletedAt ?? this.deletedAt,
|
deletedAt: deletedAt ?? this.deletedAt,
|
||||||
|
livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId,
|
||||||
visibility: visibility ?? this.visibility,
|
visibility: visibility ?? this.visibility,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1148,6 +1205,9 @@ class RemoteAssetEntityCompanion
|
|||||||
if (deletedAt.present) {
|
if (deletedAt.present) {
|
||||||
map['deleted_at'] = i0.Variable<DateTime>(deletedAt.value);
|
map['deleted_at'] = i0.Variable<DateTime>(deletedAt.value);
|
||||||
}
|
}
|
||||||
|
if (livePhotoVideoId.present) {
|
||||||
|
map['live_photo_video_id'] = i0.Variable<String>(livePhotoVideoId.value);
|
||||||
|
}
|
||||||
if (visibility.present) {
|
if (visibility.present) {
|
||||||
map['visibility'] = i0.Variable<int>(i1
|
map['visibility'] = i0.Variable<int>(i1
|
||||||
.$RemoteAssetEntityTable.$convertervisibility
|
.$RemoteAssetEntityTable.$convertervisibility
|
||||||
@@ -1173,6 +1233,7 @@ class RemoteAssetEntityCompanion
|
|||||||
..write('localDateTime: $localDateTime, ')
|
..write('localDateTime: $localDateTime, ')
|
||||||
..write('thumbHash: $thumbHash, ')
|
..write('thumbHash: $thumbHash, ')
|
||||||
..write('deletedAt: $deletedAt, ')
|
..write('deletedAt: $deletedAt, ')
|
||||||
|
..write('livePhotoVideoId: $livePhotoVideoId, ')
|
||||||
..write('visibility: $visibility')
|
..write('visibility: $visibility')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
|
|||||||
@@ -8,14 +8,16 @@ class UserMetadataEntity extends Table with DriftDefaultsMixin {
|
|||||||
|
|
||||||
TextColumn get userId =>
|
TextColumn get userId =>
|
||||||
text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
|
text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
|
||||||
TextColumn get preferences => text().map(userPreferenceConverter)();
|
|
||||||
|
IntColumn get key => intEnum<UserMetadataKey>()();
|
||||||
|
|
||||||
|
BlobColumn get value => blob().map(userMetadataConverter)();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Set<Column> get primaryKey => {userId};
|
Set<Column> get primaryKey => {userId, key};
|
||||||
}
|
}
|
||||||
|
|
||||||
final JsonTypeConverter2<UserPreferences, String, Object?>
|
final JsonTypeConverter2<Map<String, Object?>, Uint8List, Object?>
|
||||||
userPreferenceConverter = TypeConverter.json2(
|
userMetadataConverter = TypeConverter.jsonb(
|
||||||
fromJson: (json) => UserPreferences.fromMap(json as Map<String, Object?>),
|
fromJson: (json) => json as Map<String, Object?>,
|
||||||
toJson: (pref) => pref.toMap(),
|
|
||||||
);
|
);
|
||||||
|
|||||||
+148
-93
@@ -4,21 +4,24 @@ import 'package:drift/drift.dart' as i0;
|
|||||||
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart'
|
||||||
as i1;
|
as i1;
|
||||||
import 'package:immich_mobile/domain/models/user_metadata.model.dart' as i2;
|
import 'package:immich_mobile/domain/models/user_metadata.model.dart' as i2;
|
||||||
|
import 'dart:typed_data' as i3;
|
||||||
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart'
|
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart'
|
||||||
as i3;
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
|
|
||||||
as i4;
|
as i4;
|
||||||
import 'package:drift/internal/modular.dart' as i5;
|
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
|
||||||
|
as i5;
|
||||||
|
import 'package:drift/internal/modular.dart' as i6;
|
||||||
|
|
||||||
typedef $$UserMetadataEntityTableCreateCompanionBuilder
|
typedef $$UserMetadataEntityTableCreateCompanionBuilder
|
||||||
= i1.UserMetadataEntityCompanion Function({
|
= i1.UserMetadataEntityCompanion Function({
|
||||||
required String userId,
|
required String userId,
|
||||||
required i2.UserPreferences preferences,
|
required i2.UserMetadataKey key,
|
||||||
|
required Map<String, Object?> value,
|
||||||
});
|
});
|
||||||
typedef $$UserMetadataEntityTableUpdateCompanionBuilder
|
typedef $$UserMetadataEntityTableUpdateCompanionBuilder
|
||||||
= i1.UserMetadataEntityCompanion Function({
|
= i1.UserMetadataEntityCompanion Function({
|
||||||
i0.Value<String> userId,
|
i0.Value<String> userId,
|
||||||
i0.Value<i2.UserPreferences> preferences,
|
i0.Value<i2.UserMetadataKey> key,
|
||||||
|
i0.Value<Map<String, Object?>> value,
|
||||||
});
|
});
|
||||||
|
|
||||||
final class $$UserMetadataEntityTableReferences extends i0.BaseReferences<
|
final class $$UserMetadataEntityTableReferences extends i0.BaseReferences<
|
||||||
@@ -28,26 +31,26 @@ final class $$UserMetadataEntityTableReferences extends i0.BaseReferences<
|
|||||||
$$UserMetadataEntityTableReferences(
|
$$UserMetadataEntityTableReferences(
|
||||||
super.$_db, super.$_table, super.$_typedResult);
|
super.$_db, super.$_table, super.$_typedResult);
|
||||||
|
|
||||||
static i4.$UserEntityTable _userIdTable(i0.GeneratedDatabase db) =>
|
static i5.$UserEntityTable _userIdTable(i0.GeneratedDatabase db) =>
|
||||||
i5.ReadDatabaseContainer(db)
|
i6.ReadDatabaseContainer(db)
|
||||||
.resultSet<i4.$UserEntityTable>('user_entity')
|
.resultSet<i5.$UserEntityTable>('user_entity')
|
||||||
.createAlias(i0.$_aliasNameGenerator(
|
.createAlias(i0.$_aliasNameGenerator(
|
||||||
i5.ReadDatabaseContainer(db)
|
i6.ReadDatabaseContainer(db)
|
||||||
.resultSet<i1.$UserMetadataEntityTable>(
|
.resultSet<i1.$UserMetadataEntityTable>(
|
||||||
'user_metadata_entity')
|
'user_metadata_entity')
|
||||||
.userId,
|
.userId,
|
||||||
i5.ReadDatabaseContainer(db)
|
i6.ReadDatabaseContainer(db)
|
||||||
.resultSet<i4.$UserEntityTable>('user_entity')
|
.resultSet<i5.$UserEntityTable>('user_entity')
|
||||||
.id));
|
.id));
|
||||||
|
|
||||||
i4.$$UserEntityTableProcessedTableManager get userId {
|
i5.$$UserEntityTableProcessedTableManager get userId {
|
||||||
final $_column = $_itemColumn<String>('user_id')!;
|
final $_column = $_itemColumn<String>('user_id')!;
|
||||||
|
|
||||||
final manager = i4
|
final manager = i5
|
||||||
.$$UserEntityTableTableManager(
|
.$$UserEntityTableTableManager(
|
||||||
$_db,
|
$_db,
|
||||||
i5.ReadDatabaseContainer($_db)
|
i6.ReadDatabaseContainer($_db)
|
||||||
.resultSet<i4.$UserEntityTable>('user_entity'))
|
.resultSet<i5.$UserEntityTable>('user_entity'))
|
||||||
.filter((f) => f.id.sqlEquals($_column));
|
.filter((f) => f.id.sqlEquals($_column));
|
||||||
final item = $_typedResult.readTableOrNull(_userIdTable($_db));
|
final item = $_typedResult.readTableOrNull(_userIdTable($_db));
|
||||||
if (item == null) return manager;
|
if (item == null) return manager;
|
||||||
@@ -65,26 +68,31 @@ class $$UserMetadataEntityTableFilterComposer
|
|||||||
super.$addJoinBuilderToRootComposer,
|
super.$addJoinBuilderToRootComposer,
|
||||||
super.$removeJoinBuilderFromRootComposer,
|
super.$removeJoinBuilderFromRootComposer,
|
||||||
});
|
});
|
||||||
i0.ColumnWithTypeConverterFilters<i2.UserPreferences, i2.UserPreferences,
|
i0.ColumnWithTypeConverterFilters<i2.UserMetadataKey, i2.UserMetadataKey, int>
|
||||||
String>
|
get key => $composableBuilder(
|
||||||
get preferences => $composableBuilder(
|
column: $table.key,
|
||||||
column: $table.preferences,
|
|
||||||
builder: (column) => i0.ColumnWithTypeConverterFilters(column));
|
builder: (column) => i0.ColumnWithTypeConverterFilters(column));
|
||||||
|
|
||||||
i4.$$UserEntityTableFilterComposer get userId {
|
i0.ColumnWithTypeConverterFilters<Map<String, Object?>, Map<String, Object>,
|
||||||
final i4.$$UserEntityTableFilterComposer composer = $composerBuilder(
|
i3.Uint8List>
|
||||||
|
get value => $composableBuilder(
|
||||||
|
column: $table.value,
|
||||||
|
builder: (column) => i0.ColumnWithTypeConverterFilters(column));
|
||||||
|
|
||||||
|
i5.$$UserEntityTableFilterComposer get userId {
|
||||||
|
final i5.$$UserEntityTableFilterComposer composer = $composerBuilder(
|
||||||
composer: this,
|
composer: this,
|
||||||
getCurrentColumn: (t) => t.userId,
|
getCurrentColumn: (t) => t.userId,
|
||||||
referencedTable: i5.ReadDatabaseContainer($db)
|
referencedTable: i6.ReadDatabaseContainer($db)
|
||||||
.resultSet<i4.$UserEntityTable>('user_entity'),
|
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||||
getReferencedColumn: (t) => t.id,
|
getReferencedColumn: (t) => t.id,
|
||||||
builder: (joinBuilder,
|
builder: (joinBuilder,
|
||||||
{$addJoinBuilderToRootComposer,
|
{$addJoinBuilderToRootComposer,
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
$removeJoinBuilderFromRootComposer}) =>
|
||||||
i4.$$UserEntityTableFilterComposer(
|
i5.$$UserEntityTableFilterComposer(
|
||||||
$db: $db,
|
$db: $db,
|
||||||
$table: i5.ReadDatabaseContainer($db)
|
$table: i6.ReadDatabaseContainer($db)
|
||||||
.resultSet<i4.$UserEntityTable>('user_entity'),
|
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
joinBuilder: joinBuilder,
|
joinBuilder: joinBuilder,
|
||||||
$removeJoinBuilderFromRootComposer:
|
$removeJoinBuilderFromRootComposer:
|
||||||
@@ -103,24 +111,26 @@ class $$UserMetadataEntityTableOrderingComposer
|
|||||||
super.$addJoinBuilderToRootComposer,
|
super.$addJoinBuilderToRootComposer,
|
||||||
super.$removeJoinBuilderFromRootComposer,
|
super.$removeJoinBuilderFromRootComposer,
|
||||||
});
|
});
|
||||||
i0.ColumnOrderings<String> get preferences => $composableBuilder(
|
i0.ColumnOrderings<int> get key => $composableBuilder(
|
||||||
column: $table.preferences,
|
column: $table.key, builder: (column) => i0.ColumnOrderings(column));
|
||||||
builder: (column) => i0.ColumnOrderings(column));
|
|
||||||
|
|
||||||
i4.$$UserEntityTableOrderingComposer get userId {
|
i0.ColumnOrderings<i3.Uint8List> get value => $composableBuilder(
|
||||||
final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder(
|
column: $table.value, builder: (column) => i0.ColumnOrderings(column));
|
||||||
|
|
||||||
|
i5.$$UserEntityTableOrderingComposer get userId {
|
||||||
|
final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder(
|
||||||
composer: this,
|
composer: this,
|
||||||
getCurrentColumn: (t) => t.userId,
|
getCurrentColumn: (t) => t.userId,
|
||||||
referencedTable: i5.ReadDatabaseContainer($db)
|
referencedTable: i6.ReadDatabaseContainer($db)
|
||||||
.resultSet<i4.$UserEntityTable>('user_entity'),
|
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||||
getReferencedColumn: (t) => t.id,
|
getReferencedColumn: (t) => t.id,
|
||||||
builder: (joinBuilder,
|
builder: (joinBuilder,
|
||||||
{$addJoinBuilderToRootComposer,
|
{$addJoinBuilderToRootComposer,
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
$removeJoinBuilderFromRootComposer}) =>
|
||||||
i4.$$UserEntityTableOrderingComposer(
|
i5.$$UserEntityTableOrderingComposer(
|
||||||
$db: $db,
|
$db: $db,
|
||||||
$table: i5.ReadDatabaseContainer($db)
|
$table: i6.ReadDatabaseContainer($db)
|
||||||
.resultSet<i4.$UserEntityTable>('user_entity'),
|
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
joinBuilder: joinBuilder,
|
joinBuilder: joinBuilder,
|
||||||
$removeJoinBuilderFromRootComposer:
|
$removeJoinBuilderFromRootComposer:
|
||||||
@@ -139,24 +149,27 @@ class $$UserMetadataEntityTableAnnotationComposer
|
|||||||
super.$addJoinBuilderToRootComposer,
|
super.$addJoinBuilderToRootComposer,
|
||||||
super.$removeJoinBuilderFromRootComposer,
|
super.$removeJoinBuilderFromRootComposer,
|
||||||
});
|
});
|
||||||
i0.GeneratedColumnWithTypeConverter<i2.UserPreferences, String>
|
i0.GeneratedColumnWithTypeConverter<i2.UserMetadataKey, int> get key =>
|
||||||
get preferences => $composableBuilder(
|
$composableBuilder(column: $table.key, builder: (column) => column);
|
||||||
column: $table.preferences, builder: (column) => column);
|
|
||||||
|
|
||||||
i4.$$UserEntityTableAnnotationComposer get userId {
|
i0.GeneratedColumnWithTypeConverter<Map<String, Object?>, i3.Uint8List>
|
||||||
final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
|
get value =>
|
||||||
|
$composableBuilder(column: $table.value, builder: (column) => column);
|
||||||
|
|
||||||
|
i5.$$UserEntityTableAnnotationComposer get userId {
|
||||||
|
final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
|
||||||
composer: this,
|
composer: this,
|
||||||
getCurrentColumn: (t) => t.userId,
|
getCurrentColumn: (t) => t.userId,
|
||||||
referencedTable: i5.ReadDatabaseContainer($db)
|
referencedTable: i6.ReadDatabaseContainer($db)
|
||||||
.resultSet<i4.$UserEntityTable>('user_entity'),
|
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||||
getReferencedColumn: (t) => t.id,
|
getReferencedColumn: (t) => t.id,
|
||||||
builder: (joinBuilder,
|
builder: (joinBuilder,
|
||||||
{$addJoinBuilderToRootComposer,
|
{$addJoinBuilderToRootComposer,
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
$removeJoinBuilderFromRootComposer}) =>
|
||||||
i4.$$UserEntityTableAnnotationComposer(
|
i5.$$UserEntityTableAnnotationComposer(
|
||||||
$db: $db,
|
$db: $db,
|
||||||
$table: i5.ReadDatabaseContainer($db)
|
$table: i6.ReadDatabaseContainer($db)
|
||||||
.resultSet<i4.$UserEntityTable>('user_entity'),
|
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
joinBuilder: joinBuilder,
|
joinBuilder: joinBuilder,
|
||||||
$removeJoinBuilderFromRootComposer:
|
$removeJoinBuilderFromRootComposer:
|
||||||
@@ -193,19 +206,23 @@ class $$UserMetadataEntityTableTableManager extends i0.RootTableManager<
|
|||||||
$db: db, $table: table),
|
$db: db, $table: table),
|
||||||
updateCompanionCallback: ({
|
updateCompanionCallback: ({
|
||||||
i0.Value<String> userId = const i0.Value.absent(),
|
i0.Value<String> userId = const i0.Value.absent(),
|
||||||
i0.Value<i2.UserPreferences> preferences = const i0.Value.absent(),
|
i0.Value<i2.UserMetadataKey> key = const i0.Value.absent(),
|
||||||
|
i0.Value<Map<String, Object?>> value = const i0.Value.absent(),
|
||||||
}) =>
|
}) =>
|
||||||
i1.UserMetadataEntityCompanion(
|
i1.UserMetadataEntityCompanion(
|
||||||
userId: userId,
|
userId: userId,
|
||||||
preferences: preferences,
|
key: key,
|
||||||
|
value: value,
|
||||||
),
|
),
|
||||||
createCompanionCallback: ({
|
createCompanionCallback: ({
|
||||||
required String userId,
|
required String userId,
|
||||||
required i2.UserPreferences preferences,
|
required i2.UserMetadataKey key,
|
||||||
|
required Map<String, Object?> value,
|
||||||
}) =>
|
}) =>
|
||||||
i1.UserMetadataEntityCompanion.insert(
|
i1.UserMetadataEntityCompanion.insert(
|
||||||
userId: userId,
|
userId: userId,
|
||||||
preferences: preferences,
|
key: key,
|
||||||
|
value: value,
|
||||||
),
|
),
|
||||||
withReferenceMapper: (p0) => p0
|
withReferenceMapper: (p0) => p0
|
||||||
.map((e) => (
|
.map((e) => (
|
||||||
@@ -266,7 +283,7 @@ typedef $$UserMetadataEntityTableProcessedTableManager
|
|||||||
i1.UserMetadataEntityData,
|
i1.UserMetadataEntityData,
|
||||||
i0.PrefetchHooks Function({bool userId})>;
|
i0.PrefetchHooks Function({bool userId})>;
|
||||||
|
|
||||||
class $UserMetadataEntityTable extends i3.UserMetadataEntity
|
class $UserMetadataEntityTable extends i4.UserMetadataEntity
|
||||||
with i0.TableInfo<$UserMetadataEntityTable, i1.UserMetadataEntityData> {
|
with i0.TableInfo<$UserMetadataEntityTable, i1.UserMetadataEntityData> {
|
||||||
@override
|
@override
|
||||||
final i0.GeneratedDatabase attachedDatabase;
|
final i0.GeneratedDatabase attachedDatabase;
|
||||||
@@ -282,14 +299,20 @@ class $UserMetadataEntityTable extends i3.UserMetadataEntity
|
|||||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||||
'REFERENCES user_entity (id) ON DELETE CASCADE'));
|
'REFERENCES user_entity (id) ON DELETE CASCADE'));
|
||||||
@override
|
@override
|
||||||
late final i0.GeneratedColumnWithTypeConverter<i2.UserPreferences, String>
|
late final i0.GeneratedColumnWithTypeConverter<i2.UserMetadataKey, int> key =
|
||||||
preferences = i0.GeneratedColumn<String>(
|
i0.GeneratedColumn<int>('key', aliasedName, false,
|
||||||
'preferences', aliasedName, false,
|
type: i0.DriftSqlType.int, requiredDuringInsert: true)
|
||||||
type: i0.DriftSqlType.string, requiredDuringInsert: true)
|
.withConverter<i2.UserMetadataKey>(
|
||||||
.withConverter<i2.UserPreferences>(
|
i1.$UserMetadataEntityTable.$converterkey);
|
||||||
i1.$UserMetadataEntityTable.$converterpreferences);
|
|
||||||
@override
|
@override
|
||||||
List<i0.GeneratedColumn> get $columns => [userId, preferences];
|
late final i0
|
||||||
|
.GeneratedColumnWithTypeConverter<Map<String, Object?>, i3.Uint8List>
|
||||||
|
value = i0.GeneratedColumn<i3.Uint8List>('value', aliasedName, false,
|
||||||
|
type: i0.DriftSqlType.blob, requiredDuringInsert: true)
|
||||||
|
.withConverter<Map<String, Object?>>(
|
||||||
|
i1.$UserMetadataEntityTable.$convertervalue);
|
||||||
|
@override
|
||||||
|
List<i0.GeneratedColumn> get $columns => [userId, key, value];
|
||||||
@override
|
@override
|
||||||
String get aliasedName => _alias ?? actualTableName;
|
String get aliasedName => _alias ?? actualTableName;
|
||||||
@override
|
@override
|
||||||
@@ -311,7 +334,7 @@ class $UserMetadataEntityTable extends i3.UserMetadataEntity
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Set<i0.GeneratedColumn> get $primaryKey => {userId};
|
Set<i0.GeneratedColumn> get $primaryKey => {userId, key};
|
||||||
@override
|
@override
|
||||||
i1.UserMetadataEntityData map(Map<String, dynamic> data,
|
i1.UserMetadataEntityData map(Map<String, dynamic> data,
|
||||||
{String? tablePrefix}) {
|
{String? tablePrefix}) {
|
||||||
@@ -319,9 +342,12 @@ class $UserMetadataEntityTable extends i3.UserMetadataEntity
|
|||||||
return i1.UserMetadataEntityData(
|
return i1.UserMetadataEntityData(
|
||||||
userId: attachedDatabase.typeMapping
|
userId: attachedDatabase.typeMapping
|
||||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}user_id'])!,
|
.read(i0.DriftSqlType.string, data['${effectivePrefix}user_id'])!,
|
||||||
preferences: i1.$UserMetadataEntityTable.$converterpreferences.fromSql(
|
key: i1.$UserMetadataEntityTable.$converterkey.fromSql(attachedDatabase
|
||||||
attachedDatabase.typeMapping.read(
|
.typeMapping
|
||||||
i0.DriftSqlType.string, data['${effectivePrefix}preferences'])!),
|
.read(i0.DriftSqlType.int, data['${effectivePrefix}key'])!),
|
||||||
|
value: i1.$UserMetadataEntityTable.$convertervalue.fromSql(
|
||||||
|
attachedDatabase.typeMapping
|
||||||
|
.read(i0.DriftSqlType.blob, data['${effectivePrefix}value'])!),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,8 +356,11 @@ class $UserMetadataEntityTable extends i3.UserMetadataEntity
|
|||||||
return $UserMetadataEntityTable(attachedDatabase, alias);
|
return $UserMetadataEntityTable(attachedDatabase, alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
static i0.JsonTypeConverter2<i2.UserPreferences, String, Object?>
|
static i0.JsonTypeConverter2<i2.UserMetadataKey, int, int> $converterkey =
|
||||||
$converterpreferences = i3.userPreferenceConverter;
|
const i0.EnumIndexConverter<i2.UserMetadataKey>(
|
||||||
|
i2.UserMetadataKey.values);
|
||||||
|
static i0.JsonTypeConverter2<Map<String, Object?>, i3.Uint8List, Object?>
|
||||||
|
$convertervalue = i4.userMetadataConverter;
|
||||||
@override
|
@override
|
||||||
bool get withoutRowId => true;
|
bool get withoutRowId => true;
|
||||||
@override
|
@override
|
||||||
@@ -341,16 +370,21 @@ class $UserMetadataEntityTable extends i3.UserMetadataEntity
|
|||||||
class UserMetadataEntityData extends i0.DataClass
|
class UserMetadataEntityData extends i0.DataClass
|
||||||
implements i0.Insertable<i1.UserMetadataEntityData> {
|
implements i0.Insertable<i1.UserMetadataEntityData> {
|
||||||
final String userId;
|
final String userId;
|
||||||
final i2.UserPreferences preferences;
|
final i2.UserMetadataKey key;
|
||||||
|
final Map<String, Object?> value;
|
||||||
const UserMetadataEntityData(
|
const UserMetadataEntityData(
|
||||||
{required this.userId, required this.preferences});
|
{required this.userId, required this.key, required this.value});
|
||||||
@override
|
@override
|
||||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||||
final map = <String, i0.Expression>{};
|
final map = <String, i0.Expression>{};
|
||||||
map['user_id'] = i0.Variable<String>(userId);
|
map['user_id'] = i0.Variable<String>(userId);
|
||||||
{
|
{
|
||||||
map['preferences'] = i0.Variable<String>(
|
map['key'] = i0.Variable<int>(
|
||||||
i1.$UserMetadataEntityTable.$converterpreferences.toSql(preferences));
|
i1.$UserMetadataEntityTable.$converterkey.toSql(key));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
map['value'] = i0.Variable<i3.Uint8List>(
|
||||||
|
i1.$UserMetadataEntityTable.$convertervalue.toSql(value));
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
@@ -360,8 +394,10 @@ class UserMetadataEntityData extends i0.DataClass
|
|||||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||||
return UserMetadataEntityData(
|
return UserMetadataEntityData(
|
||||||
userId: serializer.fromJson<String>(json['userId']),
|
userId: serializer.fromJson<String>(json['userId']),
|
||||||
preferences: i1.$UserMetadataEntityTable.$converterpreferences
|
key: i1.$UserMetadataEntityTable.$converterkey
|
||||||
.fromJson(serializer.fromJson<Object?>(json['preferences'])),
|
.fromJson(serializer.fromJson<int>(json['key'])),
|
||||||
|
value: i1.$UserMetadataEntityTable.$convertervalue
|
||||||
|
.fromJson(serializer.fromJson<Object?>(json['value'])),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@override
|
@override
|
||||||
@@ -369,24 +405,28 @@ class UserMetadataEntityData extends i0.DataClass
|
|||||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||||
return <String, dynamic>{
|
return <String, dynamic>{
|
||||||
'userId': serializer.toJson<String>(userId),
|
'userId': serializer.toJson<String>(userId),
|
||||||
'preferences': serializer.toJson<Object?>(i1
|
'key': serializer
|
||||||
.$UserMetadataEntityTable.$converterpreferences
|
.toJson<int>(i1.$UserMetadataEntityTable.$converterkey.toJson(key)),
|
||||||
.toJson(preferences)),
|
'value': serializer.toJson<Object?>(
|
||||||
|
i1.$UserMetadataEntityTable.$convertervalue.toJson(value)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
i1.UserMetadataEntityData copyWith(
|
i1.UserMetadataEntityData copyWith(
|
||||||
{String? userId, i2.UserPreferences? preferences}) =>
|
{String? userId,
|
||||||
|
i2.UserMetadataKey? key,
|
||||||
|
Map<String, Object?>? value}) =>
|
||||||
i1.UserMetadataEntityData(
|
i1.UserMetadataEntityData(
|
||||||
userId: userId ?? this.userId,
|
userId: userId ?? this.userId,
|
||||||
preferences: preferences ?? this.preferences,
|
key: key ?? this.key,
|
||||||
|
value: value ?? this.value,
|
||||||
);
|
);
|
||||||
UserMetadataEntityData copyWithCompanion(
|
UserMetadataEntityData copyWithCompanion(
|
||||||
i1.UserMetadataEntityCompanion data) {
|
i1.UserMetadataEntityCompanion data) {
|
||||||
return UserMetadataEntityData(
|
return UserMetadataEntityData(
|
||||||
userId: data.userId.present ? data.userId.value : this.userId,
|
userId: data.userId.present ? data.userId.value : this.userId,
|
||||||
preferences:
|
key: data.key.present ? data.key.value : this.key,
|
||||||
data.preferences.present ? data.preferences.value : this.preferences,
|
value: data.value.present ? data.value.value : this.value,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,49 +434,60 @@ class UserMetadataEntityData extends i0.DataClass
|
|||||||
String toString() {
|
String toString() {
|
||||||
return (StringBuffer('UserMetadataEntityData(')
|
return (StringBuffer('UserMetadataEntityData(')
|
||||||
..write('userId: $userId, ')
|
..write('userId: $userId, ')
|
||||||
..write('preferences: $preferences')
|
..write('key: $key, ')
|
||||||
|
..write('value: $value')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(userId, preferences);
|
int get hashCode => Object.hash(userId, key, value);
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
(other is i1.UserMetadataEntityData &&
|
(other is i1.UserMetadataEntityData &&
|
||||||
other.userId == this.userId &&
|
other.userId == this.userId &&
|
||||||
other.preferences == this.preferences);
|
other.key == this.key &&
|
||||||
|
other.value == this.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
class UserMetadataEntityCompanion
|
class UserMetadataEntityCompanion
|
||||||
extends i0.UpdateCompanion<i1.UserMetadataEntityData> {
|
extends i0.UpdateCompanion<i1.UserMetadataEntityData> {
|
||||||
final i0.Value<String> userId;
|
final i0.Value<String> userId;
|
||||||
final i0.Value<i2.UserPreferences> preferences;
|
final i0.Value<i2.UserMetadataKey> key;
|
||||||
|
final i0.Value<Map<String, Object?>> value;
|
||||||
const UserMetadataEntityCompanion({
|
const UserMetadataEntityCompanion({
|
||||||
this.userId = const i0.Value.absent(),
|
this.userId = const i0.Value.absent(),
|
||||||
this.preferences = const i0.Value.absent(),
|
this.key = const i0.Value.absent(),
|
||||||
|
this.value = const i0.Value.absent(),
|
||||||
});
|
});
|
||||||
UserMetadataEntityCompanion.insert({
|
UserMetadataEntityCompanion.insert({
|
||||||
required String userId,
|
required String userId,
|
||||||
required i2.UserPreferences preferences,
|
required i2.UserMetadataKey key,
|
||||||
|
required Map<String, Object?> value,
|
||||||
}) : userId = i0.Value(userId),
|
}) : userId = i0.Value(userId),
|
||||||
preferences = i0.Value(preferences);
|
key = i0.Value(key),
|
||||||
|
value = i0.Value(value);
|
||||||
static i0.Insertable<i1.UserMetadataEntityData> custom({
|
static i0.Insertable<i1.UserMetadataEntityData> custom({
|
||||||
i0.Expression<String>? userId,
|
i0.Expression<String>? userId,
|
||||||
i0.Expression<String>? preferences,
|
i0.Expression<int>? key,
|
||||||
|
i0.Expression<i3.Uint8List>? value,
|
||||||
}) {
|
}) {
|
||||||
return i0.RawValuesInsertable({
|
return i0.RawValuesInsertable({
|
||||||
if (userId != null) 'user_id': userId,
|
if (userId != null) 'user_id': userId,
|
||||||
if (preferences != null) 'preferences': preferences,
|
if (key != null) 'key': key,
|
||||||
|
if (value != null) 'value': value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
i1.UserMetadataEntityCompanion copyWith(
|
i1.UserMetadataEntityCompanion copyWith(
|
||||||
{i0.Value<String>? userId, i0.Value<i2.UserPreferences>? preferences}) {
|
{i0.Value<String>? userId,
|
||||||
|
i0.Value<i2.UserMetadataKey>? key,
|
||||||
|
i0.Value<Map<String, Object?>>? value}) {
|
||||||
return i1.UserMetadataEntityCompanion(
|
return i1.UserMetadataEntityCompanion(
|
||||||
userId: userId ?? this.userId,
|
userId: userId ?? this.userId,
|
||||||
preferences: preferences ?? this.preferences,
|
key: key ?? this.key,
|
||||||
|
value: value ?? this.value,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -446,10 +497,13 @@ class UserMetadataEntityCompanion
|
|||||||
if (userId.present) {
|
if (userId.present) {
|
||||||
map['user_id'] = i0.Variable<String>(userId.value);
|
map['user_id'] = i0.Variable<String>(userId.value);
|
||||||
}
|
}
|
||||||
if (preferences.present) {
|
if (key.present) {
|
||||||
map['preferences'] = i0.Variable<String>(i1
|
map['key'] = i0.Variable<int>(
|
||||||
.$UserMetadataEntityTable.$converterpreferences
|
i1.$UserMetadataEntityTable.$converterkey.toSql(key.value));
|
||||||
.toSql(preferences.value));
|
}
|
||||||
|
if (value.present) {
|
||||||
|
map['value'] = i0.Variable<i3.Uint8List>(
|
||||||
|
i1.$UserMetadataEntityTable.$convertervalue.toSql(value.value));
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
@@ -458,7 +512,8 @@ class UserMetadataEntityCompanion
|
|||||||
String toString() {
|
String toString() {
|
||||||
return (StringBuffer('UserMetadataEntityCompanion(')
|
return (StringBuffer('UserMetadataEntityCompanion(')
|
||||||
..write('userId: $userId, ')
|
..write('userId: $userId, ')
|
||||||
..write('preferences: $preferences')
|
..write('key: $key, ')
|
||||||
|
..write('value: $value')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,42 @@ class RemoteAssetRepository extends DriftDatabaseRepository {
|
|||||||
.getSingleOrNull();
|
.getSingleOrNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<(String, String)>> getPlaces() {
|
||||||
|
final asset = Subquery(
|
||||||
|
_db.remoteAssetEntity.select()
|
||||||
|
..orderBy([(row) => OrderingTerm.desc(row.createdAt)]),
|
||||||
|
"asset",
|
||||||
|
);
|
||||||
|
|
||||||
|
final query = asset.selectOnly().join([
|
||||||
|
innerJoin(
|
||||||
|
_db.remoteExifEntity,
|
||||||
|
_db.remoteExifEntity.assetId
|
||||||
|
.equalsExp(asset.ref(_db.remoteAssetEntity.id)),
|
||||||
|
useColumns: false,
|
||||||
|
),
|
||||||
|
])
|
||||||
|
..addColumns([
|
||||||
|
_db.remoteExifEntity.city,
|
||||||
|
_db.remoteExifEntity.assetId,
|
||||||
|
])
|
||||||
|
..where(
|
||||||
|
_db.remoteExifEntity.city.isNotNull() &
|
||||||
|
asset.ref(_db.remoteAssetEntity.deletedAt).isNull() &
|
||||||
|
asset
|
||||||
|
.ref(_db.remoteAssetEntity.visibility)
|
||||||
|
.equals(AssetVisibility.timeline.index),
|
||||||
|
)
|
||||||
|
..groupBy([_db.remoteExifEntity.city])
|
||||||
|
..orderBy([OrderingTerm.asc(_db.remoteExifEntity.city)]);
|
||||||
|
|
||||||
|
return query.map((row) {
|
||||||
|
final assetId = row.read(_db.remoteExifEntity.assetId);
|
||||||
|
final city = row.read(_db.remoteExifEntity.city);
|
||||||
|
return (city!, assetId!);
|
||||||
|
}).get();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> updateFavorite(List<String> ids, bool isFavorite) {
|
Future<void> updateFavorite(List<String> ids, bool isFavorite) {
|
||||||
return _db.batch((batch) async {
|
return _db.batch((batch) async {
|
||||||
for (final id in ids) {
|
for (final id in ids) {
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ class SyncApiRepository {
|
|||||||
SyncRequestType.memoryToAssetsV1,
|
SyncRequestType.memoryToAssetsV1,
|
||||||
SyncRequestType.stacksV1,
|
SyncRequestType.stacksV1,
|
||||||
SyncRequestType.partnerStacksV1,
|
SyncRequestType.partnerStacksV1,
|
||||||
|
SyncRequestType.userMetadataV1,
|
||||||
],
|
],
|
||||||
).toJson(),
|
).toJson(),
|
||||||
);
|
);
|
||||||
@@ -170,6 +171,8 @@ const _kResponseMap = <SyncEntityType, Function(Object)>{
|
|||||||
SyncEntityType.partnerStackV1: SyncStackV1.fromJson,
|
SyncEntityType.partnerStackV1: SyncStackV1.fromJson,
|
||||||
SyncEntityType.partnerStackBackfillV1: SyncStackV1.fromJson,
|
SyncEntityType.partnerStackBackfillV1: SyncStackV1.fromJson,
|
||||||
SyncEntityType.partnerStackDeleteV1: SyncStackDeleteV1.fromJson,
|
SyncEntityType.partnerStackDeleteV1: SyncStackDeleteV1.fromJson,
|
||||||
|
SyncEntityType.userMetadataV1: SyncUserMetadataV1.fromJson,
|
||||||
|
SyncEntityType.userMetadataDeleteV1: SyncUserMetadataDeleteV1.fromJson,
|
||||||
};
|
};
|
||||||
|
|
||||||
class _SyncAckV1 {
|
class _SyncAckV1 {
|
||||||
|
|||||||
@@ -4,16 +4,18 @@ import 'package:drift/drift.dart';
|
|||||||
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/memory.model.dart';
|
import 'package:immich_mobile/domain/models/memory.model.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/user_metadata.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:openapi/api.dart' as api show AssetVisibility, AlbumUserRole;
|
import 'package:openapi/api.dart' as api show AssetVisibility, AlbumUserRole;
|
||||||
@@ -134,6 +136,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
thumbHash: Value(asset.thumbhash),
|
thumbHash: Value(asset.thumbhash),
|
||||||
deletedAt: Value(asset.deletedAt),
|
deletedAt: Value(asset.deletedAt),
|
||||||
visibility: Value(asset.visibility.toAssetVisibility()),
|
visibility: Value(asset.visibility.toAssetVisibility()),
|
||||||
|
livePhotoVideoId: Value(asset.livePhotoVideoId),
|
||||||
);
|
);
|
||||||
|
|
||||||
batch.insert(
|
batch.insert(
|
||||||
@@ -460,6 +463,53 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> updateUserMetadatasV1(
|
||||||
|
Iterable<SyncUserMetadataV1> data,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
await _db.batch((batch) {
|
||||||
|
for (final userMetadata in data) {
|
||||||
|
final companion = UserMetadataEntityCompanion(
|
||||||
|
value: Value(userMetadata.value as Map<String, Object?>),
|
||||||
|
);
|
||||||
|
|
||||||
|
batch.insert(
|
||||||
|
_db.userMetadataEntity,
|
||||||
|
companion.copyWith(
|
||||||
|
userId: Value(userMetadata.userId),
|
||||||
|
key: Value(userMetadata.key.toUserMetadataKey()),
|
||||||
|
),
|
||||||
|
onConflict: DoUpdate((_) => companion),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error, stack) {
|
||||||
|
_logger.severe('Error: deleteUserMetadatasV1', error, stack);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> deleteUserMetadatasV1(
|
||||||
|
Iterable<SyncUserMetadataDeleteV1> data,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
await _db.batch((batch) {
|
||||||
|
for (final userMetadata in data) {
|
||||||
|
batch.delete(
|
||||||
|
_db.userMetadataEntity,
|
||||||
|
UserMetadataEntityCompanion(
|
||||||
|
userId: Value(userMetadata.userId),
|
||||||
|
key: Value(userMetadata.key.toUserMetadataKey()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error, stack) {
|
||||||
|
_logger.severe('Error: deleteUserMetadatasV1', error, stack);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension on AssetTypeEnum {
|
extension on AssetTypeEnum {
|
||||||
@@ -505,6 +555,15 @@ extension on api.AssetVisibility {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension on String {
|
||||||
|
UserMetadataKey toUserMetadataKey() => switch (this) {
|
||||||
|
"onboarding" => UserMetadataKey.onboarding,
|
||||||
|
"preferences" => UserMetadataKey.preferences,
|
||||||
|
"license" => UserMetadataKey.license,
|
||||||
|
_ => throw Exception('Unknown UserMetadataKey value: $this'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
extension on String {
|
extension on String {
|
||||||
Duration? toDuration() {
|
Duration? toDuration() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||||||
height: row.height,
|
height: row.height,
|
||||||
isFavorite: row.isFavorite,
|
isFavorite: row.isFavorite,
|
||||||
durationInSeconds: row.durationInSeconds,
|
durationInSeconds: row.durationInSeconds,
|
||||||
|
livePhotoVideoId: row.livePhotoVideoId,
|
||||||
)
|
)
|
||||||
: LocalAsset(
|
: LocalAsset(
|
||||||
id: row.localId!,
|
id: row.localId!,
|
||||||
@@ -302,6 +303,84 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||||||
groupBy: groupBy,
|
groupBy: groupBy,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TimelineQuery place(String place, GroupAssetsBy groupBy) => (
|
||||||
|
bucketSource: () => _watchPlaceBucket(
|
||||||
|
place,
|
||||||
|
groupBy: groupBy,
|
||||||
|
),
|
||||||
|
assetSource: (offset, count) => _getPlaceBucketAssets(
|
||||||
|
place,
|
||||||
|
offset: offset,
|
||||||
|
count: count,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Stream<List<Bucket>> _watchPlaceBucket(
|
||||||
|
String place, {
|
||||||
|
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||||
|
}) {
|
||||||
|
if (groupBy == GroupAssetsBy.none) {
|
||||||
|
// TODO: implement GroupAssetBy for place
|
||||||
|
throw UnsupportedError(
|
||||||
|
"GroupAssetsBy.none is not supported for watchPlaceBucket",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final assetCountExp = _db.remoteAssetEntity.id.count();
|
||||||
|
final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy);
|
||||||
|
|
||||||
|
final query = _db.remoteAssetEntity.selectOnly()
|
||||||
|
..addColumns([assetCountExp, dateExp])
|
||||||
|
..join([
|
||||||
|
innerJoin(
|
||||||
|
_db.remoteExifEntity,
|
||||||
|
_db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
|
||||||
|
useColumns: false,
|
||||||
|
),
|
||||||
|
])
|
||||||
|
..where(
|
||||||
|
_db.remoteExifEntity.city.equals(place) &
|
||||||
|
_db.remoteAssetEntity.deletedAt.isNull() &
|
||||||
|
_db.remoteAssetEntity.visibility
|
||||||
|
.equalsValue(AssetVisibility.timeline),
|
||||||
|
)
|
||||||
|
..groupBy([dateExp])
|
||||||
|
..orderBy([OrderingTerm.desc(dateExp)]);
|
||||||
|
|
||||||
|
return query.map((row) {
|
||||||
|
final timeline = row.read(dateExp)!.dateFmt(groupBy);
|
||||||
|
final assetCount = row.read(assetCountExp)!;
|
||||||
|
return TimeBucket(date: timeline, assetCount: assetCount);
|
||||||
|
}).watch();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<BaseAsset>> _getPlaceBucketAssets(
|
||||||
|
String place, {
|
||||||
|
required int offset,
|
||||||
|
required int count,
|
||||||
|
}) {
|
||||||
|
final query = _db.remoteAssetEntity.select().join(
|
||||||
|
[
|
||||||
|
innerJoin(
|
||||||
|
_db.remoteExifEntity,
|
||||||
|
_db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
|
||||||
|
useColumns: false,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
..where(
|
||||||
|
_db.remoteAssetEntity.deletedAt.isNull() &
|
||||||
|
_db.remoteAssetEntity.visibility
|
||||||
|
.equalsValue(AssetVisibility.timeline) &
|
||||||
|
_db.remoteExifEntity.city.equals(place),
|
||||||
|
)
|
||||||
|
..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)])
|
||||||
|
..limit(count, offset: offset);
|
||||||
|
return query
|
||||||
|
.map((row) => row.readTable(_db.remoteAssetEntity).toDto())
|
||||||
|
.get();
|
||||||
|
}
|
||||||
|
|
||||||
TimelineQuery _remoteQueryBuilder({
|
TimelineQuery _remoteQueryBuilder({
|
||||||
required Expression<bool> Function($RemoteAssetEntityTable row) filter,
|
required Expression<bool> Function($RemoteAssetEntityTable row) filter,
|
||||||
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/user_metadata.model.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
|
|
||||||
|
class DriftUserMetadataRepository extends DriftDatabaseRepository {
|
||||||
|
final Drift _db;
|
||||||
|
const DriftUserMetadataRepository(this._db) : super(_db);
|
||||||
|
|
||||||
|
Future<List<UserMetadata>> getUserMetadata(String userId) {
|
||||||
|
final query = _db.userMetadataEntity.select()
|
||||||
|
..where((e) => e.userId.equals(userId));
|
||||||
|
|
||||||
|
return query.map((userMetadata) {
|
||||||
|
return userMetadata.toDto();
|
||||||
|
}).get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension on UserMetadataEntityData {
|
||||||
|
UserMetadata toDto() => switch (key) {
|
||||||
|
UserMetadataKey.onboarding => UserMetadata(
|
||||||
|
userId: userId,
|
||||||
|
key: key,
|
||||||
|
onboarding: Onboarding.fromMap(value),
|
||||||
|
),
|
||||||
|
UserMetadataKey.preferences => UserMetadata(
|
||||||
|
userId: userId,
|
||||||
|
key: key,
|
||||||
|
preferences: Preferences.fromMap(value),
|
||||||
|
),
|
||||||
|
UserMetadataKey.license => UserMetadata(
|
||||||
|
userId: userId,
|
||||||
|
key: key,
|
||||||
|
license: License.fromMap(value),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -256,9 +256,7 @@ class _PlacesCollectionCard extends StatelessWidget {
|
|||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => context.pushRoute(
|
onTap: () => context.pushRoute(
|
||||||
PlacesCollectionRoute(
|
DriftPlaceRoute(currentLocation: null),
|
||||||
currentLocation: null,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|||||||
@@ -0,0 +1,190 @@
|
|||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
|
import 'package:immich_mobile/pages/common/large_leading_tile.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
|
||||||
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
|
import 'package:immich_mobile/widgets/common/search_field.dart';
|
||||||
|
import 'package:immich_mobile/widgets/map/map_thumbnail.dart';
|
||||||
|
import 'package:maplibre_gl/maplibre_gl.dart';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class DriftPlacePage extends StatelessWidget {
|
||||||
|
const DriftPlacePage({super.key, this.currentLocation});
|
||||||
|
|
||||||
|
final LatLng? currentLocation;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final ValueNotifier<String?> search = ValueNotifier(null);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
body: CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
_PlaceSliverAppBar(search: search),
|
||||||
|
_Map(search: search, currentLocation: currentLocation),
|
||||||
|
_PlaceList(search: search),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PlaceSliverAppBar extends StatelessWidget {
|
||||||
|
const _PlaceSliverAppBar({required this.search});
|
||||||
|
|
||||||
|
final ValueNotifier<String?> search;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final searchFocusNode = FocusNode();
|
||||||
|
|
||||||
|
return SliverAppBar(
|
||||||
|
floating: true,
|
||||||
|
pinned: true,
|
||||||
|
snap: false,
|
||||||
|
backgroundColor: context.colorScheme.surfaceContainer,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(5)),
|
||||||
|
),
|
||||||
|
automaticallyImplyLeading: search.value == null,
|
||||||
|
centerTitle: true,
|
||||||
|
title: search.value != null
|
||||||
|
? SearchField(
|
||||||
|
focusNode: searchFocusNode,
|
||||||
|
onTapOutside: (_) => searchFocusNode.unfocus(),
|
||||||
|
onChanged: (value) => search.value = value,
|
||||||
|
filled: true,
|
||||||
|
hintText: 'filter_places'.t(context: context),
|
||||||
|
autofocus: true,
|
||||||
|
)
|
||||||
|
: Text('places'.t(context: context)),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(search.value != null ? Icons.close : Icons.search),
|
||||||
|
onPressed: () {
|
||||||
|
search.value = search.value == null ? '' : null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _Map extends StatelessWidget {
|
||||||
|
const _Map({required this.search, this.currentLocation});
|
||||||
|
|
||||||
|
final ValueNotifier<String?> search;
|
||||||
|
final LatLng? currentLocation;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return search.value == null
|
||||||
|
? SliverPadding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
sliver: SliverToBoxAdapter(
|
||||||
|
child: SizedBox(
|
||||||
|
height: 200,
|
||||||
|
width: context.width,
|
||||||
|
// TODO: migrate to DriftMapRoute after merging #19898
|
||||||
|
child: MapThumbnail(
|
||||||
|
onTap: (_, __) => context
|
||||||
|
.pushRoute(MapRoute(initialLocation: currentLocation)),
|
||||||
|
zoom: 8,
|
||||||
|
centre: currentLocation ??
|
||||||
|
const LatLng(
|
||||||
|
21.44950,
|
||||||
|
-157.91959,
|
||||||
|
),
|
||||||
|
showAttribution: false,
|
||||||
|
themeMode:
|
||||||
|
context.isDarkTheme ? ThemeMode.dark : ThemeMode.light,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PlaceList extends ConsumerWidget {
|
||||||
|
const _PlaceList({required this.search});
|
||||||
|
|
||||||
|
final ValueNotifier<String?> search;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final places = ref.watch(placesProvider);
|
||||||
|
|
||||||
|
return places.when(
|
||||||
|
loading: () => const SliverToBoxAdapter(
|
||||||
|
child: Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(20.0),
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
error: (error, stack) => SliverToBoxAdapter(
|
||||||
|
child: Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(20.0),
|
||||||
|
child: Text(
|
||||||
|
'Error loading places: $error, stack: $stack',
|
||||||
|
style: TextStyle(
|
||||||
|
color: context.colorScheme.error,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
data: (places) {
|
||||||
|
if (search.value != null) {
|
||||||
|
places = places.where((place) {
|
||||||
|
return place.$1.toLowerCase().contains(search.value!.toLowerCase());
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return SliverList.builder(
|
||||||
|
itemCount: places.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final place = places[index];
|
||||||
|
return _PlaceTile(place: place);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PlaceTile extends StatelessWidget {
|
||||||
|
const _PlaceTile({required this.place});
|
||||||
|
|
||||||
|
final (String, String) place;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return LargeLeadingTile(
|
||||||
|
onTap: () => context.pushRoute(DriftPlaceDetailRoute(place: place.$1)),
|
||||||
|
title: Text(
|
||||||
|
place.$1,
|
||||||
|
style: context.textTheme.titleMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
leading: ClipRRect(
|
||||||
|
borderRadius: const BorderRadius.all(
|
||||||
|
Radius.circular(20),
|
||||||
|
),
|
||||||
|
child: Thumbnail(
|
||||||
|
size: const Size(80, 80),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
remoteId: place.$2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
|
import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class DriftPlaceDetailPage extends StatelessWidget {
|
||||||
|
final String place;
|
||||||
|
|
||||||
|
const DriftPlaceDetailPage({
|
||||||
|
super.key,
|
||||||
|
required this.place,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ProviderScope(
|
||||||
|
overrides: [
|
||||||
|
timelineServiceProvider.overrideWith(
|
||||||
|
(ref) {
|
||||||
|
final timelineService =
|
||||||
|
ref.watch(timelineFactoryProvider).place(place);
|
||||||
|
ref.onDispose(timelineService.dispose);
|
||||||
|
return timelineService;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: Timeline(
|
||||||
|
appBar: MesmerizingSliverAppBar(
|
||||||
|
title: place,
|
||||||
|
icon: Icons.location_on,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart';
|
||||||
|
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
|
||||||
|
|
||||||
|
class MotionPhotoActionButton extends ConsumerWidget {
|
||||||
|
const MotionPhotoActionButton({super.key, this.menuItem = true});
|
||||||
|
|
||||||
|
final bool menuItem;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final isPlaying = ref.watch(isPlayingMotionVideoProvider);
|
||||||
|
|
||||||
|
return BaseActionButton(
|
||||||
|
iconData: isPlaying
|
||||||
|
? Icons.motion_photos_pause_outlined
|
||||||
|
: Icons.play_circle_outline_rounded,
|
||||||
|
label: "play_motion_photo".t(context: context),
|
||||||
|
onPressed: ref.read(isPlayingMotionVideoProvider.notifier).toggle,
|
||||||
|
menuItem: menuItem,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ import 'package:immich_mobile/presentation/widgets/asset_viewer/top_app_bar.widg
|
|||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
|
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
|
||||||
|
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provider.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
|
||||||
@@ -165,7 +166,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||||||
void _onAssetChanged(int index) {
|
void _onAssetChanged(int index) {
|
||||||
final asset = ref.read(timelineServiceProvider).getAsset(index);
|
final asset = ref.read(timelineServiceProvider).getAsset(index);
|
||||||
ref.read(currentAssetNotifier.notifier).setAsset(asset);
|
ref.read(currentAssetNotifier.notifier).setAsset(asset);
|
||||||
if (asset.isVideo) {
|
if (asset.isVideo || asset.isMotionPhoto) {
|
||||||
ref.read(videoPlaybackValueProvider.notifier).reset();
|
ref.read(videoPlaybackValueProvider.notifier).reset();
|
||||||
ref.read(videoPlayerControlsProvider.notifier).pause();
|
ref.read(videoPlayerControlsProvider.notifier).pause();
|
||||||
}
|
}
|
||||||
@@ -473,11 +474,16 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onLongPress(_, __, ___) {
|
||||||
|
ref.read(isPlayingMotionVideoProvider.notifier).playing = true;
|
||||||
|
}
|
||||||
|
|
||||||
PhotoViewGalleryPageOptions _assetBuilder(BuildContext ctx, int index) {
|
PhotoViewGalleryPageOptions _assetBuilder(BuildContext ctx, int index) {
|
||||||
scaffoldContext ??= ctx;
|
scaffoldContext ??= ctx;
|
||||||
final asset = ref.read(timelineServiceProvider).getAsset(index);
|
final asset = ref.read(timelineServiceProvider).getAsset(index);
|
||||||
|
final isPlayingMotionVideo = ref.read(isPlayingMotionVideoProvider);
|
||||||
|
|
||||||
if (asset.isImage) {
|
if (asset.isImage && !isPlayingMotionVideo) {
|
||||||
return _imageBuilder(ctx, asset);
|
return _imageBuilder(ctx, asset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,6 +506,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||||||
onDragUpdate: _onDragUpdate,
|
onDragUpdate: _onDragUpdate,
|
||||||
onDragEnd: _onDragEnd,
|
onDragEnd: _onDragEnd,
|
||||||
onTapDown: _onTapDown,
|
onTapDown: _onTapDown,
|
||||||
|
onLongPressStart: asset.isMotionPhoto ? _onLongPress : null,
|
||||||
errorBuilder: (_, __, ___) => Container(
|
errorBuilder: (_, __, ___) => Container(
|
||||||
width: ctx.width,
|
width: ctx.width,
|
||||||
height: ctx.height,
|
height: ctx.height,
|
||||||
@@ -561,6 +568,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||||||
// Using multiple selectors to avoid unnecessary rebuilds for other state changes
|
// Using multiple selectors to avoid unnecessary rebuilds for other state changes
|
||||||
ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet));
|
ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet));
|
||||||
ref.watch(assetViewerProvider.select((s) => s.backgroundOpacity));
|
ref.watch(assetViewerProvider.select((s) => s.backgroundOpacity));
|
||||||
|
ref.watch(isPlayingMotionVideoProvider);
|
||||||
|
|
||||||
// Currently it is not possible to scroll the asset when the bottom sheet is open all the way.
|
// Currently it is not possible to scroll the asset when the bottom sheet is open all the way.
|
||||||
// Issue: https://github.com/flutter/flutter/issues/109037
|
// Issue: https://github.com/flutter/flutter/issues/109037
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
|||||||
import 'package:immich_mobile/domain/utils/event_stream.dart';
|
import 'package:immich_mobile/domain/utils/event_stream.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/favorite_action_button.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/action_buttons/favorite_action_button.widget.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/action_buttons/motion_photo_action_button.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
|
||||||
@@ -44,6 +45,7 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||||||
source: ActionSource.viewer,
|
source: ActionSource.viewer,
|
||||||
menuItem: true,
|
menuItem: true,
|
||||||
),
|
),
|
||||||
|
if (asset.isMotionPhoto) const MotionPhotoActionButton(menuItem: true),
|
||||||
const _KebabMenu(),
|
const _KebabMenu(),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -270,10 +270,7 @@ class NativeVideoViewer extends HookConsumerWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (videoController.playbackInfo?.status == PlaybackStatus.stopped &&
|
if (videoController.playbackInfo?.status == PlaybackStatus.stopped) {
|
||||||
!ref
|
|
||||||
.read(appSettingsServiceProvider)
|
|
||||||
.getSetting<bool>(AppSettingsEnum.loopVideo)) {
|
|
||||||
ref.read(isPlayingMotionVideoProvider.notifier).playing = false;
|
ref.read(isPlayingMotionVideoProvider.notifier).playing = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -310,7 +307,7 @@ class NativeVideoViewer extends HookConsumerWidget {
|
|||||||
final loopVideo = ref
|
final loopVideo = ref
|
||||||
.read(appSettingsServiceProvider)
|
.read(appSettingsServiceProvider)
|
||||||
.getSetting<bool>(AppSettingsEnum.loopVideo);
|
.getSetting<bool>(AppSettingsEnum.loopVideo);
|
||||||
nc.setLoop(loopVideo);
|
nc.setLoop(!asset.isMotionPhoto && loopVideo);
|
||||||
|
|
||||||
controller.value = nc;
|
controller.value = nc;
|
||||||
Timer(const Duration(milliseconds: 200), checkIfBuffering);
|
Timer(const Duration(milliseconds: 200), checkIfBuffering);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import 'package:immich_mobile/presentation/widgets/timeline/header.widget.dart';
|
|||||||
import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart';
|
import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/timeline/segment_builder.dart';
|
import 'package:immich_mobile/presentation/widgets/timeline/segment_builder.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart';
|
import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart';
|
||||||
|
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
|
||||||
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
|
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||||
@@ -171,6 +172,7 @@ class _AssetTileWidget extends ConsumerWidget {
|
|||||||
ref.read(multiSelectProvider.notifier).toggleAssetSelection(asset);
|
ref.read(multiSelectProvider.notifier).toggleAssetSelection(asset);
|
||||||
} else {
|
} else {
|
||||||
await ref.read(timelineServiceProvider).loadAssets(assetIndex, 1);
|
await ref.read(timelineServiceProvider).loadAssets(assetIndex, 1);
|
||||||
|
ref.read(isPlayingMotionVideoProvider.notifier).playing = false;
|
||||||
ctx.pushRoute(
|
ctx.pushRoute(
|
||||||
AssetViewerRoute(
|
AssetViewerRoute(
|
||||||
initialIndex: assetIndex,
|
initialIndex: assetIndex,
|
||||||
|
|||||||
@@ -18,3 +18,10 @@ final assetServiceProvider = Provider(
|
|||||||
localAssetRepository: ref.watch(localAssetRepository),
|
localAssetRepository: ref.watch(localAssetRepository),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final placesProvider = FutureProvider<List<(String, String)>>(
|
||||||
|
(ref) => AssetService(
|
||||||
|
remoteAssetRepository: ref.watch(remoteAssetRepositoryProvider),
|
||||||
|
localAssetRepository: ref.watch(localAssetRepository),
|
||||||
|
).getPlaces(),
|
||||||
|
);
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/user_metadata.repository.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||||
|
|
||||||
|
final userMetadataRepository = Provider<DriftUserMetadataRepository>(
|
||||||
|
(ref) => DriftUserMetadataRepository(ref.watch(driftProvider)),
|
||||||
|
);
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
@@ -10,6 +11,7 @@ import 'package:immich_mobile/entities/store.entity.dart';
|
|||||||
import 'package:immich_mobile/models/server_info/server_version.model.dart';
|
import 'package:immich_mobile/models/server_info/server_version.model.dart';
|
||||||
import 'package:immich_mobile/providers/asset.provider.dart';
|
import 'package:immich_mobile/providers/asset.provider.dart';
|
||||||
import 'package:immich_mobile/providers/auth.provider.dart';
|
import 'package:immich_mobile/providers/auth.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
||||||
import 'package:immich_mobile/providers/db.provider.dart';
|
import 'package:immich_mobile/providers/db.provider.dart';
|
||||||
import 'package:immich_mobile/providers/server_info.provider.dart';
|
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||||
import 'package:immich_mobile/services/api.service.dart';
|
import 'package:immich_mobile/services/api.service.dart';
|
||||||
@@ -106,6 +108,18 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
|
|||||||
final Debouncer _debounce =
|
final Debouncer _debounce =
|
||||||
Debouncer(interval: const Duration(milliseconds: 500));
|
Debouncer(interval: const Duration(milliseconds: 500));
|
||||||
|
|
||||||
|
final Debouncer _batchDebouncer = Debouncer(
|
||||||
|
interval: const Duration(seconds: 5),
|
||||||
|
maxWaitTime: const Duration(seconds: 10),
|
||||||
|
);
|
||||||
|
final List<dynamic> _batchedAssetUploadReady = [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_batchDebouncer.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
/// Connects websocket to server unless already connected
|
/// Connects websocket to server unless already connected
|
||||||
void connect() {
|
void connect() {
|
||||||
if (state.isConnected) return;
|
if (state.isConnected) return;
|
||||||
@@ -171,6 +185,7 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
|
|||||||
socket.on('on_asset_stack_update', _handleServerUpdates);
|
socket.on('on_asset_stack_update', _handleServerUpdates);
|
||||||
socket.on('on_asset_hidden', _handleOnAssetHidden);
|
socket.on('on_asset_hidden', _handleOnAssetHidden);
|
||||||
socket.on('on_new_release', _handleReleaseUpdates);
|
socket.on('on_new_release', _handleReleaseUpdates);
|
||||||
|
socket.on('AssetUploadReadyV1', _handleSyncAssetUploadReady);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint("[WEBSOCKET] Catch Websocket Error - ${e.toString()}");
|
debugPrint("[WEBSOCKET] Catch Websocket Error - ${e.toString()}");
|
||||||
}
|
}
|
||||||
@@ -180,6 +195,8 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
|
|||||||
void disconnect() {
|
void disconnect() {
|
||||||
debugPrint("Attempting to disconnect from websocket");
|
debugPrint("Attempting to disconnect from websocket");
|
||||||
|
|
||||||
|
_batchedAssetUploadReady.clear();
|
||||||
|
|
||||||
var socket = state.socket?.disconnect();
|
var socket = state.socket?.disconnect();
|
||||||
|
|
||||||
if (socket?.disconnected == true) {
|
if (socket?.disconnected == true) {
|
||||||
@@ -288,7 +305,7 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void handlePendingChanges() async {
|
Future<void> handlePendingChanges() async {
|
||||||
await _handlePendingUploaded();
|
await _handlePendingUploaded();
|
||||||
await _handlePendingDeletes();
|
await _handlePendingDeletes();
|
||||||
await _handlingPendingHidden();
|
await _handlingPendingHidden();
|
||||||
@@ -347,6 +364,29 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
|
|||||||
.read(serverInfoProvider.notifier)
|
.read(serverInfoProvider.notifier)
|
||||||
.handleNewRelease(serverVersion, releaseVersion);
|
.handleNewRelease(serverVersion, releaseVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _handleSyncAssetUploadReady(dynamic data) {
|
||||||
|
_batchedAssetUploadReady.add(data);
|
||||||
|
_batchDebouncer.run(_processBatchedAssetUploadReady);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _processBatchedAssetUploadReady() {
|
||||||
|
if (_batchedAssetUploadReady.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
unawaited(
|
||||||
|
_ref
|
||||||
|
.read(backgroundSyncProvider)
|
||||||
|
.syncWebsocketBatch(_batchedAssetUploadReady.toList()),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
_log.severe("Error processing batched AssetUploadReadyV1 events: $error");
|
||||||
|
}
|
||||||
|
|
||||||
|
_batchedAssetUploadReady.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final websocketProvider =
|
final websocketProvider =
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ import 'package:immich_mobile/pages/share_intent/share_intent.page.dart';
|
|||||||
import 'package:immich_mobile/presentation/pages/drift_favorite.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_favorite.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_partner_detail.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_partner_detail.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_local_album.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_local_album.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/drift_place.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/drift_place_detail.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_recently_taken.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_recently_taken.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_video.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_video.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_trash.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_trash.page.dart';
|
||||||
@@ -453,7 +455,14 @@ class AppRouter extends RootStackRouter {
|
|||||||
page: DriftCreateAlbumRoute.page,
|
page: DriftCreateAlbumRoute.page,
|
||||||
guards: [_authGuard, _duplicateGuard],
|
guards: [_authGuard, _duplicateGuard],
|
||||||
),
|
),
|
||||||
|
AutoRoute(
|
||||||
|
page: DriftPlaceRoute.page,
|
||||||
|
guards: [_authGuard, _duplicateGuard],
|
||||||
|
),
|
||||||
|
AutoRoute(
|
||||||
|
page: DriftPlaceDetailRoute.page,
|
||||||
|
guards: [_authGuard, _duplicateGuard],
|
||||||
|
),
|
||||||
// required to handle all deeplinks in deep_link.service.dart
|
// required to handle all deeplinks in deep_link.service.dart
|
||||||
// auto_route_library#1722
|
// auto_route_library#1722
|
||||||
RedirectRoute(path: '*', redirectTo: '/'),
|
RedirectRoute(path: '*', redirectTo: '/'),
|
||||||
|
|||||||
@@ -853,6 +853,85 @@ class DriftPartnerDetailRouteArgs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [DriftPlaceDetailPage]
|
||||||
|
class DriftPlaceDetailRoute extends PageRouteInfo<DriftPlaceDetailRouteArgs> {
|
||||||
|
DriftPlaceDetailRoute({
|
||||||
|
Key? key,
|
||||||
|
required String place,
|
||||||
|
List<PageRouteInfo>? children,
|
||||||
|
}) : super(
|
||||||
|
DriftPlaceDetailRoute.name,
|
||||||
|
args: DriftPlaceDetailRouteArgs(key: key, place: place),
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'DriftPlaceDetailRoute';
|
||||||
|
|
||||||
|
static PageInfo page = PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
final args = data.argsAs<DriftPlaceDetailRouteArgs>();
|
||||||
|
return DriftPlaceDetailPage(key: args.key, place: args.place);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class DriftPlaceDetailRouteArgs {
|
||||||
|
const DriftPlaceDetailRouteArgs({this.key, required this.place});
|
||||||
|
|
||||||
|
final Key? key;
|
||||||
|
|
||||||
|
final String place;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'DriftPlaceDetailRouteArgs{key: $key, place: $place}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [DriftPlacePage]
|
||||||
|
class DriftPlaceRoute extends PageRouteInfo<DriftPlaceRouteArgs> {
|
||||||
|
DriftPlaceRoute({
|
||||||
|
Key? key,
|
||||||
|
LatLng? currentLocation,
|
||||||
|
List<PageRouteInfo>? children,
|
||||||
|
}) : super(
|
||||||
|
DriftPlaceRoute.name,
|
||||||
|
args: DriftPlaceRouteArgs(key: key, currentLocation: currentLocation),
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'DriftPlaceRoute';
|
||||||
|
|
||||||
|
static PageInfo page = PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
final args = data.argsAs<DriftPlaceRouteArgs>(
|
||||||
|
orElse: () => const DriftPlaceRouteArgs(),
|
||||||
|
);
|
||||||
|
return DriftPlacePage(
|
||||||
|
key: args.key,
|
||||||
|
currentLocation: args.currentLocation,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class DriftPlaceRouteArgs {
|
||||||
|
const DriftPlaceRouteArgs({this.key, this.currentLocation});
|
||||||
|
|
||||||
|
final Key? key;
|
||||||
|
|
||||||
|
final LatLng? currentLocation;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'DriftPlaceRouteArgs{key: $key, currentLocation: $currentLocation}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [DriftRecentlyTakenPage]
|
/// [DriftRecentlyTakenPage]
|
||||||
class DriftRecentlyTakenRoute extends PageRouteInfo<void> {
|
class DriftRecentlyTakenRoute extends PageRouteInfo<void> {
|
||||||
|
|||||||
@@ -482,10 +482,10 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground>
|
|||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
return Transform.scale(
|
return Transform.scale(
|
||||||
scale: _zoomAnimation.value,
|
scale: _zoomAnimation.value,
|
||||||
filterQuality: FilterQuality.low,
|
filterQuality: Platform.isAndroid ? FilterQuality.low : null,
|
||||||
child: Transform.translate(
|
child: Transform.translate(
|
||||||
offset: _panAnimation.value,
|
offset: _panAnimation.value,
|
||||||
filterQuality: FilterQuality.low,
|
filterQuality: Platform.isAndroid ? FilterQuality.low : null,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -101,6 +101,10 @@ void main() {
|
|||||||
debugLabel: any(named: 'debugLabel'),
|
debugLabel: any(named: 'debugLabel'),
|
||||||
),
|
),
|
||||||
).thenAnswer(successHandler);
|
).thenAnswer(successHandler);
|
||||||
|
when(() => mockSyncStreamRepo.updateUserMetadatasV1(any()))
|
||||||
|
.thenAnswer(successHandler);
|
||||||
|
when(() => mockSyncStreamRepo.deleteUserMetadatasV1(any()))
|
||||||
|
.thenAnswer(successHandler);
|
||||||
|
|
||||||
sut = SyncStreamService(
|
sut = SyncStreamService(
|
||||||
syncApiRepository: mockSyncApiRepo,
|
syncApiRepository: mockSyncApiRepo,
|
||||||
|
|||||||
Generated
+1904
-881
File diff suppressed because it is too large
Load Diff
+8
-8
@@ -44,18 +44,18 @@
|
|||||||
"@nestjs/swagger": "^11.0.2",
|
"@nestjs/swagger": "^11.0.2",
|
||||||
"@nestjs/websockets": "^11.0.4",
|
"@nestjs/websockets": "^11.0.4",
|
||||||
"@opentelemetry/api": "^1.9.0",
|
"@opentelemetry/api": "^1.9.0",
|
||||||
"@opentelemetry/auto-instrumentations-node": "^0.60.0",
|
"@opentelemetry/auto-instrumentations-node": "^0.62.0",
|
||||||
"@opentelemetry/context-async-hooks": "^2.0.0",
|
"@opentelemetry/context-async-hooks": "^2.0.0",
|
||||||
"@opentelemetry/exporter-prometheus": "^0.202.0",
|
"@opentelemetry/exporter-prometheus": "^0.203.0",
|
||||||
"@opentelemetry/instrumentation-http": "^0.202.0",
|
"@opentelemetry/instrumentation-http": "^0.203.0",
|
||||||
"@opentelemetry/instrumentation-ioredis": "^0.50.0",
|
"@opentelemetry/instrumentation-ioredis": "^0.51.0",
|
||||||
"@opentelemetry/instrumentation-nestjs-core": "^0.48.0",
|
"@opentelemetry/instrumentation-nestjs-core": "^0.49.0",
|
||||||
"@opentelemetry/instrumentation-pg": "^0.54.0",
|
"@opentelemetry/instrumentation-pg": "^0.55.0",
|
||||||
"@opentelemetry/resources": "^2.0.1",
|
"@opentelemetry/resources": "^2.0.1",
|
||||||
"@opentelemetry/sdk-metrics": "^2.0.1",
|
"@opentelemetry/sdk-metrics": "^2.0.1",
|
||||||
"@opentelemetry/sdk-node": "^0.202.0",
|
"@opentelemetry/sdk-node": "^0.203.0",
|
||||||
"@opentelemetry/semantic-conventions": "^1.34.0",
|
"@opentelemetry/semantic-conventions": "^1.34.0",
|
||||||
"@react-email/components": "^0.1.0",
|
"@react-email/components": "^0.2.0",
|
||||||
"@react-email/render": "^1.1.2",
|
"@react-email/render": "^1.1.2",
|
||||||
"@socket.io/redis-adapter": "^8.3.0",
|
"@socket.io/redis-adapter": "^8.3.0",
|
||||||
"archiver": "^7.0.0",
|
"archiver": "^7.0.0",
|
||||||
|
|||||||
@@ -73,11 +73,11 @@ class BaseModule implements OnModuleInit, OnModuleDestroy {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.eventRepository.setup({ services });
|
this.eventRepository.setup({ services });
|
||||||
await this.eventRepository.emit('app.bootstrap');
|
await this.eventRepository.emit('AppBootstrap');
|
||||||
}
|
}
|
||||||
|
|
||||||
async onModuleDestroy() {
|
async onModuleDestroy() {
|
||||||
await this.eventRepository.emit('app.shutdown');
|
await this.eventRepository.emit('AppShutdown');
|
||||||
await teardownTelemetry();
|
await teardownTelemetry();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,13 @@ describe(APIKeyController.name, () => {
|
|||||||
expect(status).toBe(400);
|
expect(status).toBe(400);
|
||||||
expect(body).toEqual(factory.responses.badRequest(['id must be a UUID']));
|
expect(body).toEqual(factory.responses.badRequest(['id must be a UUID']));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should allow updating just the name', async () => {
|
||||||
|
const { status } = await request(ctx.getHttpServer())
|
||||||
|
.put(`/api-keys/${factory.uuid()}`)
|
||||||
|
.send({ name: 'new name' });
|
||||||
|
expect(status).toBe(200);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('DELETE /api-keys/:id', () => {
|
describe('DELETE /api-keys/:id', () => {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { IsEnum, IsNotEmpty, IsString, ValidateIf } from 'class-validator';
|
import { IsNotEmpty, IsString, ValidateIf } from 'class-validator';
|
||||||
import { Activity } from 'src/database';
|
import { Activity } from 'src/database';
|
||||||
import { mapUser, UserResponseDto } from 'src/dtos/user.dto';
|
import { mapUser, UserResponseDto } from 'src/dtos/user.dto';
|
||||||
import { Optional, ValidateUUID } from 'src/validation';
|
import { ValidateEnum, ValidateUUID } from 'src/validation';
|
||||||
|
|
||||||
export enum ReactionType {
|
export enum ReactionType {
|
||||||
COMMENT = 'comment',
|
COMMENT = 'comment',
|
||||||
@@ -19,7 +19,7 @@ export type MaybeDuplicate<T> = { duplicate: boolean; value: T };
|
|||||||
export class ActivityResponseDto {
|
export class ActivityResponseDto {
|
||||||
id!: string;
|
id!: string;
|
||||||
createdAt!: Date;
|
createdAt!: Date;
|
||||||
@ApiProperty({ enumName: 'ReactionType', enum: ReactionType })
|
@ValidateEnum({ enum: ReactionType, name: 'ReactionType' })
|
||||||
type!: ReactionType;
|
type!: ReactionType;
|
||||||
user!: UserResponseDto;
|
user!: UserResponseDto;
|
||||||
assetId!: string | null;
|
assetId!: string | null;
|
||||||
@@ -43,14 +43,10 @@ export class ActivityDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ActivitySearchDto extends ActivityDto {
|
export class ActivitySearchDto extends ActivityDto {
|
||||||
@IsEnum(ReactionType)
|
@ValidateEnum({ enum: ReactionType, name: 'ReactionType', optional: true })
|
||||||
@Optional()
|
|
||||||
@ApiProperty({ enumName: 'ReactionType', enum: ReactionType })
|
|
||||||
type?: ReactionType;
|
type?: ReactionType;
|
||||||
|
|
||||||
@IsEnum(ReactionLevel)
|
@ValidateEnum({ enum: ReactionLevel, name: 'ReactionLevel', optional: true })
|
||||||
@Optional()
|
|
||||||
@ApiProperty({ enumName: 'ReactionLevel', enum: ReactionLevel })
|
|
||||||
level?: ReactionLevel;
|
level?: ReactionLevel;
|
||||||
|
|
||||||
@ValidateUUID({ optional: true })
|
@ValidateUUID({ optional: true })
|
||||||
@@ -60,8 +56,7 @@ export class ActivitySearchDto extends ActivityDto {
|
|||||||
const isComment = (dto: ActivityCreateDto) => dto.type === ReactionType.COMMENT;
|
const isComment = (dto: ActivityCreateDto) => dto.type === ReactionType.COMMENT;
|
||||||
|
|
||||||
export class ActivityCreateDto extends ActivityDto {
|
export class ActivityCreateDto extends ActivityDto {
|
||||||
@IsEnum(ReactionType)
|
@ValidateEnum({ enum: ReactionType, name: 'ReactionType' })
|
||||||
@ApiProperty({ enumName: 'ReactionType', enum: ReactionType })
|
|
||||||
type!: ReactionType;
|
type!: ReactionType;
|
||||||
|
|
||||||
@ValidateIf(isComment)
|
@ValidateIf(isComment)
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import { ArrayNotEmpty, IsArray, IsEnum, IsString, ValidateNested } from 'class-validator';
|
import { ArrayNotEmpty, IsArray, IsString, ValidateNested } from 'class-validator';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { AlbumUser, AuthSharedLink, User } from 'src/database';
|
import { AlbumUser, AuthSharedLink, User } from 'src/database';
|
||||||
import { AssetResponseDto, MapAsset, mapAsset } from 'src/dtos/asset-response.dto';
|
import { AssetResponseDto, MapAsset, mapAsset } from 'src/dtos/asset-response.dto';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { UserResponseDto, mapUser } from 'src/dtos/user.dto';
|
import { UserResponseDto, mapUser } from 'src/dtos/user.dto';
|
||||||
import { AlbumUserRole, AssetOrder } from 'src/enum';
|
import { AlbumUserRole, AssetOrder } from 'src/enum';
|
||||||
import { Optional, ValidateBoolean, ValidateUUID } from 'src/validation';
|
import { Optional, ValidateBoolean, ValidateEnum, ValidateUUID } from 'src/validation';
|
||||||
|
|
||||||
export class AlbumInfoDto {
|
export class AlbumInfoDto {
|
||||||
@ValidateBoolean({ optional: true })
|
@ValidateBoolean({ optional: true })
|
||||||
@@ -18,8 +18,7 @@ export class AlbumUserAddDto {
|
|||||||
@ValidateUUID()
|
@ValidateUUID()
|
||||||
userId!: string;
|
userId!: string;
|
||||||
|
|
||||||
@IsEnum(AlbumUserRole)
|
@ValidateEnum({ enum: AlbumUserRole, name: 'AlbumUserRole', default: AlbumUserRole.EDITOR })
|
||||||
@ApiProperty({ enum: AlbumUserRole, enumName: 'AlbumUserRole', default: AlbumUserRole.EDITOR })
|
|
||||||
role?: AlbumUserRole;
|
role?: AlbumUserRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,8 +31,7 @@ export class AlbumUserCreateDto {
|
|||||||
@ValidateUUID()
|
@ValidateUUID()
|
||||||
userId!: string;
|
userId!: string;
|
||||||
|
|
||||||
@IsEnum(AlbumUserRole)
|
@ValidateEnum({ enum: AlbumUserRole, name: 'AlbumUserRole' })
|
||||||
@ApiProperty({ enum: AlbumUserRole, enumName: 'AlbumUserRole' })
|
|
||||||
role!: AlbumUserRole;
|
role!: AlbumUserRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,9 +69,7 @@ export class UpdateAlbumDto {
|
|||||||
@ValidateBoolean({ optional: true })
|
@ValidateBoolean({ optional: true })
|
||||||
isActivityEnabled?: boolean;
|
isActivityEnabled?: boolean;
|
||||||
|
|
||||||
@IsEnum(AssetOrder)
|
@ValidateEnum({ enum: AssetOrder, name: 'AssetOrder', optional: true })
|
||||||
@Optional()
|
|
||||||
@ApiProperty({ enum: AssetOrder, enumName: 'AssetOrder' })
|
|
||||||
order?: AssetOrder;
|
order?: AssetOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,14 +103,13 @@ export class AlbumStatisticsResponseDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateAlbumUserDto {
|
export class UpdateAlbumUserDto {
|
||||||
@IsEnum(AlbumUserRole)
|
@ValidateEnum({ enum: AlbumUserRole, name: 'AlbumUserRole' })
|
||||||
@ApiProperty({ enum: AlbumUserRole, enumName: 'AlbumUserRole' })
|
|
||||||
role!: AlbumUserRole;
|
role!: AlbumUserRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AlbumUserResponseDto {
|
export class AlbumUserResponseDto {
|
||||||
user!: UserResponseDto;
|
user!: UserResponseDto;
|
||||||
@ApiProperty({ enum: AlbumUserRole, enumName: 'AlbumUserRole' })
|
@ValidateEnum({ enum: AlbumUserRole, name: 'AlbumUserRole' })
|
||||||
role!: AlbumUserRole;
|
role!: AlbumUserRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,8 +132,7 @@ export class AlbumResponseDto {
|
|||||||
startDate?: Date;
|
startDate?: Date;
|
||||||
endDate?: Date;
|
endDate?: Date;
|
||||||
isActivityEnabled!: boolean;
|
isActivityEnabled!: boolean;
|
||||||
@Optional()
|
@ValidateEnum({ enum: AssetOrder, name: 'AssetOrder', optional: true })
|
||||||
@ApiProperty({ enumName: 'AssetOrder', enum: AssetOrder })
|
|
||||||
order?: AssetOrder;
|
order?: AssetOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ArrayMinSize, IsNotEmpty, IsString } from 'class-validator';
|
||||||
import { ArrayMinSize, IsEnum, IsNotEmpty, IsString } from 'class-validator';
|
|
||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
import { Optional } from 'src/validation';
|
import { Optional, ValidateEnum } from 'src/validation';
|
||||||
export class APIKeyCreateDto {
|
export class APIKeyCreateDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@Optional()
|
@Optional()
|
||||||
name?: string;
|
name?: string;
|
||||||
|
|
||||||
@IsEnum(Permission, { each: true })
|
@ValidateEnum({ enum: Permission, name: 'Permission', each: true })
|
||||||
@ApiProperty({ enum: Permission, enumName: 'Permission', isArray: true })
|
|
||||||
@ArrayMinSize(1)
|
@ArrayMinSize(1)
|
||||||
permissions!: Permission[];
|
permissions!: Permission[];
|
||||||
}
|
}
|
||||||
@@ -20,9 +18,7 @@ export class APIKeyUpdateDto {
|
|||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
name?: string;
|
name?: string;
|
||||||
|
|
||||||
@Optional()
|
@ValidateEnum({ enum: Permission, name: 'Permission', each: true, optional: true })
|
||||||
@IsEnum(Permission, { each: true })
|
|
||||||
@ApiProperty({ enum: Permission, enumName: 'Permission', isArray: true })
|
|
||||||
@ArrayMinSize(1)
|
@ArrayMinSize(1)
|
||||||
permissions?: Permission[];
|
permissions?: Permission[];
|
||||||
}
|
}
|
||||||
@@ -37,6 +33,6 @@ export class APIKeyResponseDto {
|
|||||||
name!: string;
|
name!: string;
|
||||||
createdAt!: Date;
|
createdAt!: Date;
|
||||||
updatedAt!: Date;
|
updatedAt!: Date;
|
||||||
@ApiProperty({ enum: Permission, enumName: 'Permission', isArray: true })
|
@ValidateEnum({ enum: Permission, name: 'Permission', each: true })
|
||||||
permissions!: Permission[];
|
permissions!: Permission[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ValidateEnum } from 'src/validation';
|
||||||
|
|
||||||
export enum AssetMediaStatus {
|
export enum AssetMediaStatus {
|
||||||
CREATED = 'created',
|
CREATED = 'created',
|
||||||
@@ -6,7 +6,7 @@ export enum AssetMediaStatus {
|
|||||||
DUPLICATE = 'duplicate',
|
DUPLICATE = 'duplicate',
|
||||||
}
|
}
|
||||||
export class AssetMediaResponseDto {
|
export class AssetMediaResponseDto {
|
||||||
@ApiProperty({ enum: AssetMediaStatus, enumName: 'AssetMediaStatus' })
|
@ValidateEnum({ enum: AssetMediaStatus, name: 'AssetMediaStatus' })
|
||||||
status!: AssetMediaStatus;
|
status!: AssetMediaStatus;
|
||||||
id!: string;
|
id!: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import { ArrayNotEmpty, IsArray, IsEnum, IsNotEmpty, IsString, ValidateNested } from 'class-validator';
|
import { ArrayNotEmpty, IsArray, IsNotEmpty, IsString, ValidateNested } from 'class-validator';
|
||||||
import { AssetVisibility } from 'src/enum';
|
import { AssetVisibility } from 'src/enum';
|
||||||
import { Optional, ValidateAssetVisibility, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation';
|
import { Optional, ValidateBoolean, ValidateDate, ValidateEnum, ValidateUUID } from 'src/validation';
|
||||||
|
|
||||||
export enum AssetMediaSize {
|
export enum AssetMediaSize {
|
||||||
/**
|
/**
|
||||||
@@ -15,9 +15,7 @@ export enum AssetMediaSize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class AssetMediaOptionsDto {
|
export class AssetMediaOptionsDto {
|
||||||
@Optional()
|
@ValidateEnum({ enum: AssetMediaSize, name: 'AssetMediaSize', optional: true })
|
||||||
@IsEnum(AssetMediaSize)
|
|
||||||
@ApiProperty({ enumName: 'AssetMediaSize', enum: AssetMediaSize })
|
|
||||||
size?: AssetMediaSize;
|
size?: AssetMediaSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +58,7 @@ export class AssetMediaCreateDto extends AssetMediaBase {
|
|||||||
@ValidateBoolean({ optional: true })
|
@ValidateBoolean({ optional: true })
|
||||||
isFavorite?: boolean;
|
isFavorite?: boolean;
|
||||||
|
|
||||||
@ValidateAssetVisibility({ optional: true })
|
@ValidateEnum({ enum: AssetVisibility, name: 'AssetVisibility', optional: true })
|
||||||
visibility?: AssetVisibility;
|
visibility?: AssetVisibility;
|
||||||
|
|
||||||
@ValidateUUID({ optional: true })
|
@ValidateUUID({ optional: true })
|
||||||
|
|||||||
@@ -15,10 +15,11 @@ import { UserResponseDto, mapUser } from 'src/dtos/user.dto';
|
|||||||
import { AssetStatus, AssetType, AssetVisibility } from 'src/enum';
|
import { AssetStatus, AssetType, AssetVisibility } from 'src/enum';
|
||||||
import { hexOrBufferToBase64 } from 'src/utils/bytes';
|
import { hexOrBufferToBase64 } from 'src/utils/bytes';
|
||||||
import { mimeTypes } from 'src/utils/mime-types';
|
import { mimeTypes } from 'src/utils/mime-types';
|
||||||
|
import { ValidateEnum } from 'src/validation';
|
||||||
|
|
||||||
export class SanitizedAssetResponseDto {
|
export class SanitizedAssetResponseDto {
|
||||||
id!: string;
|
id!: string;
|
||||||
@ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType })
|
@ValidateEnum({ enum: AssetType, name: 'AssetTypeEnum' })
|
||||||
type!: AssetType;
|
type!: AssetType;
|
||||||
thumbhash!: string | null;
|
thumbhash!: string | null;
|
||||||
originalMimeType?: string;
|
originalMimeType?: string;
|
||||||
@@ -72,7 +73,7 @@ export class AssetResponseDto extends SanitizedAssetResponseDto {
|
|||||||
isArchived!: boolean;
|
isArchived!: boolean;
|
||||||
isTrashed!: boolean;
|
isTrashed!: boolean;
|
||||||
isOffline!: boolean;
|
isOffline!: boolean;
|
||||||
@ApiProperty({ enum: AssetVisibility, enumName: 'AssetVisibility' })
|
@ValidateEnum({ enum: AssetVisibility, name: 'AssetVisibility' })
|
||||||
visibility!: AssetVisibility;
|
visibility!: AssetVisibility;
|
||||||
exifInfo?: ExifResponseDto;
|
exifInfo?: ExifResponseDto;
|
||||||
tags?: TagResponseDto[];
|
tags?: TagResponseDto[];
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { ApiProperty } from '@nestjs/swagger';
|
|||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import {
|
import {
|
||||||
IsDateString,
|
IsDateString,
|
||||||
IsEnum,
|
|
||||||
IsInt,
|
IsInt,
|
||||||
IsLatitude,
|
IsLatitude,
|
||||||
IsLongitude,
|
IsLongitude,
|
||||||
@@ -16,7 +15,7 @@ import {
|
|||||||
import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
||||||
import { AssetType, AssetVisibility } from 'src/enum';
|
import { AssetType, AssetVisibility } from 'src/enum';
|
||||||
import { AssetStats } from 'src/repositories/asset.repository';
|
import { AssetStats } from 'src/repositories/asset.repository';
|
||||||
import { Optional, ValidateAssetVisibility, ValidateBoolean, ValidateUUID } from 'src/validation';
|
import { Optional, ValidateBoolean, ValidateEnum, ValidateUUID } from 'src/validation';
|
||||||
|
|
||||||
export class DeviceIdDto {
|
export class DeviceIdDto {
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@@ -32,7 +31,7 @@ export class UpdateAssetBase {
|
|||||||
@ValidateBoolean({ optional: true })
|
@ValidateBoolean({ optional: true })
|
||||||
isFavorite?: boolean;
|
isFavorite?: boolean;
|
||||||
|
|
||||||
@ValidateAssetVisibility({ optional: true })
|
@ValidateEnum({ enum: AssetVisibility, name: 'AssetVisibility', optional: true })
|
||||||
visibility?: AssetVisibility;
|
visibility?: AssetVisibility;
|
||||||
|
|
||||||
@Optional()
|
@Optional()
|
||||||
@@ -99,13 +98,12 @@ export enum AssetJobName {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class AssetJobsDto extends AssetIdsDto {
|
export class AssetJobsDto extends AssetIdsDto {
|
||||||
@ApiProperty({ enumName: 'AssetJobName', enum: AssetJobName })
|
@ValidateEnum({ enum: AssetJobName, name: 'AssetJobName' })
|
||||||
@IsEnum(AssetJobName)
|
|
||||||
name!: AssetJobName;
|
name!: AssetJobName;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AssetStatsDto {
|
export class AssetStatsDto {
|
||||||
@ValidateAssetVisibility({ optional: true })
|
@ValidateEnum({ enum: AssetVisibility, name: 'AssetVisibility', optional: true })
|
||||||
visibility?: AssetVisibility;
|
visibility?: AssetVisibility;
|
||||||
|
|
||||||
@ValidateBoolean({ optional: true })
|
@ValidateBoolean({ optional: true })
|
||||||
|
|||||||
@@ -1,19 +1,14 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { IsEnum, IsNotEmpty } from 'class-validator';
|
|
||||||
import { JobCommand, ManualJobName, QueueName } from 'src/enum';
|
import { JobCommand, ManualJobName, QueueName } from 'src/enum';
|
||||||
import { ValidateBoolean } from 'src/validation';
|
import { ValidateBoolean, ValidateEnum } from 'src/validation';
|
||||||
|
|
||||||
export class JobIdParamDto {
|
export class JobIdParamDto {
|
||||||
@IsNotEmpty()
|
@ValidateEnum({ enum: QueueName, name: 'JobName' })
|
||||||
@IsEnum(QueueName)
|
|
||||||
@ApiProperty({ type: String, enum: QueueName, enumName: 'JobName' })
|
|
||||||
id!: QueueName;
|
id!: QueueName;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class JobCommandDto {
|
export class JobCommandDto {
|
||||||
@IsNotEmpty()
|
@ValidateEnum({ enum: JobCommand, name: 'JobCommand' })
|
||||||
@IsEnum(JobCommand)
|
|
||||||
@ApiProperty({ type: 'string', enum: JobCommand, enumName: 'JobCommand' })
|
|
||||||
command!: JobCommand;
|
command!: JobCommand;
|
||||||
|
|
||||||
@ValidateBoolean({ optional: true })
|
@ValidateBoolean({ optional: true })
|
||||||
@@ -21,8 +16,7 @@ export class JobCommandDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class JobCreateDto {
|
export class JobCreateDto {
|
||||||
@IsEnum(ManualJobName)
|
@ValidateEnum({ enum: ManualJobName, name: 'ManualJobName' })
|
||||||
@ApiProperty({ type: 'string', enum: ManualJobName, enumName: 'ManualJobName' })
|
|
||||||
name!: ManualJobName;
|
name!: ManualJobName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import { IsEnum, IsInt, IsObject, IsPositive, ValidateNested } from 'class-validator';
|
import { IsInt, IsObject, IsPositive, ValidateNested } from 'class-validator';
|
||||||
import { Memory } from 'src/database';
|
import { Memory } from 'src/database';
|
||||||
import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto';
|
import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { MemoryType } from 'src/enum';
|
import { MemoryType } from 'src/enum';
|
||||||
import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation';
|
import { ValidateBoolean, ValidateDate, ValidateEnum, ValidateUUID } from 'src/validation';
|
||||||
|
|
||||||
class MemoryBaseDto {
|
class MemoryBaseDto {
|
||||||
@ValidateBoolean({ optional: true })
|
@ValidateBoolean({ optional: true })
|
||||||
@@ -16,9 +16,7 @@ class MemoryBaseDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class MemorySearchDto {
|
export class MemorySearchDto {
|
||||||
@Optional()
|
@ValidateEnum({ enum: MemoryType, name: 'MemoryType', optional: true })
|
||||||
@IsEnum(MemoryType)
|
|
||||||
@ApiProperty({ enum: MemoryType, enumName: 'MemoryType' })
|
|
||||||
type?: MemoryType;
|
type?: MemoryType;
|
||||||
|
|
||||||
@ValidateDate({ optional: true })
|
@ValidateDate({ optional: true })
|
||||||
@@ -45,8 +43,7 @@ export class MemoryUpdateDto extends MemoryBaseDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class MemoryCreateDto extends MemoryBaseDto {
|
export class MemoryCreateDto extends MemoryBaseDto {
|
||||||
@IsEnum(MemoryType)
|
@ValidateEnum({ enum: MemoryType, name: 'MemoryType' })
|
||||||
@ApiProperty({ enum: MemoryType, enumName: 'MemoryType' })
|
|
||||||
type!: MemoryType;
|
type!: MemoryType;
|
||||||
|
|
||||||
@IsObject()
|
@IsObject()
|
||||||
@@ -86,7 +83,7 @@ export class MemoryResponseDto {
|
|||||||
showAt?: Date;
|
showAt?: Date;
|
||||||
hideAt?: Date;
|
hideAt?: Date;
|
||||||
ownerId!: string;
|
ownerId!: string;
|
||||||
@ApiProperty({ enumName: 'MemoryType', enum: MemoryType })
|
@ValidateEnum({ enum: MemoryType, name: 'MemoryType' })
|
||||||
type!: MemoryType;
|
type!: MemoryType;
|
||||||
data!: MemoryData;
|
data!: MemoryData;
|
||||||
isSaved!: boolean;
|
isSaved!: boolean;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { IsString } from 'class-validator';
|
||||||
import { IsEnum, IsString } from 'class-validator';
|
|
||||||
import { NotificationLevel, NotificationType } from 'src/enum';
|
import { NotificationLevel, NotificationType } from 'src/enum';
|
||||||
import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation';
|
import { Optional, ValidateBoolean, ValidateDate, ValidateEnum, ValidateUUID } from 'src/validation';
|
||||||
|
|
||||||
export class TestEmailResponseDto {
|
export class TestEmailResponseDto {
|
||||||
messageId!: string;
|
messageId!: string;
|
||||||
@@ -19,9 +18,9 @@ export class NotificationDto {
|
|||||||
id!: string;
|
id!: string;
|
||||||
@ValidateDate()
|
@ValidateDate()
|
||||||
createdAt!: Date;
|
createdAt!: Date;
|
||||||
@ApiProperty({ enum: NotificationLevel, enumName: 'NotificationLevel' })
|
@ValidateEnum({ enum: NotificationLevel, name: 'NotificationLevel' })
|
||||||
level!: NotificationLevel;
|
level!: NotificationLevel;
|
||||||
@ApiProperty({ enum: NotificationType, enumName: 'NotificationType' })
|
@ValidateEnum({ enum: NotificationType, name: 'NotificationType' })
|
||||||
type!: NotificationType;
|
type!: NotificationType;
|
||||||
title!: string;
|
title!: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
@@ -30,18 +29,13 @@ export class NotificationDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class NotificationSearchDto {
|
export class NotificationSearchDto {
|
||||||
@Optional()
|
|
||||||
@ValidateUUID({ optional: true })
|
@ValidateUUID({ optional: true })
|
||||||
id?: string;
|
id?: string;
|
||||||
|
|
||||||
@IsEnum(NotificationLevel)
|
@ValidateEnum({ enum: NotificationLevel, name: 'NotificationLevel', optional: true })
|
||||||
@Optional()
|
|
||||||
@ApiProperty({ enum: NotificationLevel, enumName: 'NotificationLevel' })
|
|
||||||
level?: NotificationLevel;
|
level?: NotificationLevel;
|
||||||
|
|
||||||
@IsEnum(NotificationType)
|
@ValidateEnum({ enum: NotificationType, name: 'NotificationType', optional: true })
|
||||||
@Optional()
|
|
||||||
@ApiProperty({ enum: NotificationType, enumName: 'NotificationType' })
|
|
||||||
type?: NotificationType;
|
type?: NotificationType;
|
||||||
|
|
||||||
@ValidateBoolean({ optional: true })
|
@ValidateBoolean({ optional: true })
|
||||||
@@ -49,14 +43,10 @@ export class NotificationSearchDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class NotificationCreateDto {
|
export class NotificationCreateDto {
|
||||||
@Optional()
|
@ValidateEnum({ enum: NotificationLevel, name: 'NotificationLevel', optional: true })
|
||||||
@IsEnum(NotificationLevel)
|
|
||||||
@ApiProperty({ enum: NotificationLevel, enumName: 'NotificationLevel' })
|
|
||||||
level?: NotificationLevel;
|
level?: NotificationLevel;
|
||||||
|
|
||||||
@IsEnum(NotificationType)
|
@ValidateEnum({ enum: NotificationType, name: 'NotificationType', optional: true })
|
||||||
@Optional()
|
|
||||||
@ApiProperty({ enum: NotificationType, enumName: 'NotificationType' })
|
|
||||||
type?: NotificationType;
|
type?: NotificationType;
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { IsBoolean, IsNotEmpty } from 'class-validator';
|
import { ValidateBoolean } from 'src/validation';
|
||||||
|
|
||||||
export class OnboardingDto {
|
export class OnboardingDto {
|
||||||
@IsBoolean()
|
@ValidateBoolean()
|
||||||
@IsNotEmpty()
|
|
||||||
isOnboarded!: boolean;
|
isOnboarded!: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { IsNotEmpty } from 'class-validator';
|
||||||
import { IsEnum, IsNotEmpty } from 'class-validator';
|
|
||||||
import { UserResponseDto } from 'src/dtos/user.dto';
|
import { UserResponseDto } from 'src/dtos/user.dto';
|
||||||
import { PartnerDirection } from 'src/repositories/partner.repository';
|
import { PartnerDirection } from 'src/repositories/partner.repository';
|
||||||
|
import { ValidateEnum } from 'src/validation';
|
||||||
|
|
||||||
export class UpdatePartnerDto {
|
export class UpdatePartnerDto {
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@@ -9,8 +9,7 @@ export class UpdatePartnerDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class PartnerSearchDto {
|
export class PartnerSearchDto {
|
||||||
@IsEnum(PartnerDirection)
|
@ValidateEnum({ enum: PartnerDirection, name: 'PartnerDirection' })
|
||||||
@ApiProperty({ enum: PartnerDirection, enumName: 'PartnerDirection' })
|
|
||||||
direction!: PartnerDirection;
|
direction!: PartnerDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
MaxDateString,
|
MaxDateString,
|
||||||
Optional,
|
Optional,
|
||||||
ValidateBoolean,
|
ValidateBoolean,
|
||||||
|
ValidateEnum,
|
||||||
ValidateHexColor,
|
ValidateHexColor,
|
||||||
ValidateUUID,
|
ValidateUUID,
|
||||||
} from 'src/validation';
|
} from 'src/validation';
|
||||||
@@ -137,7 +138,7 @@ export class AssetFaceWithoutPersonResponseDto {
|
|||||||
boundingBoxY1!: number;
|
boundingBoxY1!: number;
|
||||||
@ApiProperty({ type: 'integer' })
|
@ApiProperty({ type: 'integer' })
|
||||||
boundingBoxY2!: number;
|
boundingBoxY2!: number;
|
||||||
@ApiProperty({ enum: SourceType, enumName: 'SourceType' })
|
@ValidateEnum({ enum: SourceType, name: 'SourceType' })
|
||||||
sourceType?: SourceType;
|
sourceType?: SourceType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import { IsEnum, IsInt, IsNotEmpty, IsString, Max, Min } from 'class-validator';
|
import { IsInt, IsNotEmpty, IsString, Max, Min } from 'class-validator';
|
||||||
import { Place } from 'src/database';
|
import { Place } from 'src/database';
|
||||||
import { PropertyLifecycle } from 'src/decorators';
|
import { PropertyLifecycle } from 'src/decorators';
|
||||||
import { AlbumResponseDto } from 'src/dtos/album.dto';
|
import { AlbumResponseDto } from 'src/dtos/album.dto';
|
||||||
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
||||||
import { AssetOrder, AssetType, AssetVisibility } from 'src/enum';
|
import { AssetOrder, AssetType, AssetVisibility } from 'src/enum';
|
||||||
import { Optional, ValidateAssetVisibility, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation';
|
import { Optional, ValidateBoolean, ValidateDate, ValidateEnum, ValidateUUID } from 'src/validation';
|
||||||
|
|
||||||
class BaseSearchDto {
|
class BaseSearchDto {
|
||||||
@ValidateUUID({ optional: true, nullable: true })
|
@ValidateUUID({ optional: true, nullable: true })
|
||||||
@@ -17,9 +17,7 @@ class BaseSearchDto {
|
|||||||
@Optional()
|
@Optional()
|
||||||
deviceId?: string;
|
deviceId?: string;
|
||||||
|
|
||||||
@IsEnum(AssetType)
|
@ValidateEnum({ enum: AssetType, name: 'AssetTypeEnum', optional: true })
|
||||||
@Optional()
|
|
||||||
@ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType })
|
|
||||||
type?: AssetType;
|
type?: AssetType;
|
||||||
|
|
||||||
@ValidateBoolean({ optional: true })
|
@ValidateBoolean({ optional: true })
|
||||||
@@ -34,7 +32,7 @@ class BaseSearchDto {
|
|||||||
@ValidateBoolean({ optional: true })
|
@ValidateBoolean({ optional: true })
|
||||||
isOffline?: boolean;
|
isOffline?: boolean;
|
||||||
|
|
||||||
@ValidateAssetVisibility({ optional: true })
|
@ValidateEnum({ enum: AssetVisibility, name: 'AssetVisibility', optional: true })
|
||||||
visibility?: AssetVisibility;
|
visibility?: AssetVisibility;
|
||||||
|
|
||||||
@ValidateDate({ optional: true })
|
@ValidateDate({ optional: true })
|
||||||
@@ -172,9 +170,7 @@ export class MetadataSearchDto extends RandomSearchDto {
|
|||||||
@Optional()
|
@Optional()
|
||||||
encodedVideoPath?: string;
|
encodedVideoPath?: string;
|
||||||
|
|
||||||
@IsEnum(AssetOrder)
|
@ValidateEnum({ enum: AssetOrder, name: 'AssetOrder', optional: true, default: AssetOrder.DESC })
|
||||||
@Optional()
|
|
||||||
@ApiProperty({ enumName: 'AssetOrder', enum: AssetOrder, default: AssetOrder.DESC })
|
|
||||||
order?: AssetOrder;
|
order?: AssetOrder;
|
||||||
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@@ -250,9 +246,7 @@ export enum SearchSuggestionType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class SearchSuggestionRequestDto {
|
export class SearchSuggestionRequestDto {
|
||||||
@IsEnum(SearchSuggestionType)
|
@ValidateEnum({ enum: SearchSuggestionType, name: 'SearchSuggestionType' })
|
||||||
@IsNotEmpty()
|
|
||||||
@ApiProperty({ enumName: 'SearchSuggestionType', enum: SearchSuggestionType })
|
|
||||||
type!: SearchSuggestionType;
|
type!: SearchSuggestionType;
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { IsEnum, IsString } from 'class-validator';
|
import { IsString } from 'class-validator';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { SharedLink } from 'src/database';
|
import { SharedLink } from 'src/database';
|
||||||
import { AlbumResponseDto, mapAlbumWithoutAssets } from 'src/dtos/album.dto';
|
import { AlbumResponseDto, mapAlbumWithoutAssets } from 'src/dtos/album.dto';
|
||||||
import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto';
|
import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto';
|
||||||
import { SharedLinkType } from 'src/enum';
|
import { SharedLinkType } from 'src/enum';
|
||||||
import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation';
|
import { Optional, ValidateBoolean, ValidateDate, ValidateEnum, ValidateUUID } from 'src/validation';
|
||||||
|
|
||||||
export class SharedLinkSearchDto {
|
export class SharedLinkSearchDto {
|
||||||
@ValidateUUID({ optional: true })
|
@ValidateUUID({ optional: true })
|
||||||
@@ -13,8 +13,7 @@ export class SharedLinkSearchDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class SharedLinkCreateDto {
|
export class SharedLinkCreateDto {
|
||||||
@IsEnum(SharedLinkType)
|
@ValidateEnum({ enum: SharedLinkType, name: 'SharedLinkType' })
|
||||||
@ApiProperty({ enum: SharedLinkType, enumName: 'SharedLinkType' })
|
|
||||||
type!: SharedLinkType;
|
type!: SharedLinkType;
|
||||||
|
|
||||||
@ValidateUUID({ each: true, optional: true })
|
@ValidateUUID({ each: true, optional: true })
|
||||||
@@ -90,7 +89,7 @@ export class SharedLinkResponseDto {
|
|||||||
userId!: string;
|
userId!: string;
|
||||||
key!: string;
|
key!: string;
|
||||||
|
|
||||||
@ApiProperty({ enumName: 'SharedLinkType', enum: SharedLinkType })
|
@ValidateEnum({ enum: SharedLinkType, name: 'SharedLinkType' })
|
||||||
type!: SharedLinkType;
|
type!: SharedLinkType;
|
||||||
createdAt!: Date;
|
createdAt!: Date;
|
||||||
expiresAt!: Date | null;
|
expiresAt!: Date | null;
|
||||||
|
|||||||
+10
-13
@@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
|
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { ArrayMaxSize, IsEnum, IsInt, IsPositive, IsString } from 'class-validator';
|
import { ArrayMaxSize, IsInt, IsPositive, IsString } from 'class-validator';
|
||||||
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
||||||
import {
|
import {
|
||||||
AlbumUserRole,
|
AlbumUserRole,
|
||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
UserMetadataKey,
|
UserMetadataKey,
|
||||||
} from 'src/enum';
|
} from 'src/enum';
|
||||||
import { UserMetadata } from 'src/types';
|
import { UserMetadata } from 'src/types';
|
||||||
import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation';
|
import { ValidateBoolean, ValidateDate, ValidateEnum, ValidateUUID } from 'src/validation';
|
||||||
|
|
||||||
export class AssetFullSyncDto {
|
export class AssetFullSyncDto {
|
||||||
@ValidateUUID({ optional: true })
|
@ValidateUUID({ optional: true })
|
||||||
@@ -90,11 +90,11 @@ export class SyncAssetV1 {
|
|||||||
fileModifiedAt!: Date | null;
|
fileModifiedAt!: Date | null;
|
||||||
localDateTime!: Date | null;
|
localDateTime!: Date | null;
|
||||||
duration!: string | null;
|
duration!: string | null;
|
||||||
@ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType })
|
@ValidateEnum({ enum: AssetType, name: 'AssetTypeEnum' })
|
||||||
type!: AssetType;
|
type!: AssetType;
|
||||||
deletedAt!: Date | null;
|
deletedAt!: Date | null;
|
||||||
isFavorite!: boolean;
|
isFavorite!: boolean;
|
||||||
@ApiProperty({ enumName: 'AssetVisibility', enum: AssetVisibility })
|
@ValidateEnum({ enum: AssetVisibility, name: 'AssetVisibility' })
|
||||||
visibility!: AssetVisibility;
|
visibility!: AssetVisibility;
|
||||||
livePhotoVideoId!: string | null;
|
livePhotoVideoId!: string | null;
|
||||||
stackId!: string | null;
|
stackId!: string | null;
|
||||||
@@ -159,7 +159,7 @@ export class SyncAlbumUserDeleteV1 {
|
|||||||
export class SyncAlbumUserV1 {
|
export class SyncAlbumUserV1 {
|
||||||
albumId!: string;
|
albumId!: string;
|
||||||
userId!: string;
|
userId!: string;
|
||||||
@ApiProperty({ enumName: 'AlbumUserRole', enum: AlbumUserRole })
|
@ValidateEnum({ enum: AlbumUserRole, name: 'AlbumUserRole' })
|
||||||
role!: AlbumUserRole;
|
role!: AlbumUserRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +173,7 @@ export class SyncAlbumV1 {
|
|||||||
updatedAt!: Date;
|
updatedAt!: Date;
|
||||||
thumbnailAssetId!: string | null;
|
thumbnailAssetId!: string | null;
|
||||||
isActivityEnabled!: boolean;
|
isActivityEnabled!: boolean;
|
||||||
@ApiProperty({ enumName: 'AssetOrder', enum: AssetOrder })
|
@ValidateEnum({ enum: AssetOrder, name: 'AssetOrder' })
|
||||||
order!: AssetOrder;
|
order!: AssetOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +196,7 @@ export class SyncMemoryV1 {
|
|||||||
updatedAt!: Date;
|
updatedAt!: Date;
|
||||||
deletedAt!: Date | null;
|
deletedAt!: Date | null;
|
||||||
ownerId!: string;
|
ownerId!: string;
|
||||||
@ApiProperty({ enumName: 'MemoryType', enum: MemoryType })
|
@ValidateEnum({ enum: MemoryType, name: 'MemoryType' })
|
||||||
type!: MemoryType;
|
type!: MemoryType;
|
||||||
data!: object;
|
data!: object;
|
||||||
isSaved!: boolean;
|
isSaved!: boolean;
|
||||||
@@ -319,8 +319,7 @@ export type SyncItem = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class SyncStreamDto {
|
export class SyncStreamDto {
|
||||||
@IsEnum(SyncRequestType, { each: true })
|
@ValidateEnum({ enum: SyncRequestType, name: 'SyncRequestType', each: true })
|
||||||
@ApiProperty({ enumName: 'SyncRequestType', enum: SyncRequestType, isArray: true })
|
|
||||||
types!: SyncRequestType[];
|
types!: SyncRequestType[];
|
||||||
|
|
||||||
@ValidateBoolean({ optional: true })
|
@ValidateBoolean({ optional: true })
|
||||||
@@ -328,7 +327,7 @@ export class SyncStreamDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class SyncAckDto {
|
export class SyncAckDto {
|
||||||
@ApiProperty({ enumName: 'SyncEntityType', enum: SyncEntityType })
|
@ValidateEnum({ enum: SyncEntityType, name: 'SyncEntityType' })
|
||||||
type!: SyncEntityType;
|
type!: SyncEntityType;
|
||||||
ack!: string;
|
ack!: string;
|
||||||
}
|
}
|
||||||
@@ -340,8 +339,6 @@ export class SyncAckSetDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class SyncAckDeleteDto {
|
export class SyncAckDeleteDto {
|
||||||
@IsEnum(SyncEntityType, { each: true })
|
@ValidateEnum({ enum: SyncEntityType, name: 'SyncEntityType', optional: true, each: true })
|
||||||
@ApiProperty({ enumName: 'SyncEntityType', enum: SyncEntityType, isArray: true })
|
|
||||||
@Optional()
|
|
||||||
types?: SyncEntityType[];
|
types?: SyncEntityType[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import { ApiProperty } from '@nestjs/swagger';
|
|||||||
import { Exclude, Transform, Type } from 'class-transformer';
|
import { Exclude, Transform, Type } from 'class-transformer';
|
||||||
import {
|
import {
|
||||||
ArrayMinSize,
|
ArrayMinSize,
|
||||||
IsBoolean,
|
|
||||||
IsEnum,
|
|
||||||
IsInt,
|
IsInt,
|
||||||
IsNotEmpty,
|
IsNotEmpty,
|
||||||
IsNumber,
|
IsNumber,
|
||||||
@@ -34,7 +32,7 @@ import {
|
|||||||
VideoContainer,
|
VideoContainer,
|
||||||
} from 'src/enum';
|
} from 'src/enum';
|
||||||
import { ConcurrentQueueName } from 'src/types';
|
import { ConcurrentQueueName } from 'src/types';
|
||||||
import { IsCronExpression, IsDateStringFormat, Optional, ValidateBoolean } from 'src/validation';
|
import { IsCronExpression, IsDateStringFormat, Optional, ValidateBoolean, ValidateEnum } from 'src/validation';
|
||||||
|
|
||||||
const isLibraryScanEnabled = (config: SystemConfigLibraryScanDto) => config.enabled;
|
const isLibraryScanEnabled = (config: SystemConfigLibraryScanDto) => config.enabled;
|
||||||
const isOAuthEnabled = (config: SystemConfigOAuthDto) => config.enabled;
|
const isOAuthEnabled = (config: SystemConfigOAuthDto) => config.enabled;
|
||||||
@@ -82,24 +80,19 @@ export class SystemConfigFFmpegDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
preset!: string;
|
preset!: string;
|
||||||
|
|
||||||
@IsEnum(VideoCodec)
|
@ValidateEnum({ enum: VideoCodec, name: 'VideoCodec' })
|
||||||
@ApiProperty({ enumName: 'VideoCodec', enum: VideoCodec })
|
|
||||||
targetVideoCodec!: VideoCodec;
|
targetVideoCodec!: VideoCodec;
|
||||||
|
|
||||||
@IsEnum(VideoCodec, { each: true })
|
@ValidateEnum({ enum: VideoCodec, name: 'VideoCodec', each: true })
|
||||||
@ApiProperty({ enumName: 'VideoCodec', enum: VideoCodec, isArray: true })
|
|
||||||
acceptedVideoCodecs!: VideoCodec[];
|
acceptedVideoCodecs!: VideoCodec[];
|
||||||
|
|
||||||
@IsEnum(AudioCodec)
|
@ValidateEnum({ enum: AudioCodec, name: 'AudioCodec' })
|
||||||
@ApiProperty({ enumName: 'AudioCodec', enum: AudioCodec })
|
|
||||||
targetAudioCodec!: AudioCodec;
|
targetAudioCodec!: AudioCodec;
|
||||||
|
|
||||||
@IsEnum(AudioCodec, { each: true })
|
@ValidateEnum({ enum: AudioCodec, name: 'AudioCodec', each: true })
|
||||||
@ApiProperty({ enumName: 'AudioCodec', enum: AudioCodec, isArray: true })
|
|
||||||
acceptedAudioCodecs!: AudioCodec[];
|
acceptedAudioCodecs!: AudioCodec[];
|
||||||
|
|
||||||
@IsEnum(VideoContainer, { each: true })
|
@ValidateEnum({ enum: VideoContainer, name: 'VideoContainer', each: true })
|
||||||
@ApiProperty({ enumName: 'VideoContainer', enum: VideoContainer, isArray: true })
|
|
||||||
acceptedContainers!: VideoContainer[];
|
acceptedContainers!: VideoContainer[];
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
@@ -131,8 +124,7 @@ export class SystemConfigFFmpegDto {
|
|||||||
@ValidateBoolean()
|
@ValidateBoolean()
|
||||||
temporalAQ!: boolean;
|
temporalAQ!: boolean;
|
||||||
|
|
||||||
@IsEnum(CQMode)
|
@ValidateEnum({ enum: CQMode, name: 'CQMode' })
|
||||||
@ApiProperty({ enumName: 'CQMode', enum: CQMode })
|
|
||||||
cqMode!: CQMode;
|
cqMode!: CQMode;
|
||||||
|
|
||||||
@ValidateBoolean()
|
@ValidateBoolean()
|
||||||
@@ -141,19 +133,16 @@ export class SystemConfigFFmpegDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
preferredHwDevice!: string;
|
preferredHwDevice!: string;
|
||||||
|
|
||||||
@IsEnum(TranscodePolicy)
|
@ValidateEnum({ enum: TranscodePolicy, name: 'TranscodePolicy' })
|
||||||
@ApiProperty({ enumName: 'TranscodePolicy', enum: TranscodePolicy })
|
|
||||||
transcode!: TranscodePolicy;
|
transcode!: TranscodePolicy;
|
||||||
|
|
||||||
@IsEnum(TranscodeHWAccel)
|
@ValidateEnum({ enum: TranscodeHWAccel, name: 'TranscodeHWAccel' })
|
||||||
@ApiProperty({ enumName: 'TranscodeHWAccel', enum: TranscodeHWAccel })
|
|
||||||
accel!: TranscodeHWAccel;
|
accel!: TranscodeHWAccel;
|
||||||
|
|
||||||
@ValidateBoolean()
|
@ValidateBoolean()
|
||||||
accelDecode!: boolean;
|
accelDecode!: boolean;
|
||||||
|
|
||||||
@IsEnum(ToneMapping)
|
@ValidateEnum({ enum: ToneMapping, name: 'ToneMapping' })
|
||||||
@ApiProperty({ enumName: 'ToneMapping', enum: ToneMapping })
|
|
||||||
tonemap!: ToneMapping;
|
tonemap!: ToneMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,8 +253,7 @@ class SystemConfigLoggingDto {
|
|||||||
@ValidateBoolean()
|
@ValidateBoolean()
|
||||||
enabled!: boolean;
|
enabled!: boolean;
|
||||||
|
|
||||||
@ApiProperty({ enum: LogLevel, enumName: 'LogLevel' })
|
@ValidateEnum({ enum: LogLevel, name: 'LogLevel' })
|
||||||
@IsEnum(LogLevel)
|
|
||||||
level!: LogLevel;
|
level!: LogLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,8 +294,7 @@ enum MapTheme {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class MapThemeDto {
|
export class MapThemeDto {
|
||||||
@IsEnum(MapTheme)
|
@ValidateEnum({ enum: MapTheme, name: 'MapTheme' })
|
||||||
@ApiProperty({ enum: MapTheme, enumName: 'MapTheme' })
|
|
||||||
theme!: MapTheme;
|
theme!: MapTheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,8 +355,7 @@ class SystemConfigOAuthDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
clientSecret!: string;
|
clientSecret!: string;
|
||||||
|
|
||||||
@IsEnum(OAuthTokenEndpointAuthMethod)
|
@ValidateEnum({ enum: OAuthTokenEndpointAuthMethod, name: 'OAuthTokenEndpointAuthMethod' })
|
||||||
@ApiProperty({ enum: OAuthTokenEndpointAuthMethod, enumName: 'OAuthTokenEndpointAuthMethod' })
|
|
||||||
tokenEndpointAuthMethod!: OAuthTokenEndpointAuthMethod;
|
tokenEndpointAuthMethod!: OAuthTokenEndpointAuthMethod;
|
||||||
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@@ -431,7 +417,7 @@ class SystemConfigReverseGeocodingDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SystemConfigFacesDto {
|
class SystemConfigFacesDto {
|
||||||
@IsBoolean()
|
@ValidateBoolean()
|
||||||
import!: boolean;
|
import!: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,12 +436,12 @@ class SystemConfigServerDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
loginPageMessage!: string;
|
loginPageMessage!: string;
|
||||||
|
|
||||||
@IsBoolean()
|
@ValidateBoolean()
|
||||||
publicUsers!: boolean;
|
publicUsers!: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class SystemConfigSmtpTransportDto {
|
class SystemConfigSmtpTransportDto {
|
||||||
@IsBoolean()
|
@ValidateBoolean()
|
||||||
ignoreCert!: boolean;
|
ignoreCert!: boolean;
|
||||||
|
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@@ -475,7 +461,7 @@ class SystemConfigSmtpTransportDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class SystemConfigSmtpDto {
|
export class SystemConfigSmtpDto {
|
||||||
@IsBoolean()
|
@ValidateBoolean()
|
||||||
enabled!: boolean;
|
enabled!: boolean;
|
||||||
|
|
||||||
@ValidateIf(isEmailNotificationEnabled)
|
@ValidateIf(isEmailNotificationEnabled)
|
||||||
@@ -548,8 +534,7 @@ export class SystemConfigThemeDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SystemConfigGeneratedImageDto {
|
class SystemConfigGeneratedImageDto {
|
||||||
@IsEnum(ImageFormat)
|
@ValidateEnum({ enum: ImageFormat, name: 'ImageFormat' })
|
||||||
@ApiProperty({ enumName: 'ImageFormat', enum: ImageFormat })
|
|
||||||
format!: ImageFormat;
|
format!: ImageFormat;
|
||||||
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@@ -567,13 +552,10 @@ class SystemConfigGeneratedImageDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SystemConfigGeneratedFullsizeImageDto {
|
class SystemConfigGeneratedFullsizeImageDto {
|
||||||
@IsBoolean()
|
@ValidateBoolean()
|
||||||
@Type(() => Boolean)
|
|
||||||
@ApiProperty({ type: 'boolean' })
|
|
||||||
enabled!: boolean;
|
enabled!: boolean;
|
||||||
|
|
||||||
@IsEnum(ImageFormat)
|
@ValidateEnum({ enum: ImageFormat, name: 'ImageFormat' })
|
||||||
@ApiProperty({ enumName: 'ImageFormat', enum: ImageFormat })
|
|
||||||
format!: ImageFormat;
|
format!: ImageFormat;
|
||||||
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@@ -600,8 +582,7 @@ export class SystemConfigImageDto {
|
|||||||
@IsObject()
|
@IsObject()
|
||||||
fullsize!: SystemConfigGeneratedFullsizeImageDto;
|
fullsize!: SystemConfigGeneratedFullsizeImageDto;
|
||||||
|
|
||||||
@IsEnum(Colorspace)
|
@ValidateEnum({ enum: Colorspace, name: 'Colorspace' })
|
||||||
@ApiProperty({ enumName: 'Colorspace', enum: Colorspace })
|
|
||||||
colorspace!: Colorspace;
|
colorspace!: Colorspace;
|
||||||
|
|
||||||
@ValidateBoolean()
|
@ValidateBoolean()
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { IsBoolean } from 'class-validator';
|
import { ValidateBoolean } from 'src/validation';
|
||||||
|
|
||||||
export class AdminOnboardingUpdateDto {
|
export class AdminOnboardingUpdateDto {
|
||||||
@IsBoolean()
|
@ValidateBoolean()
|
||||||
isOnboarded!: boolean;
|
isOnboarded!: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AdminOnboardingResponseDto {
|
export class AdminOnboardingResponseDto {
|
||||||
|
@ValidateBoolean()
|
||||||
isOnboarded!: boolean;
|
isOnboarded!: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
|
||||||
import { IsEnum, IsString } from 'class-validator';
|
import { IsString } from 'class-validator';
|
||||||
import { AssetOrder, AssetVisibility } from 'src/enum';
|
import { AssetOrder, AssetVisibility } from 'src/enum';
|
||||||
import { Optional, ValidateAssetVisibility, ValidateBoolean, ValidateUUID } from 'src/validation';
|
import { ValidateBoolean, ValidateEnum, ValidateUUID } from 'src/validation';
|
||||||
|
|
||||||
export class TimeBucketDto {
|
export class TimeBucketDto {
|
||||||
@ValidateUUID({ optional: true, description: 'Filter assets by specific user ID' })
|
@ValidateUUID({ optional: true, description: 'Filter assets by specific user ID' })
|
||||||
@@ -38,16 +38,17 @@ export class TimeBucketDto {
|
|||||||
@ValidateBoolean({ optional: true, description: 'Include assets shared by partners' })
|
@ValidateBoolean({ optional: true, description: 'Include assets shared by partners' })
|
||||||
withPartners?: boolean;
|
withPartners?: boolean;
|
||||||
|
|
||||||
@IsEnum(AssetOrder)
|
@ValidateEnum({
|
||||||
@Optional()
|
|
||||||
@ApiProperty({
|
|
||||||
enum: AssetOrder,
|
enum: AssetOrder,
|
||||||
enumName: 'AssetOrder',
|
name: 'AssetOrder',
|
||||||
description: 'Sort order for assets within time buckets (ASC for oldest first, DESC for newest first)',
|
description: 'Sort order for assets within time buckets (ASC for oldest first, DESC for newest first)',
|
||||||
|
optional: true,
|
||||||
})
|
})
|
||||||
order?: AssetOrder;
|
order?: AssetOrder;
|
||||||
|
|
||||||
@ValidateAssetVisibility({
|
@ValidateEnum({
|
||||||
|
enum: AssetVisibility,
|
||||||
|
name: 'AssetVisibility',
|
||||||
optional: true,
|
optional: true,
|
||||||
description: 'Filter by asset visibility status (ARCHIVE, TIMELINE, HIDDEN, LOCKED)',
|
description: 'Filter by asset visibility status (ARCHIVE, TIMELINE, HIDDEN, LOCKED)',
|
||||||
})
|
})
|
||||||
@@ -93,10 +94,10 @@ export class TimeBucketAssetResponseDto {
|
|||||||
})
|
})
|
||||||
isFavorite!: boolean[];
|
isFavorite!: boolean[];
|
||||||
|
|
||||||
@ApiProperty({
|
@ValidateEnum({
|
||||||
enum: AssetVisibility,
|
enum: AssetVisibility,
|
||||||
enumName: 'AssetVisibility',
|
name: 'AssetVisibility',
|
||||||
isArray: true,
|
each: true,
|
||||||
description: 'Array of visibility statuses for each asset (e.g., ARCHIVE, TIMELINE, HIDDEN, LOCKED)',
|
description: 'Array of visibility statuses for each asset (e.g., ARCHIVE, TIMELINE, HIDDEN, LOCKED)',
|
||||||
})
|
})
|
||||||
visibility!: AssetVisibility[];
|
visibility!: AssetVisibility[];
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import { IsDateString, IsEnum, IsInt, IsPositive, ValidateNested } from 'class-validator';
|
import { IsDateString, IsInt, IsPositive, ValidateNested } from 'class-validator';
|
||||||
import { AssetOrder, UserAvatarColor } from 'src/enum';
|
import { AssetOrder, UserAvatarColor } from 'src/enum';
|
||||||
import { UserPreferences } from 'src/types';
|
import { UserPreferences } from 'src/types';
|
||||||
import { Optional, ValidateBoolean } from 'src/validation';
|
import { Optional, ValidateBoolean, ValidateEnum } from 'src/validation';
|
||||||
|
|
||||||
class AvatarUpdate {
|
class AvatarUpdate {
|
||||||
@Optional()
|
@ValidateEnum({ enum: UserAvatarColor, name: 'UserAvatarColor', optional: true })
|
||||||
@IsEnum(UserAvatarColor)
|
|
||||||
@ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor })
|
|
||||||
color?: UserAvatarColor;
|
color?: UserAvatarColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,8 +21,7 @@ class RatingsUpdate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class AlbumsUpdate {
|
class AlbumsUpdate {
|
||||||
@IsEnum(AssetOrder)
|
@ValidateEnum({ enum: AssetOrder, name: 'AssetOrder', optional: true })
|
||||||
@ApiProperty({ enumName: 'AssetOrder', enum: AssetOrder })
|
|
||||||
defaultAssetOrder?: AssetOrder;
|
defaultAssetOrder?: AssetOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,8 +156,7 @@ export class UserPreferencesUpdateDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class AlbumsResponse {
|
class AlbumsResponse {
|
||||||
@IsEnum(AssetOrder)
|
@ValidateEnum({ enum: AssetOrder, name: 'AssetOrder' })
|
||||||
@ApiProperty({ enumName: 'AssetOrder', enum: AssetOrder })
|
|
||||||
defaultAssetOrder: AssetOrder = AssetOrder.DESC;
|
defaultAssetOrder: AssetOrder = AssetOrder.DESC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-19
@@ -1,10 +1,10 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Transform } from 'class-transformer';
|
import { Transform } from 'class-transformer';
|
||||||
import { IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumber, IsString, Min } from 'class-validator';
|
import { IsEmail, IsNotEmpty, IsNumber, IsString, Min } from 'class-validator';
|
||||||
import { User, UserAdmin } from 'src/database';
|
import { User, UserAdmin } from 'src/database';
|
||||||
import { UserAvatarColor, UserMetadataKey, UserStatus } from 'src/enum';
|
import { UserAvatarColor, UserMetadataKey, UserStatus } from 'src/enum';
|
||||||
import { UserMetadataItem } from 'src/types';
|
import { UserMetadataItem } from 'src/types';
|
||||||
import { Optional, PinCode, ValidateBoolean, ValidateUUID, toEmail, toSanitized } from 'src/validation';
|
import { Optional, PinCode, ValidateBoolean, ValidateEnum, ValidateUUID, toEmail, toSanitized } from 'src/validation';
|
||||||
|
|
||||||
export class UserUpdateMeDto {
|
export class UserUpdateMeDto {
|
||||||
@Optional()
|
@Optional()
|
||||||
@@ -23,9 +23,7 @@ export class UserUpdateMeDto {
|
|||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
name?: string;
|
name?: string;
|
||||||
|
|
||||||
@Optional({ nullable: true })
|
@ValidateEnum({ enum: UserAvatarColor, name: 'UserAvatarColor', optional: true, nullable: true })
|
||||||
@IsEnum(UserAvatarColor)
|
|
||||||
@ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor })
|
|
||||||
avatarColor?: UserAvatarColor | null;
|
avatarColor?: UserAvatarColor | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +32,7 @@ export class UserResponseDto {
|
|||||||
name!: string;
|
name!: string;
|
||||||
email!: string;
|
email!: string;
|
||||||
profileImagePath!: string;
|
profileImagePath!: string;
|
||||||
@ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor })
|
@ValidateEnum({ enum: UserAvatarColor, name: 'UserAvatarColor' })
|
||||||
avatarColor!: UserAvatarColor;
|
avatarColor!: UserAvatarColor;
|
||||||
profileChangedAt!: Date;
|
profileChangedAt!: Date;
|
||||||
}
|
}
|
||||||
@@ -84,9 +82,7 @@ export class UserAdminCreateDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
name!: string;
|
name!: string;
|
||||||
|
|
||||||
@Optional({ nullable: true })
|
@ValidateEnum({ enum: UserAvatarColor, name: 'UserAvatarColor', optional: true, nullable: true })
|
||||||
@IsEnum(UserAvatarColor)
|
|
||||||
@ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor })
|
|
||||||
avatarColor?: UserAvatarColor | null;
|
avatarColor?: UserAvatarColor | null;
|
||||||
|
|
||||||
@Optional({ nullable: true })
|
@Optional({ nullable: true })
|
||||||
@@ -103,12 +99,10 @@ export class UserAdminCreateDto {
|
|||||||
@ValidateBoolean({ optional: true })
|
@ValidateBoolean({ optional: true })
|
||||||
shouldChangePassword?: boolean;
|
shouldChangePassword?: boolean;
|
||||||
|
|
||||||
@Optional()
|
@ValidateBoolean({ optional: true })
|
||||||
@IsBoolean()
|
|
||||||
notify?: boolean;
|
notify?: boolean;
|
||||||
|
|
||||||
@Optional()
|
@ValidateBoolean({ optional: true })
|
||||||
@IsBoolean()
|
|
||||||
isAdmin?: boolean;
|
isAdmin?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,9 +125,7 @@ export class UserAdminUpdateDto {
|
|||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
name?: string;
|
name?: string;
|
||||||
|
|
||||||
@Optional({ nullable: true })
|
@ValidateEnum({ enum: UserAvatarColor, name: 'UserAvatarColor', optional: true, nullable: true })
|
||||||
@IsEnum(UserAvatarColor)
|
|
||||||
@ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor })
|
|
||||||
avatarColor?: UserAvatarColor | null;
|
avatarColor?: UserAvatarColor | null;
|
||||||
|
|
||||||
@Optional({ nullable: true })
|
@Optional({ nullable: true })
|
||||||
@@ -150,8 +142,7 @@ export class UserAdminUpdateDto {
|
|||||||
@ApiProperty({ type: 'integer', format: 'int64' })
|
@ApiProperty({ type: 'integer', format: 'int64' })
|
||||||
quotaSizeInBytes?: number | null;
|
quotaSizeInBytes?: number | null;
|
||||||
|
|
||||||
@Optional()
|
@ValidateBoolean({ optional: true })
|
||||||
@IsBoolean()
|
|
||||||
isAdmin?: boolean;
|
isAdmin?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +163,7 @@ export class UserAdminResponseDto extends UserResponseDto {
|
|||||||
quotaSizeInBytes!: number | null;
|
quotaSizeInBytes!: number | null;
|
||||||
@ApiProperty({ type: 'integer', format: 'int64' })
|
@ApiProperty({ type: 'integer', format: 'int64' })
|
||||||
quotaUsageInBytes!: number | null;
|
quotaUsageInBytes!: number | null;
|
||||||
@ApiProperty({ enumName: 'UserStatus', enum: UserStatus })
|
@ValidateEnum({ enum: UserStatus, name: 'UserStatus' })
|
||||||
status!: string;
|
status!: string;
|
||||||
license!: UserLicense | null;
|
license!: UserLicense | null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,59 +35,59 @@ type Item<T extends EmitEvent> = {
|
|||||||
|
|
||||||
type EventMap = {
|
type EventMap = {
|
||||||
// app events
|
// app events
|
||||||
'app.bootstrap': [];
|
AppBootstrap: [];
|
||||||
'app.shutdown': [];
|
AppShutdown: [];
|
||||||
|
|
||||||
'config.init': [{ newConfig: SystemConfig }];
|
ConfigInit: [{ newConfig: SystemConfig }];
|
||||||
// config events
|
// config events
|
||||||
'config.update': [
|
ConfigUpdate: [
|
||||||
{
|
{
|
||||||
newConfig: SystemConfig;
|
newConfig: SystemConfig;
|
||||||
oldConfig: SystemConfig;
|
oldConfig: SystemConfig;
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
'config.validate': [{ newConfig: SystemConfig; oldConfig: SystemConfig }];
|
ConfigValidate: [{ newConfig: SystemConfig; oldConfig: SystemConfig }];
|
||||||
|
|
||||||
// album events
|
// album events
|
||||||
'album.update': [{ id: string; recipientId: string }];
|
AlbumUpdate: [{ id: string; recipientId: string }];
|
||||||
'album.invite': [{ id: string; userId: string }];
|
AlbumInvite: [{ id: string; userId: string }];
|
||||||
|
|
||||||
// asset events
|
// asset events
|
||||||
'asset.tag': [{ assetId: string }];
|
AssetTag: [{ assetId: string }];
|
||||||
'asset.untag': [{ assetId: string }];
|
AssetUntag: [{ assetId: string }];
|
||||||
'asset.hide': [{ assetId: string; userId: string }];
|
AssetHide: [{ assetId: string; userId: string }];
|
||||||
'asset.show': [{ assetId: string; userId: string }];
|
AssetShow: [{ assetId: string; userId: string }];
|
||||||
'asset.trash': [{ assetId: string; userId: string }];
|
AssetTrash: [{ assetId: string; userId: string }];
|
||||||
'asset.delete': [{ assetId: string; userId: string }];
|
AssetDelete: [{ assetId: string; userId: string }];
|
||||||
'asset.metadataExtracted': [{ assetId: string; userId: string; source?: JobSource }];
|
AssetMetadataExtracted: [{ assetId: string; userId: string; source?: JobSource }];
|
||||||
|
|
||||||
// asset bulk events
|
// asset bulk events
|
||||||
'assets.trash': [{ assetIds: string[]; userId: string }];
|
AssetTrashAll: [{ assetIds: string[]; userId: string }];
|
||||||
'assets.delete': [{ assetIds: string[]; userId: string }];
|
AssetDeleteAll: [{ assetIds: string[]; userId: string }];
|
||||||
'assets.restore': [{ assetIds: string[]; userId: string }];
|
AssetRestoreAll: [{ assetIds: string[]; userId: string }];
|
||||||
|
|
||||||
'job.start': [QueueName, JobItem];
|
JobStart: [QueueName, JobItem];
|
||||||
'job.failed': [{ job: JobItem; error: Error | any }];
|
JobFailed: [{ job: JobItem; error: Error | any }];
|
||||||
|
|
||||||
// session events
|
// session events
|
||||||
'session.delete': [{ sessionId: string }];
|
SessionDelete: [{ sessionId: string }];
|
||||||
|
|
||||||
// stack events
|
// stack events
|
||||||
'stack.create': [{ stackId: string; userId: string }];
|
StackCreate: [{ stackId: string; userId: string }];
|
||||||
'stack.update': [{ stackId: string; userId: string }];
|
StackUpdate: [{ stackId: string; userId: string }];
|
||||||
'stack.delete': [{ stackId: string; userId: string }];
|
StackDelete: [{ stackId: string; userId: string }];
|
||||||
|
|
||||||
// stack bulk events
|
// stack bulk events
|
||||||
'stacks.delete': [{ stackIds: string[]; userId: string }];
|
StackDeleteAll: [{ stackIds: string[]; userId: string }];
|
||||||
|
|
||||||
// user events
|
// user events
|
||||||
'user.signup': [{ notify: boolean; id: string; tempPassword?: string }];
|
UserSignup: [{ notify: boolean; id: string; tempPassword?: string }];
|
||||||
|
|
||||||
// websocket events
|
// websocket events
|
||||||
'websocket.connect': [{ userId: string }];
|
WebsocketConnect: [{ userId: string }];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const serverEvents = ['config.update'] as const;
|
export const serverEvents = ['ConfigUpdate'] as const;
|
||||||
export type ServerEvents = (typeof serverEvents)[number];
|
export type ServerEvents = (typeof serverEvents)[number];
|
||||||
|
|
||||||
export type EmitEvent = keyof EventMap;
|
export type EmitEvent = keyof EventMap;
|
||||||
@@ -213,7 +213,7 @@ export class EventRepository implements OnGatewayConnection, OnGatewayDisconnect
|
|||||||
if (auth.session) {
|
if (auth.session) {
|
||||||
await client.join(auth.session.id);
|
await client.join(auth.session.id);
|
||||||
}
|
}
|
||||||
await this.onEvent({ name: 'websocket.connect', args: [{ userId: auth.user.id }], server: false });
|
await this.onEvent({ name: 'WebsocketConnect', args: [{ userId: auth.user.id }], server: false });
|
||||||
} catch (error: Error | any) {
|
} catch (error: Error | any) {
|
||||||
this.logger.error(`Websocket connection error: ${error}`, error?.stack);
|
this.logger.error(`Websocket connection error: ${error}`, error?.stack);
|
||||||
client.emit('error', 'unauthorized');
|
client.emit('error', 'unauthorized');
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export class JobRepository {
|
|||||||
this.logger.debug(`Starting worker for queue: ${queueName}`);
|
this.logger.debug(`Starting worker for queue: ${queueName}`);
|
||||||
this.workers[queueName] = new Worker(
|
this.workers[queueName] = new Worker(
|
||||||
queueName,
|
queueName,
|
||||||
(job) => this.eventRepository.emit('job.start', queueName, job as JobItem),
|
(job) => this.eventRepository.emit('JobStart', queueName, job as JobItem),
|
||||||
{ ...bull.config, concurrency: 1 },
|
{ ...bull.config, concurrency: 1 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -265,6 +265,9 @@ export class MapRepository {
|
|||||||
throw new Error(`Geodata file ${cities500} not found`);
|
throw new Error(`Geodata file ${cities500} not found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.log(`Starting geodata import`);
|
||||||
|
const startTime = performance.now();
|
||||||
|
|
||||||
const input = createReadStream(cities500, { highWaterMark: 512 * 1024 * 1024 });
|
const input = createReadStream(cities500, { highWaterMark: 512 * 1024 * 1024 });
|
||||||
let bufferGeodata = [];
|
let bufferGeodata = [];
|
||||||
const lineReader = readLine.createInterface({ input });
|
const lineReader = readLine.createInterface({ input });
|
||||||
@@ -314,7 +317,20 @@ export class MapRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.db.insertInto('geodata_places').values(bufferGeodata).execute();
|
if (bufferGeodata.length > 0) {
|
||||||
|
await this.db.insertInto('geodata_places').values(bufferGeodata).execute();
|
||||||
|
count += bufferGeodata.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(futures);
|
||||||
|
|
||||||
|
const duration = performance.now() - startTime;
|
||||||
|
const seconds = duration / 1000;
|
||||||
|
const recordsPerSecond = Math.round(count / seconds);
|
||||||
|
|
||||||
|
this.logger.log(
|
||||||
|
`Successfully imported ${count} geodata records in ${seconds.toFixed(2)}s (${recordsPerSecond} records/second)`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadAdmin(filePath: string) {
|
private async loadAdmin(filePath: string) {
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ describe(AlbumService.name, () => {
|
|||||||
expect(mocks.user.get).toHaveBeenCalledWith('user-id', {});
|
expect(mocks.user.get).toHaveBeenCalledWith('user-id', {});
|
||||||
expect(mocks.user.getMetadata).toHaveBeenCalledWith(authStub.admin.user.id);
|
expect(mocks.user.getMetadata).toHaveBeenCalledWith(authStub.admin.user.id);
|
||||||
expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['123']), false);
|
expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['123']), false);
|
||||||
expect(mocks.event.emit).toHaveBeenCalledWith('album.invite', {
|
expect(mocks.event.emit).toHaveBeenCalledWith('AlbumInvite', {
|
||||||
id: albumStub.empty.id,
|
id: albumStub.empty.id,
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
});
|
});
|
||||||
@@ -209,7 +209,7 @@ describe(AlbumService.name, () => {
|
|||||||
expect(mocks.user.get).toHaveBeenCalledWith('user-id', {});
|
expect(mocks.user.get).toHaveBeenCalledWith('user-id', {});
|
||||||
expect(mocks.user.getMetadata).toHaveBeenCalledWith(authStub.admin.user.id);
|
expect(mocks.user.getMetadata).toHaveBeenCalledWith(authStub.admin.user.id);
|
||||||
expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['123']), false);
|
expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['123']), false);
|
||||||
expect(mocks.event.emit).toHaveBeenCalledWith('album.invite', {
|
expect(mocks.event.emit).toHaveBeenCalledWith('AlbumInvite', {
|
||||||
id: albumStub.empty.id,
|
id: albumStub.empty.id,
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
});
|
});
|
||||||
@@ -413,7 +413,7 @@ describe(AlbumService.name, () => {
|
|||||||
usersId: authStub.user2.user.id,
|
usersId: authStub.user2.user.id,
|
||||||
albumsId: albumStub.sharedWithAdmin.id,
|
albumsId: albumStub.sharedWithAdmin.id,
|
||||||
});
|
});
|
||||||
expect(mocks.event.emit).toHaveBeenCalledWith('album.invite', {
|
expect(mocks.event.emit).toHaveBeenCalledWith('AlbumInvite', {
|
||||||
id: albumStub.sharedWithAdmin.id,
|
id: albumStub.sharedWithAdmin.id,
|
||||||
userId: userStub.user2.id,
|
userId: userStub.user2.id,
|
||||||
});
|
});
|
||||||
@@ -662,7 +662,7 @@ describe(AlbumService.name, () => {
|
|||||||
albumThumbnailAssetId: 'asset-1',
|
albumThumbnailAssetId: 'asset-1',
|
||||||
});
|
});
|
||||||
expect(mocks.album.addAssetIds).toHaveBeenCalledWith('album-123', ['asset-1', 'asset-2', 'asset-3']);
|
expect(mocks.album.addAssetIds).toHaveBeenCalledWith('album-123', ['asset-1', 'asset-2', 'asset-3']);
|
||||||
expect(mocks.event.emit).toHaveBeenCalledWith('album.update', {
|
expect(mocks.event.emit).toHaveBeenCalledWith('AlbumUpdate', {
|
||||||
id: 'album-123',
|
id: 'album-123',
|
||||||
recipientId: 'admin_id',
|
recipientId: 'admin_id',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ export class AlbumService extends BaseService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
for (const { userId } of albumUsers) {
|
for (const { userId } of albumUsers) {
|
||||||
await this.eventRepository.emit('album.invite', { id: album.id, userId });
|
await this.eventRepository.emit('AlbumInvite', { id: album.id, userId });
|
||||||
}
|
}
|
||||||
|
|
||||||
return mapAlbumWithAssets(album);
|
return mapAlbumWithAssets(album);
|
||||||
@@ -187,7 +187,7 @@ export class AlbumService extends BaseService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
for (const recipientId of allUsersExceptUs) {
|
for (const recipientId of allUsersExceptUs) {
|
||||||
await this.eventRepository.emit('album.update', { id, recipientId });
|
await this.eventRepository.emit('AlbumUpdate', { id, recipientId });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +233,7 @@ export class AlbumService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await this.albumUserRepository.create({ usersId: userId, albumsId: id, role });
|
await this.albumUserRepository.create({ usersId: userId, albumsId: id, role });
|
||||||
await this.eventRepository.emit('album.invite', { id, userId });
|
await this.eventRepository.emit('AlbumInvite', { id, userId });
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.findOrFail(id, { withAssets: true }).then(mapAlbumWithoutAssets);
|
return this.findOrFail(id, { withAssets: true }).then(mapAlbumWithoutAssets);
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ export class AssetMediaService extends BaseService {
|
|||||||
const copiedPhoto = await this.createCopy(asset);
|
const copiedPhoto = await this.createCopy(asset);
|
||||||
// and immediate trash it
|
// and immediate trash it
|
||||||
await this.assetRepository.updateAll([copiedPhoto.id], { deletedAt: new Date(), status: AssetStatus.TRASHED });
|
await this.assetRepository.updateAll([copiedPhoto.id], { deletedAt: new Date(), status: AssetStatus.TRASHED });
|
||||||
await this.eventRepository.emit('asset.trash', { assetId: copiedPhoto.id, userId: auth.user.id });
|
await this.eventRepository.emit('AssetTrash', { assetId: copiedPhoto.id, userId: auth.user.id });
|
||||||
|
|
||||||
await this.userRepository.updateUsage(auth.user.id, file.size);
|
await this.userRepository.updateUsage(auth.user.id, file.size);
|
||||||
|
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ describe(AssetService.name, () => {
|
|||||||
id: assetStub.livePhotoMotionAsset.id,
|
id: assetStub.livePhotoMotionAsset.id,
|
||||||
visibility: AssetVisibility.TIMELINE,
|
visibility: AssetVisibility.TIMELINE,
|
||||||
});
|
});
|
||||||
expect(mocks.event.emit).not.toHaveBeenCalledWith('asset.show', {
|
expect(mocks.event.emit).not.toHaveBeenCalledWith('AssetShow', {
|
||||||
assetId: assetStub.livePhotoMotionAsset.id,
|
assetId: assetStub.livePhotoMotionAsset.id,
|
||||||
userId: userStub.admin.id,
|
userId: userStub.admin.id,
|
||||||
});
|
});
|
||||||
@@ -279,7 +279,7 @@ describe(AssetService.name, () => {
|
|||||||
id: assetStub.livePhotoMotionAsset.id,
|
id: assetStub.livePhotoMotionAsset.id,
|
||||||
visibility: AssetVisibility.TIMELINE,
|
visibility: AssetVisibility.TIMELINE,
|
||||||
});
|
});
|
||||||
expect(mocks.event.emit).not.toHaveBeenCalledWith('asset.show', {
|
expect(mocks.event.emit).not.toHaveBeenCalledWith('AssetShow', {
|
||||||
assetId: assetStub.livePhotoMotionAsset.id,
|
assetId: assetStub.livePhotoMotionAsset.id,
|
||||||
userId: userStub.admin.id,
|
userId: userStub.admin.id,
|
||||||
});
|
});
|
||||||
@@ -303,7 +303,7 @@ describe(AssetService.name, () => {
|
|||||||
id: assetStub.livePhotoMotionAsset.id,
|
id: assetStub.livePhotoMotionAsset.id,
|
||||||
visibility: AssetVisibility.TIMELINE,
|
visibility: AssetVisibility.TIMELINE,
|
||||||
});
|
});
|
||||||
expect(mocks.event.emit).not.toHaveBeenCalledWith('asset.show', {
|
expect(mocks.event.emit).not.toHaveBeenCalledWith('AssetShow', {
|
||||||
assetId: assetStub.livePhotoMotionAsset.id,
|
assetId: assetStub.livePhotoMotionAsset.id,
|
||||||
userId: userStub.admin.id,
|
userId: userStub.admin.id,
|
||||||
});
|
});
|
||||||
@@ -327,7 +327,7 @@ describe(AssetService.name, () => {
|
|||||||
id: assetStub.livePhotoMotionAsset.id,
|
id: assetStub.livePhotoMotionAsset.id,
|
||||||
visibility: AssetVisibility.HIDDEN,
|
visibility: AssetVisibility.HIDDEN,
|
||||||
});
|
});
|
||||||
expect(mocks.event.emit).toHaveBeenCalledWith('asset.hide', {
|
expect(mocks.event.emit).toHaveBeenCalledWith('AssetHide', {
|
||||||
assetId: assetStub.livePhotoMotionAsset.id,
|
assetId: assetStub.livePhotoMotionAsset.id,
|
||||||
userId: userStub.admin.id,
|
userId: userStub.admin.id,
|
||||||
});
|
});
|
||||||
@@ -360,7 +360,7 @@ describe(AssetService.name, () => {
|
|||||||
id: assetStub.livePhotoMotionAsset.id,
|
id: assetStub.livePhotoMotionAsset.id,
|
||||||
visibility: assetStub.livePhotoStillAsset.visibility,
|
visibility: assetStub.livePhotoStillAsset.visibility,
|
||||||
});
|
});
|
||||||
expect(mocks.event.emit).toHaveBeenCalledWith('asset.show', {
|
expect(mocks.event.emit).toHaveBeenCalledWith('AssetShow', {
|
||||||
assetId: assetStub.livePhotoMotionAsset.id,
|
assetId: assetStub.livePhotoMotionAsset.id,
|
||||||
userId: userStub.admin.id,
|
userId: userStub.admin.id,
|
||||||
});
|
});
|
||||||
@@ -484,7 +484,7 @@ describe(AssetService.name, () => {
|
|||||||
|
|
||||||
await sut.deleteAll(authStub.user1, { ids: ['asset1', 'asset2'], force: true });
|
await sut.deleteAll(authStub.user1, { ids: ['asset1', 'asset2'], force: true });
|
||||||
|
|
||||||
expect(mocks.event.emit).toHaveBeenCalledWith('assets.delete', {
|
expect(mocks.event.emit).toHaveBeenCalledWith('AssetDeleteAll', {
|
||||||
assetIds: ['asset1', 'asset2'],
|
assetIds: ['asset1', 'asset2'],
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ export class AssetService extends BaseService {
|
|||||||
await this.userRepository.updateUsage(asset.ownerId, -(asset.exifInfo?.fileSizeInByte || 0));
|
await this.userRepository.updateUsage(asset.ownerId, -(asset.exifInfo?.fileSizeInByte || 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.eventRepository.emit('asset.delete', { assetId: id, userId: asset.ownerId });
|
await this.eventRepository.emit('AssetDelete', { assetId: id, userId: asset.ownerId });
|
||||||
|
|
||||||
// delete the motion if it is not used by another asset
|
// delete the motion if it is not used by another asset
|
||||||
if (asset.livePhotoVideoId) {
|
if (asset.livePhotoVideoId) {
|
||||||
@@ -241,7 +241,10 @@ export class AssetService extends BaseService {
|
|||||||
deletedAt: new Date(),
|
deletedAt: new Date(),
|
||||||
status: force ? AssetStatus.DELETED : AssetStatus.TRASHED,
|
status: force ? AssetStatus.DELETED : AssetStatus.TRASHED,
|
||||||
});
|
});
|
||||||
await this.eventRepository.emit(force ? 'assets.delete' : 'assets.trash', { assetIds: ids, userId: auth.user.id });
|
await this.eventRepository.emit(force ? 'AssetDeleteAll' : 'AssetTrashAll', {
|
||||||
|
assetIds: ids,
|
||||||
|
userId: auth.user.id,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async run(auth: AuthDto, dto: AssetJobsDto) {
|
async run(auth: AuthDto, dto: AssetJobsDto) {
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ describe(AuthService.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(mocks.session.delete).toHaveBeenCalledWith('token123');
|
expect(mocks.session.delete).toHaveBeenCalledWith('token123');
|
||||||
expect(mocks.event.emit).toHaveBeenCalledWith('session.delete', { sessionId: 'token123' });
|
expect(mocks.event.emit).toHaveBeenCalledWith('SessionDelete', { sessionId: 'token123' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the default redirect if auth type is OAUTH but oauth is not enabled', async () => {
|
it('should return the default redirect if auth type is OAUTH but oauth is not enabled', async () => {
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export class AuthService extends BaseService {
|
|||||||
async logout(auth: AuthDto, authType: AuthType): Promise<LogoutResponseDto> {
|
async logout(auth: AuthDto, authType: AuthType): Promise<LogoutResponseDto> {
|
||||||
if (auth.session) {
|
if (auth.session) {
|
||||||
await this.sessionRepository.delete(auth.session.id);
|
await this.sessionRepository.delete(auth.session.id);
|
||||||
await this.eventRepository.emit('session.delete', { sessionId: auth.session.id });
|
await this.eventRepository.emit('SessionDelete', { sessionId: auth.session.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ import { handlePromiseError } from 'src/utils/misc';
|
|||||||
export class BackupService extends BaseService {
|
export class BackupService extends BaseService {
|
||||||
private backupLock = false;
|
private backupLock = false;
|
||||||
|
|
||||||
@OnEvent({ name: 'config.init', workers: [ImmichWorker.MICROSERVICES] })
|
@OnEvent({ name: 'ConfigInit', workers: [ImmichWorker.MICROSERVICES] })
|
||||||
async onConfigInit({
|
async onConfigInit({
|
||||||
newConfig: {
|
newConfig: {
|
||||||
backup: { database },
|
backup: { database },
|
||||||
},
|
},
|
||||||
}: ArgOf<'config.init'>) {
|
}: ArgOf<'ConfigInit'>) {
|
||||||
this.backupLock = await this.databaseRepository.tryLock(DatabaseLock.BackupDatabase);
|
this.backupLock = await this.databaseRepository.tryLock(DatabaseLock.BackupDatabase);
|
||||||
|
|
||||||
if (this.backupLock) {
|
if (this.backupLock) {
|
||||||
@@ -32,8 +32,8 @@ export class BackupService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'config.update', server: true })
|
@OnEvent({ name: 'ConfigUpdate', server: true })
|
||||||
onConfigUpdate({ newConfig: { backup } }: ArgOf<'config.update'>) {
|
onConfigUpdate({ newConfig: { backup } }: ArgOf<'ConfigUpdate'>) {
|
||||||
if (!this.backupLock) {
|
if (!this.backupLock) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ const messages = {
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DatabaseService extends BaseService {
|
export class DatabaseService extends BaseService {
|
||||||
@OnEvent({ name: 'app.bootstrap', priority: BootstrapEventPriority.DatabaseService })
|
@OnEvent({ name: 'AppBootstrap', priority: BootstrapEventPriority.DatabaseService })
|
||||||
async onBootstrap() {
|
async onBootstrap() {
|
||||||
const version = await this.databaseRepository.getPostgresVersion();
|
const version = await this.databaseRepository.getPostgresVersion();
|
||||||
const current = semver.coerce(version);
|
const current = semver.coerce(version);
|
||||||
|
|||||||
@@ -67,8 +67,8 @@ export class JobService extends BaseService {
|
|||||||
private services: ClassConstructor<unknown>[] = [];
|
private services: ClassConstructor<unknown>[] = [];
|
||||||
private nightlyJobsLock = false;
|
private nightlyJobsLock = false;
|
||||||
|
|
||||||
@OnEvent({ name: 'config.init' })
|
@OnEvent({ name: 'ConfigInit' })
|
||||||
async onConfigInit({ newConfig: config }: ArgOf<'config.init'>) {
|
async onConfigInit({ newConfig: config }: ArgOf<'ConfigInit'>) {
|
||||||
if (this.worker === ImmichWorker.MICROSERVICES) {
|
if (this.worker === ImmichWorker.MICROSERVICES) {
|
||||||
this.updateQueueConcurrency(config);
|
this.updateQueueConcurrency(config);
|
||||||
return;
|
return;
|
||||||
@@ -87,8 +87,8 @@ export class JobService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'config.update', server: true })
|
@OnEvent({ name: 'ConfigUpdate', server: true })
|
||||||
onConfigUpdate({ newConfig: config }: ArgOf<'config.update'>) {
|
onConfigUpdate({ newConfig: config }: ArgOf<'ConfigUpdate'>) {
|
||||||
if (this.worker === ImmichWorker.MICROSERVICES) {
|
if (this.worker === ImmichWorker.MICROSERVICES) {
|
||||||
this.updateQueueConcurrency(config);
|
this.updateQueueConcurrency(config);
|
||||||
return;
|
return;
|
||||||
@@ -101,7 +101,7 @@ export class JobService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'app.bootstrap', priority: BootstrapEventPriority.JobService })
|
@OnEvent({ name: 'AppBootstrap', priority: BootstrapEventPriority.JobService })
|
||||||
onBootstrap() {
|
onBootstrap() {
|
||||||
this.jobRepository.setup(this.services);
|
this.jobRepository.setup(this.services);
|
||||||
if (this.worker === ImmichWorker.MICROSERVICES) {
|
if (this.worker === ImmichWorker.MICROSERVICES) {
|
||||||
@@ -243,8 +243,8 @@ export class JobService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'job.start' })
|
@OnEvent({ name: 'JobStart' })
|
||||||
async onJobStart(...[queueName, job]: ArgsOf<'job.start'>) {
|
async onJobStart(...[queueName, job]: ArgsOf<'JobStart'>) {
|
||||||
const queueMetric = `immich.queues.${snakeCase(queueName)}.active`;
|
const queueMetric = `immich.queues.${snakeCase(queueName)}.active`;
|
||||||
this.telemetryRepository.jobs.addToGauge(queueMetric, 1);
|
this.telemetryRepository.jobs.addToGauge(queueMetric, 1);
|
||||||
try {
|
try {
|
||||||
@@ -255,7 +255,7 @@ export class JobService extends BaseService {
|
|||||||
await this.onDone(job);
|
await this.onDone(job);
|
||||||
}
|
}
|
||||||
} catch (error: Error | any) {
|
} catch (error: Error | any) {
|
||||||
await this.eventRepository.emit('job.failed', { job, error });
|
await this.eventRepository.emit('JobFailed', { job, error });
|
||||||
} finally {
|
} finally {
|
||||||
this.telemetryRepository.jobs.addToGauge(queueMetric, -1);
|
this.telemetryRepository.jobs.addToGauge(queueMetric, -1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,12 +32,12 @@ export class LibraryService extends BaseService {
|
|||||||
private lock = false;
|
private lock = false;
|
||||||
private watchers: Record<string, () => Promise<void>> = {};
|
private watchers: Record<string, () => Promise<void>> = {};
|
||||||
|
|
||||||
@OnEvent({ name: 'config.init', workers: [ImmichWorker.MICROSERVICES] })
|
@OnEvent({ name: 'ConfigInit', workers: [ImmichWorker.MICROSERVICES] })
|
||||||
async onConfigInit({
|
async onConfigInit({
|
||||||
newConfig: {
|
newConfig: {
|
||||||
library: { watch, scan },
|
library: { watch, scan },
|
||||||
},
|
},
|
||||||
}: ArgOf<'config.init'>) {
|
}: ArgOf<'ConfigInit'>) {
|
||||||
// This ensures that library watching only occurs in one microservice
|
// This ensures that library watching only occurs in one microservice
|
||||||
this.lock = await this.databaseRepository.tryLock(DatabaseLock.Library);
|
this.lock = await this.databaseRepository.tryLock(DatabaseLock.Library);
|
||||||
|
|
||||||
@@ -58,8 +58,8 @@ export class LibraryService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'config.update', server: true })
|
@OnEvent({ name: 'ConfigUpdate', server: true })
|
||||||
async onConfigUpdate({ newConfig: { library } }: ArgOf<'config.update'>) {
|
async onConfigUpdate({ newConfig: { library } }: ArgOf<'ConfigUpdate'>) {
|
||||||
if (!this.lock) {
|
if (!this.lock) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -155,7 +155,7 @@ export class LibraryService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'app.shutdown' })
|
@OnEvent({ name: 'AppShutdown' })
|
||||||
async onShutdown() {
|
async onShutdown() {
|
||||||
await this.unwatchAll();
|
await this.unwatchAll();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ interface UpsertFileOptions {
|
|||||||
export class MediaService extends BaseService {
|
export class MediaService extends BaseService {
|
||||||
videoInterfaces: VideoInterfaces = { dri: [], mali: false };
|
videoInterfaces: VideoInterfaces = { dri: [], mali: false };
|
||||||
|
|
||||||
@OnEvent({ name: 'app.bootstrap' })
|
@OnEvent({ name: 'AppBootstrap' })
|
||||||
async onBootstrap() {
|
async onBootstrap() {
|
||||||
const [dri, mali] = await Promise.all([this.getDevices(), this.hasMaliOpenCL()]);
|
const [dri, mali] = await Promise.all([this.getDevices(), this.hasMaliOpenCL()]);
|
||||||
this.videoInterfaces = { dri, mali };
|
this.videoInterfaces = { dri, mali };
|
||||||
|
|||||||
@@ -1368,7 +1368,7 @@ describe(MetadataService.name, () => {
|
|||||||
|
|
||||||
await sut.handleMetadataExtraction({ id: assetStub.livePhotoStillAsset.id });
|
await sut.handleMetadataExtraction({ id: assetStub.livePhotoStillAsset.id });
|
||||||
|
|
||||||
expect(mocks.event.emit).toHaveBeenCalledWith('asset.hide', {
|
expect(mocks.event.emit).toHaveBeenCalledWith('AssetHide', {
|
||||||
userId: assetStub.livePhotoMotionAsset.ownerId,
|
userId: assetStub.livePhotoMotionAsset.ownerId,
|
||||||
assetId: assetStub.livePhotoMotionAsset.id,
|
assetId: assetStub.livePhotoMotionAsset.id,
|
||||||
});
|
});
|
||||||
@@ -1384,7 +1384,7 @@ describe(MetadataService.name, () => {
|
|||||||
|
|
||||||
await sut.handleMetadataExtraction({ id: assetStub.livePhotoStillAsset.id });
|
await sut.handleMetadataExtraction({ id: assetStub.livePhotoStillAsset.id });
|
||||||
|
|
||||||
expect(mocks.event.emit).toHaveBeenCalledWith('asset.metadataExtracted', {
|
expect(mocks.event.emit).toHaveBeenCalledWith('AssetMetadataExtracted', {
|
||||||
assetId: assetStub.livePhotoStillAsset.id,
|
assetId: assetStub.livePhotoStillAsset.id,
|
||||||
userId: assetStub.livePhotoStillAsset.ownerId,
|
userId: assetStub.livePhotoStillAsset.ownerId,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -126,24 +126,24 @@ type Dates = {
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MetadataService extends BaseService {
|
export class MetadataService extends BaseService {
|
||||||
@OnEvent({ name: 'app.bootstrap', workers: [ImmichWorker.MICROSERVICES] })
|
@OnEvent({ name: 'AppBootstrap', workers: [ImmichWorker.MICROSERVICES] })
|
||||||
async onBootstrap() {
|
async onBootstrap() {
|
||||||
this.logger.log('Bootstrapping metadata service');
|
this.logger.log('Bootstrapping metadata service');
|
||||||
await this.init();
|
await this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'app.shutdown' })
|
@OnEvent({ name: 'AppShutdown' })
|
||||||
async onShutdown() {
|
async onShutdown() {
|
||||||
await this.metadataRepository.teardown();
|
await this.metadataRepository.teardown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'config.init', workers: [ImmichWorker.MICROSERVICES] })
|
@OnEvent({ name: 'ConfigInit', workers: [ImmichWorker.MICROSERVICES] })
|
||||||
onConfigInit({ newConfig }: ArgOf<'config.init'>) {
|
onConfigInit({ newConfig }: ArgOf<'ConfigInit'>) {
|
||||||
this.metadataRepository.setMaxConcurrency(newConfig.job.metadataExtraction.concurrency);
|
this.metadataRepository.setMaxConcurrency(newConfig.job.metadataExtraction.concurrency);
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'config.update', workers: [ImmichWorker.MICROSERVICES], server: true })
|
@OnEvent({ name: 'ConfigUpdate', workers: [ImmichWorker.MICROSERVICES], server: true })
|
||||||
onConfigUpdate({ newConfig }: ArgOf<'config.update'>) {
|
onConfigUpdate({ newConfig }: ArgOf<'ConfigUpdate'>) {
|
||||||
this.metadataRepository.setMaxConcurrency(newConfig.job.metadataExtraction.concurrency);
|
this.metadataRepository.setMaxConcurrency(newConfig.job.metadataExtraction.concurrency);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,7 +190,7 @@ export class MetadataService extends BaseService {
|
|||||||
this.albumRepository.removeAssetsFromAll([motionAsset.id]),
|
this.albumRepository.removeAssetsFromAll([motionAsset.id]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await this.eventRepository.emit('asset.hide', { assetId: motionAsset.id, userId: motionAsset.ownerId });
|
await this.eventRepository.emit('AssetHide', { assetId: motionAsset.id, userId: motionAsset.ownerId });
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnJob({ name: JobName.QUEUE_METADATA_EXTRACTION, queue: QueueName.METADATA_EXTRACTION })
|
@OnJob({ name: JobName.QUEUE_METADATA_EXTRACTION, queue: QueueName.METADATA_EXTRACTION })
|
||||||
@@ -313,7 +313,7 @@ export class MetadataService extends BaseService {
|
|||||||
|
|
||||||
await this.assetRepository.upsertJobStatus({ assetId: asset.id, metadataExtractedAt: new Date() });
|
await this.assetRepository.upsertJobStatus({ assetId: asset.id, metadataExtractedAt: new Date() });
|
||||||
|
|
||||||
await this.eventRepository.emit('asset.metadataExtracted', {
|
await this.eventRepository.emit('AssetMetadataExtracted', {
|
||||||
assetId: asset.id,
|
assetId: asset.id,
|
||||||
userId: asset.ownerId,
|
userId: asset.ownerId,
|
||||||
source: data.source,
|
source: data.source,
|
||||||
@@ -351,13 +351,13 @@ export class MetadataService extends BaseService {
|
|||||||
return this.processSidecar(id, false);
|
return this.processSidecar(id, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'asset.tag' })
|
@OnEvent({ name: 'AssetTag' })
|
||||||
async handleTagAsset({ assetId }: ArgOf<'asset.tag'>) {
|
async handleTagAsset({ assetId }: ArgOf<'AssetTag'>) {
|
||||||
await this.jobRepository.queue({ name: JobName.SIDECAR_WRITE, data: { id: assetId, tags: true } });
|
await this.jobRepository.queue({ name: JobName.SIDECAR_WRITE, data: { id: assetId, tags: true } });
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'asset.untag' })
|
@OnEvent({ name: 'AssetUntag' })
|
||||||
async handleUntagAsset({ assetId }: ArgOf<'asset.untag'>) {
|
async handleUntagAsset({ assetId }: ArgOf<'AssetUntag'>) {
|
||||||
await this.jobRepository.queue({ name: JobName.SIDECAR_WRITE, data: { id: assetId, tags: true } });
|
await this.jobRepository.queue({ name: JobName.SIDECAR_WRITE, data: { id: assetId, tags: true } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ describe(NotificationService.name, () => {
|
|||||||
const update = { oldConfig: defaults, newConfig: defaults };
|
const update = { oldConfig: defaults, newConfig: defaults };
|
||||||
expect(sut.onConfigUpdate(update)).toBeUndefined();
|
expect(sut.onConfigUpdate(update)).toBeUndefined();
|
||||||
expect(mocks.event.clientBroadcast).toHaveBeenCalledWith('on_config_update');
|
expect(mocks.event.clientBroadcast).toHaveBeenCalledWith('on_config_update');
|
||||||
expect(mocks.event.serverSend).toHaveBeenCalledWith('config.update', update);
|
expect(mocks.event.serverSend).toHaveBeenCalledWith('ConfigUpdate', update);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -77,8 +77,8 @@ export class NotificationService extends BaseService {
|
|||||||
await this.notificationRepository.cleanup();
|
await this.notificationRepository.cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'job.failed' })
|
@OnEvent({ name: 'JobFailed' })
|
||||||
async onJobFailed({ job, error }: ArgOf<'job.failed'>) {
|
async onJobFailed({ job, error }: ArgOf<'JobFailed'>) {
|
||||||
const admin = await this.userRepository.getAdmin();
|
const admin = await this.userRepository.getAdmin();
|
||||||
if (!admin) {
|
if (!admin) {
|
||||||
return;
|
return;
|
||||||
@@ -107,14 +107,14 @@ export class NotificationService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'config.update' })
|
@OnEvent({ name: 'ConfigUpdate' })
|
||||||
onConfigUpdate({ oldConfig, newConfig }: ArgOf<'config.update'>) {
|
onConfigUpdate({ oldConfig, newConfig }: ArgOf<'ConfigUpdate'>) {
|
||||||
this.eventRepository.clientBroadcast('on_config_update');
|
this.eventRepository.clientBroadcast('on_config_update');
|
||||||
this.eventRepository.serverSend('config.update', { oldConfig, newConfig });
|
this.eventRepository.serverSend('ConfigUpdate', { oldConfig, newConfig });
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'config.validate', priority: -100 })
|
@OnEvent({ name: 'ConfigValidate', priority: -100 })
|
||||||
async onConfigValidate({ oldConfig, newConfig }: ArgOf<'config.validate'>) {
|
async onConfigValidate({ oldConfig, newConfig }: ArgOf<'ConfigValidate'>) {
|
||||||
try {
|
try {
|
||||||
if (
|
if (
|
||||||
newConfig.notifications.smtp.enabled &&
|
newConfig.notifications.smtp.enabled &&
|
||||||
@@ -128,33 +128,33 @@ export class NotificationService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'asset.hide' })
|
@OnEvent({ name: 'AssetHide' })
|
||||||
onAssetHide({ assetId, userId }: ArgOf<'asset.hide'>) {
|
onAssetHide({ assetId, userId }: ArgOf<'AssetHide'>) {
|
||||||
this.eventRepository.clientSend('on_asset_hidden', userId, assetId);
|
this.eventRepository.clientSend('on_asset_hidden', userId, assetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'asset.show' })
|
@OnEvent({ name: 'AssetShow' })
|
||||||
async onAssetShow({ assetId }: ArgOf<'asset.show'>) {
|
async onAssetShow({ assetId }: ArgOf<'AssetShow'>) {
|
||||||
await this.jobRepository.queue({ name: JobName.GENERATE_THUMBNAILS, data: { id: assetId, notify: true } });
|
await this.jobRepository.queue({ name: JobName.GENERATE_THUMBNAILS, data: { id: assetId, notify: true } });
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'asset.trash' })
|
@OnEvent({ name: 'AssetTrash' })
|
||||||
onAssetTrash({ assetId, userId }: ArgOf<'asset.trash'>) {
|
onAssetTrash({ assetId, userId }: ArgOf<'AssetTrash'>) {
|
||||||
this.eventRepository.clientSend('on_asset_trash', userId, [assetId]);
|
this.eventRepository.clientSend('on_asset_trash', userId, [assetId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'asset.delete' })
|
@OnEvent({ name: 'AssetDelete' })
|
||||||
onAssetDelete({ assetId, userId }: ArgOf<'asset.delete'>) {
|
onAssetDelete({ assetId, userId }: ArgOf<'AssetDelete'>) {
|
||||||
this.eventRepository.clientSend('on_asset_delete', userId, assetId);
|
this.eventRepository.clientSend('on_asset_delete', userId, assetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'assets.trash' })
|
@OnEvent({ name: 'AssetTrashAll' })
|
||||||
onAssetsTrash({ assetIds, userId }: ArgOf<'assets.trash'>) {
|
onAssetsTrash({ assetIds, userId }: ArgOf<'AssetTrashAll'>) {
|
||||||
this.eventRepository.clientSend('on_asset_trash', userId, assetIds);
|
this.eventRepository.clientSend('on_asset_trash', userId, assetIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'asset.metadataExtracted' })
|
@OnEvent({ name: 'AssetMetadataExtracted' })
|
||||||
async onAssetMetadataExtracted({ assetId, userId, source }: ArgOf<'asset.metadataExtracted'>) {
|
async onAssetMetadataExtracted({ assetId, userId, source }: ArgOf<'AssetMetadataExtracted'>) {
|
||||||
if (source !== 'sidecar-write') {
|
if (source !== 'sidecar-write') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -165,40 +165,40 @@ export class NotificationService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'assets.restore' })
|
@OnEvent({ name: 'AssetRestoreAll' })
|
||||||
onAssetsRestore({ assetIds, userId }: ArgOf<'assets.restore'>) {
|
onAssetsRestore({ assetIds, userId }: ArgOf<'AssetRestoreAll'>) {
|
||||||
this.eventRepository.clientSend('on_asset_restore', userId, assetIds);
|
this.eventRepository.clientSend('on_asset_restore', userId, assetIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'stack.create' })
|
@OnEvent({ name: 'StackCreate' })
|
||||||
onStackCreate({ userId }: ArgOf<'stack.create'>) {
|
onStackCreate({ userId }: ArgOf<'StackCreate'>) {
|
||||||
this.eventRepository.clientSend('on_asset_stack_update', userId);
|
this.eventRepository.clientSend('on_asset_stack_update', userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'stack.update' })
|
@OnEvent({ name: 'StackUpdate' })
|
||||||
onStackUpdate({ userId }: ArgOf<'stack.update'>) {
|
onStackUpdate({ userId }: ArgOf<'StackUpdate'>) {
|
||||||
this.eventRepository.clientSend('on_asset_stack_update', userId);
|
this.eventRepository.clientSend('on_asset_stack_update', userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'stack.delete' })
|
@OnEvent({ name: 'StackDelete' })
|
||||||
onStackDelete({ userId }: ArgOf<'stack.delete'>) {
|
onStackDelete({ userId }: ArgOf<'StackDelete'>) {
|
||||||
this.eventRepository.clientSend('on_asset_stack_update', userId);
|
this.eventRepository.clientSend('on_asset_stack_update', userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'stacks.delete' })
|
@OnEvent({ name: 'StackDeleteAll' })
|
||||||
onStacksDelete({ userId }: ArgOf<'stacks.delete'>) {
|
onStacksDelete({ userId }: ArgOf<'StackDeleteAll'>) {
|
||||||
this.eventRepository.clientSend('on_asset_stack_update', userId);
|
this.eventRepository.clientSend('on_asset_stack_update', userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'user.signup' })
|
@OnEvent({ name: 'UserSignup' })
|
||||||
async onUserSignup({ notify, id, tempPassword }: ArgOf<'user.signup'>) {
|
async onUserSignup({ notify, id, tempPassword }: ArgOf<'UserSignup'>) {
|
||||||
if (notify) {
|
if (notify) {
|
||||||
await this.jobRepository.queue({ name: JobName.NOTIFY_SIGNUP, data: { id, tempPassword } });
|
await this.jobRepository.queue({ name: JobName.NOTIFY_SIGNUP, data: { id, tempPassword } });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'album.update' })
|
@OnEvent({ name: 'AlbumUpdate' })
|
||||||
async onAlbumUpdate({ id, recipientId }: ArgOf<'album.update'>) {
|
async onAlbumUpdate({ id, recipientId }: ArgOf<'AlbumUpdate'>) {
|
||||||
await this.jobRepository.removeJob(JobName.NOTIFY_ALBUM_UPDATE, `${id}/${recipientId}`);
|
await this.jobRepository.removeJob(JobName.NOTIFY_ALBUM_UPDATE, `${id}/${recipientId}`);
|
||||||
await this.jobRepository.queue({
|
await this.jobRepository.queue({
|
||||||
name: JobName.NOTIFY_ALBUM_UPDATE,
|
name: JobName.NOTIFY_ALBUM_UPDATE,
|
||||||
@@ -206,13 +206,13 @@ export class NotificationService extends BaseService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'album.invite' })
|
@OnEvent({ name: 'AlbumInvite' })
|
||||||
async onAlbumInvite({ id, userId }: ArgOf<'album.invite'>) {
|
async onAlbumInvite({ id, userId }: ArgOf<'AlbumInvite'>) {
|
||||||
await this.jobRepository.queue({ name: JobName.NOTIFY_ALBUM_INVITE, data: { id, recipientId: userId } });
|
await this.jobRepository.queue({ name: JobName.NOTIFY_ALBUM_INVITE, data: { id, recipientId: userId } });
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'session.delete' })
|
@OnEvent({ name: 'SessionDelete' })
|
||||||
onSessionDelete({ sessionId }: ArgOf<'session.delete'>) {
|
onSessionDelete({ sessionId }: ArgOf<'SessionDelete'>) {
|
||||||
// after the response is sent
|
// after the response is sent
|
||||||
setTimeout(() => this.eventRepository.clientSend('on_session_delete', sessionId, sessionId), 500);
|
setTimeout(() => this.eventRepository.clientSend('on_session_delete', sessionId, sessionId), 500);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import { isDuplicateDetectionEnabled, isFacialRecognitionEnabled, isSmartSearchE
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ServerService extends BaseService {
|
export class ServerService extends BaseService {
|
||||||
@OnEvent({ name: 'app.bootstrap' })
|
@OnEvent({ name: 'AppBootstrap' })
|
||||||
async onBootstrap(): Promise<void> {
|
async onBootstrap(): Promise<void> {
|
||||||
const featureFlags = await this.getFeatures();
|
const featureFlags = await this.getFeatures();
|
||||||
if (featureFlags.configFile) {
|
if (featureFlags.configFile) {
|
||||||
|
|||||||
@@ -10,18 +10,18 @@ import { getCLIPModelInfo, isSmartSearchEnabled } from 'src/utils/misc';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SmartInfoService extends BaseService {
|
export class SmartInfoService extends BaseService {
|
||||||
@OnEvent({ name: 'config.init', workers: [ImmichWorker.MICROSERVICES] })
|
@OnEvent({ name: 'ConfigInit', workers: [ImmichWorker.MICROSERVICES] })
|
||||||
async onConfigInit({ newConfig }: ArgOf<'config.init'>) {
|
async onConfigInit({ newConfig }: ArgOf<'ConfigInit'>) {
|
||||||
await this.init(newConfig);
|
await this.init(newConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'config.update', workers: [ImmichWorker.MICROSERVICES], server: true })
|
@OnEvent({ name: 'ConfigUpdate', workers: [ImmichWorker.MICROSERVICES], server: true })
|
||||||
async onConfigUpdate({ oldConfig, newConfig }: ArgOf<'config.update'>) {
|
async onConfigUpdate({ oldConfig, newConfig }: ArgOf<'ConfigUpdate'>) {
|
||||||
await this.init(newConfig, oldConfig);
|
await this.init(newConfig, oldConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'config.validate' })
|
@OnEvent({ name: 'ConfigValidate' })
|
||||||
onConfigValidate({ newConfig }: ArgOf<'config.validate'>) {
|
onConfigValidate({ newConfig }: ArgOf<'ConfigValidate'>) {
|
||||||
try {
|
try {
|
||||||
getCLIPModelInfo(newConfig.machineLearning.clip.modelName);
|
getCLIPModelInfo(newConfig.machineLearning.clip.modelName);
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ describe(StackService.name, () => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(mocks.event.emit).toHaveBeenCalledWith('stack.create', {
|
expect(mocks.event.emit).toHaveBeenCalledWith('StackCreate', {
|
||||||
stackId: 'stack-id',
|
stackId: 'stack-id',
|
||||||
userId: authStub.admin.user.id,
|
userId: authStub.admin.user.id,
|
||||||
});
|
});
|
||||||
@@ -138,7 +138,7 @@ describe(StackService.name, () => {
|
|||||||
id: 'stack-id',
|
id: 'stack-id',
|
||||||
primaryAssetId: assetStub.image1.id,
|
primaryAssetId: assetStub.image1.id,
|
||||||
});
|
});
|
||||||
expect(mocks.event.emit).toHaveBeenCalledWith('stack.update', {
|
expect(mocks.event.emit).toHaveBeenCalledWith('StackUpdate', {
|
||||||
stackId: 'stack-id',
|
stackId: 'stack-id',
|
||||||
userId: authStub.admin.user.id,
|
userId: authStub.admin.user.id,
|
||||||
});
|
});
|
||||||
@@ -160,7 +160,7 @@ describe(StackService.name, () => {
|
|||||||
await sut.delete(authStub.admin, 'stack-id');
|
await sut.delete(authStub.admin, 'stack-id');
|
||||||
|
|
||||||
expect(mocks.stack.delete).toHaveBeenCalledWith('stack-id');
|
expect(mocks.stack.delete).toHaveBeenCalledWith('stack-id');
|
||||||
expect(mocks.event.emit).toHaveBeenCalledWith('stack.delete', {
|
expect(mocks.event.emit).toHaveBeenCalledWith('StackDelete', {
|
||||||
stackId: 'stack-id',
|
stackId: 'stack-id',
|
||||||
userId: authStub.admin.user.id,
|
userId: authStub.admin.user.id,
|
||||||
});
|
});
|
||||||
@@ -182,7 +182,7 @@ describe(StackService.name, () => {
|
|||||||
await sut.deleteAll(authStub.admin, { ids: ['stack-id'] });
|
await sut.deleteAll(authStub.admin, { ids: ['stack-id'] });
|
||||||
|
|
||||||
expect(mocks.stack.deleteAll).toHaveBeenCalledWith(['stack-id']);
|
expect(mocks.stack.deleteAll).toHaveBeenCalledWith(['stack-id']);
|
||||||
expect(mocks.event.emit).toHaveBeenCalledWith('stacks.delete', {
|
expect(mocks.event.emit).toHaveBeenCalledWith('StackDeleteAll', {
|
||||||
stackIds: ['stack-id'],
|
stackIds: ['stack-id'],
|
||||||
userId: authStub.admin.user.id,
|
userId: authStub.admin.user.id,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export class StackService extends BaseService {
|
|||||||
|
|
||||||
const stack = await this.stackRepository.create({ ownerId: auth.user.id }, dto.assetIds);
|
const stack = await this.stackRepository.create({ ownerId: auth.user.id }, dto.assetIds);
|
||||||
|
|
||||||
await this.eventRepository.emit('stack.create', { stackId: stack.id, userId: auth.user.id });
|
await this.eventRepository.emit('StackCreate', { stackId: stack.id, userId: auth.user.id });
|
||||||
|
|
||||||
return mapStack(stack, { auth });
|
return mapStack(stack, { auth });
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,7 @@ export class StackService extends BaseService {
|
|||||||
|
|
||||||
const updatedStack = await this.stackRepository.update(id, { id, primaryAssetId: dto.primaryAssetId });
|
const updatedStack = await this.stackRepository.update(id, { id, primaryAssetId: dto.primaryAssetId });
|
||||||
|
|
||||||
await this.eventRepository.emit('stack.update', { stackId: id, userId: auth.user.id });
|
await this.eventRepository.emit('StackUpdate', { stackId: id, userId: auth.user.id });
|
||||||
|
|
||||||
return mapStack(updatedStack, { auth });
|
return mapStack(updatedStack, { auth });
|
||||||
}
|
}
|
||||||
@@ -49,13 +49,13 @@ export class StackService extends BaseService {
|
|||||||
async delete(auth: AuthDto, id: string): Promise<void> {
|
async delete(auth: AuthDto, id: string): Promise<void> {
|
||||||
await this.requireAccess({ auth, permission: Permission.STACK_DELETE, ids: [id] });
|
await this.requireAccess({ auth, permission: Permission.STACK_DELETE, ids: [id] });
|
||||||
await this.stackRepository.delete(id);
|
await this.stackRepository.delete(id);
|
||||||
await this.eventRepository.emit('stack.delete', { stackId: id, userId: auth.user.id });
|
await this.eventRepository.emit('StackDelete', { stackId: id, userId: auth.user.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteAll(auth: AuthDto, dto: BulkIdsDto): Promise<void> {
|
async deleteAll(auth: AuthDto, dto: BulkIdsDto): Promise<void> {
|
||||||
await this.requireAccess({ auth, permission: Permission.STACK_DELETE, ids: dto.ids });
|
await this.requireAccess({ auth, permission: Permission.STACK_DELETE, ids: dto.ids });
|
||||||
await this.stackRepository.deleteAll(dto.ids);
|
await this.stackRepository.deleteAll(dto.ids);
|
||||||
await this.eventRepository.emit('stacks.delete', { stackIds: dto.ids, userId: auth.user.id });
|
await this.eventRepository.emit('StackDeleteAll', { stackIds: dto.ids, userId: auth.user.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
private async findOrFail(id: string) {
|
private async findOrFail(id: string) {
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ export class StorageTemplateService extends BaseService {
|
|||||||
return this._template;
|
return this._template;
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'config.init' })
|
@OnEvent({ name: 'ConfigInit' })
|
||||||
onConfigInit({ newConfig }: ArgOf<'config.init'>) {
|
onConfigInit({ newConfig }: ArgOf<'ConfigInit'>) {
|
||||||
const template = newConfig.storageTemplate.template;
|
const template = newConfig.storageTemplate.template;
|
||||||
if (!this._template || template !== this.template.raw) {
|
if (!this._template || template !== this.template.raw) {
|
||||||
this.logger.debug(`Compiling new storage template: ${template}`);
|
this.logger.debug(`Compiling new storage template: ${template}`);
|
||||||
@@ -84,13 +84,13 @@ export class StorageTemplateService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'config.update', server: true })
|
@OnEvent({ name: 'ConfigUpdate', server: true })
|
||||||
onConfigUpdate({ newConfig }: ArgOf<'config.update'>) {
|
onConfigUpdate({ newConfig }: ArgOf<'ConfigUpdate'>) {
|
||||||
this.onConfigInit({ newConfig });
|
this.onConfigInit({ newConfig });
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'config.validate' })
|
@OnEvent({ name: 'ConfigValidate' })
|
||||||
onConfigValidate({ newConfig }: ArgOf<'config.validate'>) {
|
onConfigValidate({ newConfig }: ArgOf<'ConfigValidate'>) {
|
||||||
try {
|
try {
|
||||||
const { compiled } = this.compile(newConfig.storageTemplate.template);
|
const { compiled } = this.compile(newConfig.storageTemplate.template);
|
||||||
this.render(compiled, {
|
this.render(compiled, {
|
||||||
@@ -116,8 +116,8 @@ export class StorageTemplateService extends BaseService {
|
|||||||
return { ...storageTokens, presetOptions: storagePresets };
|
return { ...storageTokens, presetOptions: storagePresets };
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'asset.metadataExtracted' })
|
@OnEvent({ name: 'AssetMetadataExtracted' })
|
||||||
async onAssetMetadataExtracted({ source, assetId }: ArgOf<'asset.metadataExtracted'>) {
|
async onAssetMetadataExtracted({ source, assetId }: ArgOf<'AssetMetadataExtracted'>) {
|
||||||
await this.jobRepository.queue({ name: JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE, data: { source, id: assetId } });
|
await this.jobRepository.queue({ name: JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE, data: { source, id: assetId } });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,8 +182,8 @@ export class StorageTemplateService extends BaseService {
|
|||||||
return JobStatus.SUCCESS;
|
return JobStatus.SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'asset.delete' })
|
@OnEvent({ name: 'AssetDelete' })
|
||||||
async handleMoveHistoryCleanup({ assetId }: ArgOf<'asset.delete'>) {
|
async handleMoveHistoryCleanup({ assetId }: ArgOf<'AssetDelete'>) {
|
||||||
this.logger.debug(`Cleaning up move history for asset ${assetId}`);
|
this.logger.debug(`Cleaning up move history for asset ${assetId}`);
|
||||||
await this.moveRepository.cleanMoveHistorySingle(assetId);
|
await this.moveRepository.cleanMoveHistorySingle(assetId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const docsMessage = `Please see https://immich.app/docs/administration/system-in
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class StorageService extends BaseService {
|
export class StorageService extends BaseService {
|
||||||
@OnEvent({ name: 'app.bootstrap' })
|
@OnEvent({ name: 'AppBootstrap' })
|
||||||
async onBootstrap() {
|
async onBootstrap() {
|
||||||
const envData = this.configRepository.getEnv();
|
const envData = this.configRepository.getEnv();
|
||||||
|
|
||||||
|
|||||||
@@ -412,7 +412,7 @@ describe(SystemConfigService.name, () => {
|
|||||||
mocks.systemMetadata.get.mockResolvedValue(partialConfig);
|
mocks.systemMetadata.get.mockResolvedValue(partialConfig);
|
||||||
await expect(sut.updateSystemConfig(updatedConfig)).resolves.toEqual(updatedConfig);
|
await expect(sut.updateSystemConfig(updatedConfig)).resolves.toEqual(updatedConfig);
|
||||||
expect(mocks.event.emit).toHaveBeenCalledWith(
|
expect(mocks.event.emit).toHaveBeenCalledWith(
|
||||||
'config.update',
|
'ConfigUpdate',
|
||||||
expect.objectContaining({ oldConfig: expect.any(Object), newConfig: updatedConfig }),
|
expect.objectContaining({ oldConfig: expect.any(Object), newConfig: updatedConfig }),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ import { toPlainObject } from 'src/utils/object';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SystemConfigService extends BaseService {
|
export class SystemConfigService extends BaseService {
|
||||||
@OnEvent({ name: 'app.bootstrap', priority: BootstrapEventPriority.SystemConfig })
|
@OnEvent({ name: 'AppBootstrap', priority: BootstrapEventPriority.SystemConfig })
|
||||||
async onBootstrap() {
|
async onBootstrap() {
|
||||||
const config = await this.getConfig({ withCache: false });
|
const config = await this.getConfig({ withCache: false });
|
||||||
await this.eventRepository.emit('config.init', { newConfig: config });
|
await this.eventRepository.emit('ConfigInit', { newConfig: config });
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSystemConfig(): Promise<SystemConfigDto> {
|
async getSystemConfig(): Promise<SystemConfigDto> {
|
||||||
@@ -27,8 +27,8 @@ export class SystemConfigService extends BaseService {
|
|||||||
return mapConfig(defaults);
|
return mapConfig(defaults);
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'config.init', priority: -100 })
|
@OnEvent({ name: 'ConfigInit', priority: -100 })
|
||||||
onConfigInit({ newConfig: { logging } }: ArgOf<'config.init'>) {
|
onConfigInit({ newConfig: { logging } }: ArgOf<'ConfigInit'>) {
|
||||||
const { logLevel: envLevel } = this.configRepository.getEnv();
|
const { logLevel: envLevel } = this.configRepository.getEnv();
|
||||||
const configLevel = logging.enabled ? logging.level : false;
|
const configLevel = logging.enabled ? logging.level : false;
|
||||||
const level = envLevel ?? configLevel;
|
const level = envLevel ?? configLevel;
|
||||||
@@ -36,14 +36,14 @@ export class SystemConfigService extends BaseService {
|
|||||||
this.logger.log(`LogLevel=${level} ${envLevel ? '(set via IMMICH_LOG_LEVEL)' : '(set via system config)'}`);
|
this.logger.log(`LogLevel=${level} ${envLevel ? '(set via IMMICH_LOG_LEVEL)' : '(set via system config)'}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'config.update', server: true })
|
@OnEvent({ name: 'ConfigUpdate', server: true })
|
||||||
onConfigUpdate({ newConfig }: ArgOf<'config.update'>) {
|
onConfigUpdate({ newConfig }: ArgOf<'ConfigUpdate'>) {
|
||||||
this.onConfigInit({ newConfig });
|
this.onConfigInit({ newConfig });
|
||||||
clearConfigCache();
|
clearConfigCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'config.validate' })
|
@OnEvent({ name: 'ConfigValidate' })
|
||||||
onConfigValidate({ newConfig, oldConfig }: ArgOf<'config.validate'>) {
|
onConfigValidate({ newConfig, oldConfig }: ArgOf<'ConfigValidate'>) {
|
||||||
const { logLevel } = this.configRepository.getEnv();
|
const { logLevel } = this.configRepository.getEnv();
|
||||||
if (!_.isEqual(instanceToPlain(newConfig.logging), oldConfig.logging) && logLevel) {
|
if (!_.isEqual(instanceToPlain(newConfig.logging), oldConfig.logging) && logLevel) {
|
||||||
throw new Error('Logging cannot be changed while the environment variable IMMICH_LOG_LEVEL is set.');
|
throw new Error('Logging cannot be changed while the environment variable IMMICH_LOG_LEVEL is set.');
|
||||||
@@ -59,7 +59,7 @@ export class SystemConfigService extends BaseService {
|
|||||||
const oldConfig = await this.getConfig({ withCache: false });
|
const oldConfig = await this.getConfig({ withCache: false });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.eventRepository.emit('config.validate', { newConfig: toPlainObject(dto), oldConfig });
|
await this.eventRepository.emit('ConfigValidate', { newConfig: toPlainObject(dto), oldConfig });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.warn(`Unable to save system config due to a validation error: ${error}`);
|
this.logger.warn(`Unable to save system config due to a validation error: ${error}`);
|
||||||
throw new BadRequestException(error instanceof Error ? error.message : error);
|
throw new BadRequestException(error instanceof Error ? error.message : error);
|
||||||
@@ -67,7 +67,7 @@ export class SystemConfigService extends BaseService {
|
|||||||
|
|
||||||
const newConfig = await this.updateConfig(dto);
|
const newConfig = await this.updateConfig(dto);
|
||||||
|
|
||||||
await this.eventRepository.emit('config.update', { newConfig, oldConfig });
|
await this.eventRepository.emit('ConfigUpdate', { newConfig, oldConfig });
|
||||||
|
|
||||||
return mapConfig(newConfig);
|
return mapConfig(newConfig);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ export class TagService extends BaseService {
|
|||||||
|
|
||||||
const results = await this.tagRepository.upsertAssetIds(items);
|
const results = await this.tagRepository.upsertAssetIds(items);
|
||||||
for (const assetId of new Set(results.map((item) => item.assetsId))) {
|
for (const assetId of new Set(results.map((item) => item.assetsId))) {
|
||||||
await this.eventRepository.emit('asset.tag', { assetId });
|
await this.eventRepository.emit('AssetTag', { assetId });
|
||||||
}
|
}
|
||||||
|
|
||||||
return { count: results.length };
|
return { count: results.length };
|
||||||
@@ -107,7 +107,7 @@ export class TagService extends BaseService {
|
|||||||
|
|
||||||
for (const { id: assetId, success } of results) {
|
for (const { id: assetId, success } of results) {
|
||||||
if (success) {
|
if (success) {
|
||||||
await this.eventRepository.emit('asset.tag', { assetId });
|
await this.eventRepository.emit('AssetTag', { assetId });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@ export class TagService extends BaseService {
|
|||||||
|
|
||||||
for (const { id: assetId, success } of results) {
|
for (const { id: assetId, success } of results) {
|
||||||
if (success) {
|
if (success) {
|
||||||
await this.eventRepository.emit('asset.untag', { assetId });
|
await this.eventRepository.emit('AssetUntag', { assetId });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export class TrashService extends BaseService {
|
|||||||
|
|
||||||
await this.requireAccess({ auth, permission: Permission.ASSET_DELETE, ids });
|
await this.requireAccess({ auth, permission: Permission.ASSET_DELETE, ids });
|
||||||
await this.trashRepository.restoreAll(ids);
|
await this.trashRepository.restoreAll(ids);
|
||||||
await this.eventRepository.emit('assets.restore', { assetIds: ids, userId: auth.user.id });
|
await this.eventRepository.emit('AssetRestoreAll', { assetIds: ids, userId: auth.user.id });
|
||||||
|
|
||||||
this.logger.log(`Restored ${ids.length} asset(s) from trash`);
|
this.logger.log(`Restored ${ids.length} asset(s) from trash`);
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ export class TrashService extends BaseService {
|
|||||||
return { count };
|
return { count };
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'assets.delete' })
|
@OnEvent({ name: 'AssetDeleteAll' })
|
||||||
async onAssetsDelete() {
|
async onAssetsDelete() {
|
||||||
await this.jobRepository.queue({ name: JobName.QUEUE_TRASH_EMPTY, data: {} });
|
await this.jobRepository.queue({ name: JobName.QUEUE_TRASH_EMPTY, data: {} });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export class UserAdminService extends BaseService {
|
|||||||
|
|
||||||
const user = await this.createUser(userDto);
|
const user = await this.createUser(userDto);
|
||||||
|
|
||||||
await this.eventRepository.emit('user.signup', {
|
await this.eventRepository.emit('UserSignup', {
|
||||||
notify: !!notify,
|
notify: !!notify,
|
||||||
id: user.id,
|
id: user.id,
|
||||||
tempPassword: user.shouldChangePassword ? userDto.password : undefined,
|
tempPassword: user.shouldChangePassword ? userDto.password : undefined,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const asNotification = ({ checkedAt, releaseVersion }: VersionCheckMetadata): Re
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class VersionService extends BaseService {
|
export class VersionService extends BaseService {
|
||||||
@OnEvent({ name: 'app.bootstrap' })
|
@OnEvent({ name: 'AppBootstrap' })
|
||||||
async onBootstrap(): Promise<void> {
|
async onBootstrap(): Promise<void> {
|
||||||
await this.handleVersionCheck();
|
await this.handleVersionCheck();
|
||||||
|
|
||||||
@@ -102,8 +102,8 @@ export class VersionService extends BaseService {
|
|||||||
return JobStatus.SUCCESS;
|
return JobStatus.SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'websocket.connect' })
|
@OnEvent({ name: 'WebsocketConnect' })
|
||||||
async onWebsocketConnection({ userId }: ArgOf<'websocket.connect'>) {
|
async onWebsocketConnection({ userId }: ArgOf<'WebsocketConnect'>) {
|
||||||
this.eventRepository.clientSend('on_server_version', userId, serverVersion);
|
this.eventRepository.clientSend('on_server_version', userId, serverVersion);
|
||||||
const metadata = await this.systemMetadataRepository.get(SystemMetadataKey.VERSION_CHECK_STATE);
|
const metadata = await this.systemMetadataRepository.get(SystemMetadataKey.VERSION_CHECK_STATE);
|
||||||
if (metadata) {
|
if (metadata) {
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ export const onBeforeLink = async (
|
|||||||
|
|
||||||
if (motionAsset && motionAsset.visibility === AssetVisibility.TIMELINE) {
|
if (motionAsset && motionAsset.visibility === AssetVisibility.TIMELINE) {
|
||||||
await assetRepository.update({ id: livePhotoVideoId, visibility: AssetVisibility.HIDDEN });
|
await assetRepository.update({ id: livePhotoVideoId, visibility: AssetVisibility.HIDDEN });
|
||||||
await eventRepository.emit('asset.hide', { assetId: motionAsset.id, userId });
|
await eventRepository.emit('AssetHide', { assetId: motionAsset.id, userId });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ export const onAfterUnlink = async (
|
|||||||
{ userId, livePhotoVideoId, visibility }: { userId: string; livePhotoVideoId: string; visibility: AssetVisibility },
|
{ userId, livePhotoVideoId, visibility }: { userId: string; livePhotoVideoId: string; visibility: AssetVisibility },
|
||||||
) => {
|
) => {
|
||||||
await assetRepository.update({ id: livePhotoVideoId, visibility });
|
await assetRepository.update({ id: livePhotoVideoId, visibility });
|
||||||
await eventRepository.emit('asset.show', { assetId: livePhotoVideoId, userId });
|
await eventRepository.emit('AssetShow', { assetId: livePhotoVideoId, userId });
|
||||||
};
|
};
|
||||||
|
|
||||||
export function mapToUploadFile(file: ImmichFile): UploadFile {
|
export function mapToUploadFile(file: ImmichFile): UploadFile {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user