Compare commits
7 Commits
feat/prelo
...
feat/local
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eabf535246 | ||
|
|
b0947bf9b9 | ||
|
|
5ea3b5a5c1 | ||
|
|
ded18f3b6b | ||
|
|
3724dc465b | ||
|
|
91b07fbac8 | ||
|
|
fe84f1cc90 |
@@ -25,6 +25,7 @@ server/upload/
|
||||
server/src/queries
|
||||
server/dist/
|
||||
server/www/
|
||||
server/resources/v1.pmtiles
|
||||
|
||||
web/node_modules/
|
||||
web/coverage/
|
||||
|
||||
200
cli/package-lock.json
generated
200
cli/package-lock.json
generated
@@ -1353,17 +1353,17 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.6.0.tgz",
|
||||
"integrity": "sha512-UOaz/wFowmoh2G6Mr9gw60B1mm0MzUtm6Ic8G2yM1Le6gyj5Loi/N+O5mocugRGY+8OeeKmkMmbxNqUCq3B4Sg==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.5.0.tgz",
|
||||
"integrity": "sha512-lHS5hvz33iUFQKuPFGheAB84LwcJ60G8vKnEhnfcK1l8kGVLro2SFYW6K0/tj8FUhRJ0VHyg1oAfg50QGbPPHw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.6.0",
|
||||
"@typescript-eslint/type-utils": "8.6.0",
|
||||
"@typescript-eslint/utils": "8.6.0",
|
||||
"@typescript-eslint/visitor-keys": "8.6.0",
|
||||
"@typescript-eslint/scope-manager": "8.5.0",
|
||||
"@typescript-eslint/type-utils": "8.5.0",
|
||||
"@typescript-eslint/utils": "8.5.0",
|
||||
"@typescript-eslint/visitor-keys": "8.5.0",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.3.1",
|
||||
"natural-compare": "^1.4.0",
|
||||
@@ -1387,16 +1387,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.6.0.tgz",
|
||||
"integrity": "sha512-eQcbCuA2Vmw45iGfcyG4y6rS7BhWfz9MQuk409WD47qMM+bKCGQWXxvoOs1DUp+T7UBMTtRTVT+kXr7Sh4O9Ow==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.5.0.tgz",
|
||||
"integrity": "sha512-gF77eNv0Xz2UJg/NbpWJ0kqAm35UMsvZf1GHj8D9MRFTj/V3tAciIWXfmPLsAAF/vUlpWPvUDyH1jjsr0cMVWw==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.6.0",
|
||||
"@typescript-eslint/types": "8.6.0",
|
||||
"@typescript-eslint/typescript-estree": "8.6.0",
|
||||
"@typescript-eslint/visitor-keys": "8.6.0",
|
||||
"@typescript-eslint/scope-manager": "8.5.0",
|
||||
"@typescript-eslint/types": "8.5.0",
|
||||
"@typescript-eslint/typescript-estree": "8.5.0",
|
||||
"@typescript-eslint/visitor-keys": "8.5.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1416,14 +1416,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.6.0.tgz",
|
||||
"integrity": "sha512-ZuoutoS5y9UOxKvpc/GkvF4cuEmpokda4wRg64JEia27wX+PysIE9q+lzDtlHHgblwUWwo5/Qn+/WyTUvDwBHw==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.5.0.tgz",
|
||||
"integrity": "sha512-06JOQ9Qgj33yvBEx6tpC8ecP9o860rsR22hWMEd12WcTRrfaFgHr2RB/CA/B+7BMhHkXT4chg2MyboGdFGawYg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.6.0",
|
||||
"@typescript-eslint/visitor-keys": "8.6.0"
|
||||
"@typescript-eslint/types": "8.5.0",
|
||||
"@typescript-eslint/visitor-keys": "8.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -1434,14 +1434,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.6.0.tgz",
|
||||
"integrity": "sha512-dtePl4gsuenXVwC7dVNlb4mGDcKjDT/Ropsk4za/ouMBPplCLyznIaR+W65mvCvsyS97dymoBRrioEXI7k0XIg==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.5.0.tgz",
|
||||
"integrity": "sha512-N1K8Ix+lUM+cIDhL2uekVn/ZD7TZW+9/rwz8DclQpcQ9rk4sIL5CAlBC0CugWKREmDjBzI/kQqU4wkg46jWLYA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "8.6.0",
|
||||
"@typescript-eslint/utils": "8.6.0",
|
||||
"@typescript-eslint/typescript-estree": "8.5.0",
|
||||
"@typescript-eslint/utils": "8.5.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^1.3.0"
|
||||
},
|
||||
@@ -1459,9 +1459,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.6.0.tgz",
|
||||
"integrity": "sha512-rojqFZGd4MQxw33SrOy09qIDS8WEldM8JWtKQLAjf/X5mGSeEFh5ixQlxssMNyPslVIk9yzWqXCsV2eFhYrYUw==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.5.0.tgz",
|
||||
"integrity": "sha512-qjkormnQS5wF9pjSi6q60bKUHH44j2APxfh9TQRXK8wbYVeDYYdYJGIROL87LGZZ2gz3Rbmjc736qyL8deVtdw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -1473,14 +1473,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.6.0.tgz",
|
||||
"integrity": "sha512-MOVAzsKJIPIlLK239l5s06YXjNqpKTVhBVDnqUumQJja5+Y94V3+4VUFRA0G60y2jNnTVwRCkhyGQpavfsbq/g==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.5.0.tgz",
|
||||
"integrity": "sha512-vEG2Sf9P8BPQ+d0pxdfndw3xIXaoSjliG0/Ejk7UggByZPKXmJmw3GW5jV2gHNQNawBUyfahoSiCFVov0Ruf7Q==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.6.0",
|
||||
"@typescript-eslint/visitor-keys": "8.6.0",
|
||||
"@typescript-eslint/types": "8.5.0",
|
||||
"@typescript-eslint/visitor-keys": "8.5.0",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
@@ -1502,16 +1502,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.6.0.tgz",
|
||||
"integrity": "sha512-eNp9cWnYf36NaOVjkEUznf6fEgVy1TWpE0o52e4wtojjBx7D1UV2WAWGzR+8Y5lVFtpMLPwNbC67T83DWSph4A==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.5.0.tgz",
|
||||
"integrity": "sha512-6yyGYVL0e+VzGYp60wvkBHiqDWOpT63pdMV2CVG4LVDd5uR6q1qQN/7LafBZtAtNIn/mqXjsSeS5ggv/P0iECw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@typescript-eslint/scope-manager": "8.6.0",
|
||||
"@typescript-eslint/types": "8.6.0",
|
||||
"@typescript-eslint/typescript-estree": "8.6.0"
|
||||
"@typescript-eslint/scope-manager": "8.5.0",
|
||||
"@typescript-eslint/types": "8.5.0",
|
||||
"@typescript-eslint/typescript-estree": "8.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -1525,13 +1525,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.6.0.tgz",
|
||||
"integrity": "sha512-wapVFfZg9H0qOYh4grNVQiMklJGluQrOUiOhYRrQWhx7BY/+I1IYb8BczWNbbUpO+pqy0rDciv3lQH5E1bCLrg==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.5.0.tgz",
|
||||
"integrity": "sha512-yTPqMnbAZJNy2Xq2XU8AdtOW9tJIr+UQb64aXB9f3B1498Zx9JorVgFJcZpEc9UBuCCrdzKID2RGAMkYcDtZOw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.6.0",
|
||||
"@typescript-eslint/types": "8.5.0",
|
||||
"eslint-visitor-keys": "^3.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1543,9 +1543,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/coverage-v8": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.1.tgz",
|
||||
"integrity": "sha512-md/A7A3c42oTT8JUHSqjP5uKTWJejzUW4jalpvs+rZ27gsURsMU8DEb+8Jf8C6Kj2gwfSHJqobDNBuoqlm0cFw==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.0.tgz",
|
||||
"integrity": "sha512-yqCkr2nrV4o58VcVMxTVkS6Ggxzy7pmSD8JbTbhbH5PsQfUIES1QT716VUzo33wf2lX9EcWYdT3Vl2MMmjR59g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1566,8 +1566,8 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vitest/browser": "2.1.1",
|
||||
"vitest": "2.1.1"
|
||||
"@vitest/browser": "2.1.0",
|
||||
"vitest": "2.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vitest/browser": {
|
||||
@@ -1576,14 +1576,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.1.tgz",
|
||||
"integrity": "sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.0.tgz",
|
||||
"integrity": "sha512-N3/xR4fSu0+6sVZETEtPT1orUs2+Y477JOXTcU3xKuu3uBlsgbD7/7Mz2LZ1Jr1XjwilEWlrIgSCj4N1+5ZmsQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "2.1.1",
|
||||
"@vitest/utils": "2.1.1",
|
||||
"@vitest/spy": "2.1.0",
|
||||
"@vitest/utils": "2.1.0",
|
||||
"chai": "^5.1.1",
|
||||
"tinyrainbow": "^1.2.0"
|
||||
},
|
||||
@@ -1592,9 +1592,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/mocker": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.1.tgz",
|
||||
"integrity": "sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.0.tgz",
|
||||
"integrity": "sha512-ZxENovUqhzl+QiOFpagiHUNUuZ1qPd5yYTCYHomGIZOFArzn4mgX2oxZmiAItJWAaXHG6bbpb/DpSPhlk5DgtA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1606,7 +1606,7 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vitest/spy": "2.1.1",
|
||||
"@vitest/spy": "2.1.0",
|
||||
"msw": "^2.3.5",
|
||||
"vite": "^5.0.0"
|
||||
},
|
||||
@@ -1633,13 +1633,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/runner": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.1.tgz",
|
||||
"integrity": "sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.0.tgz",
|
||||
"integrity": "sha512-D9+ZiB8MbMt7qWDRJc4CRNNUlne/8E1X7dcKhZVAbcOKG58MGGYVDqAq19xlhNfMFZsW0bpVKgztBwks38Ko0w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/utils": "2.1.1",
|
||||
"@vitest/utils": "2.1.0",
|
||||
"pathe": "^1.1.2"
|
||||
},
|
||||
"funding": {
|
||||
@@ -1647,13 +1647,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/snapshot": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.1.tgz",
|
||||
"integrity": "sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.0.tgz",
|
||||
"integrity": "sha512-x69CygGMzt9VCO283K2/FYQ+nBrOj66OTKpsPykjCR4Ac3lLV+m85hj9reaIGmjBSsKzVvbxWmjWE3kF5ha3uQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "2.1.1",
|
||||
"@vitest/pretty-format": "2.1.0",
|
||||
"magic-string": "^0.30.11",
|
||||
"pathe": "^1.1.2"
|
||||
},
|
||||
@@ -1661,10 +1661,23 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/snapshot/node_modules/@vitest/pretty-format": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.0.tgz",
|
||||
"integrity": "sha512-7sxf2F3DNYatgmzXXcTh6cq+/fxwB47RIQqZJFoSH883wnVAoccSRT6g+dTKemUBo8Q5N4OYYj1EBXLuRKvp3Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tinyrainbow": "^1.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/spy": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.1.tgz",
|
||||
"integrity": "sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.0.tgz",
|
||||
"integrity": "sha512-IXX5NkbdgTYTog3F14i2LgnBc+20YmkXMx0IWai84mcxySUDRgm0ihbOfR4L0EVRBDFG85GjmQQEZNNKVVpkZw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1675,13 +1688,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/utils": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.1.tgz",
|
||||
"integrity": "sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.0.tgz",
|
||||
"integrity": "sha512-rreyfVe0PuNqJfKYUwfPDfi6rrp0VSu0Wgvp5WBqJonP+4NvXHk48X6oBam1Lj47Hy6jbJtnMj3OcRdrkTP0tA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "2.1.1",
|
||||
"@vitest/pretty-format": "2.1.0",
|
||||
"loupe": "^3.1.1",
|
||||
"tinyrainbow": "^1.2.0"
|
||||
},
|
||||
@@ -1689,6 +1702,19 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/utils/node_modules/@vitest/pretty-format": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.0.tgz",
|
||||
"integrity": "sha512-7sxf2F3DNYatgmzXXcTh6cq+/fxwB47RIQqZJFoSH883wnVAoccSRT6g+dTKemUBo8Q5N4OYYj1EBXLuRKvp3Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tinyrainbow": "^1.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.12.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
|
||||
@@ -4215,9 +4241,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite-node": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.1.tgz",
|
||||
"integrity": "sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.0.tgz",
|
||||
"integrity": "sha512-+ybYqBVUjYyIscoLzMWodus2enQDZOpGhcU6HdOVD6n8WZdk12w1GFL3mbnxLs7hPtRtqs1Wo5YF6/Tsr6fmhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -4257,19 +4283,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.1.tgz",
|
||||
"integrity": "sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.0.tgz",
|
||||
"integrity": "sha512-XuuEeyNkqbfr0FtAvd9vFbInSSNY1ykCQTYQ0sj9wPy4hx+1gR7gqVNdW0AX2wrrM1wWlN5fnJDjF9xG6mYRSQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/expect": "2.1.1",
|
||||
"@vitest/mocker": "2.1.1",
|
||||
"@vitest/pretty-format": "^2.1.1",
|
||||
"@vitest/runner": "2.1.1",
|
||||
"@vitest/snapshot": "2.1.1",
|
||||
"@vitest/spy": "2.1.1",
|
||||
"@vitest/utils": "2.1.1",
|
||||
"@vitest/expect": "2.1.0",
|
||||
"@vitest/mocker": "2.1.0",
|
||||
"@vitest/pretty-format": "^2.1.0",
|
||||
"@vitest/runner": "2.1.0",
|
||||
"@vitest/snapshot": "2.1.0",
|
||||
"@vitest/spy": "2.1.0",
|
||||
"@vitest/utils": "2.1.0",
|
||||
"chai": "^5.1.1",
|
||||
"debug": "^4.3.6",
|
||||
"magic-string": "^0.30.11",
|
||||
@@ -4280,7 +4306,7 @@
|
||||
"tinypool": "^1.0.0",
|
||||
"tinyrainbow": "^1.2.0",
|
||||
"vite": "^5.0.0",
|
||||
"vite-node": "2.1.1",
|
||||
"vite-node": "2.1.0",
|
||||
"why-is-node-running": "^2.3.0"
|
||||
},
|
||||
"bin": {
|
||||
@@ -4295,8 +4321,8 @@
|
||||
"peerDependencies": {
|
||||
"@edge-runtime/vm": "*",
|
||||
"@types/node": "^18.0.0 || >=20.0.0",
|
||||
"@vitest/browser": "2.1.1",
|
||||
"@vitest/ui": "2.1.1",
|
||||
"@vitest/browser": "2.1.0",
|
||||
"@vitest/ui": "2.1.0",
|
||||
"happy-dom": "*",
|
||||
"jsdom": "*"
|
||||
},
|
||||
|
||||
@@ -24,6 +24,7 @@ services:
|
||||
- ${UPLOAD_LOCATION}/photos/upload:/usr/src/app/upload/upload
|
||||
- /usr/src/app/node_modules
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ../server/resources/v1.pmtiles:/usr/src/app/resources/v1.pmtiles
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
|
||||
@@ -27,7 +27,7 @@ You may use a VPN service to open an encrypted connection to your Immich instanc
|
||||
|
||||
If you are unable to open a port on your router for Wireguard or OpenVPN to your server, [Tailscale](https://tailscale.com/) is a good option. Tailscale mediates a peer-to-peer wireguard tunnel between your server and remote device, even if one or both of them are behind a [NAT firewall](https://en.wikipedia.org/wiki/Network_address_translation).
|
||||
|
||||
:::tip Video tutorial
|
||||
:::tip Video toturial
|
||||
You can learn how to set up Tailscale together with Immich with the [tutorial video](https://www.youtube.com/watch?v=Vt4PDUXB_fg) they created.
|
||||
:::
|
||||
|
||||
|
||||
@@ -58,7 +58,6 @@ Optionally, you can enable hardware acceleration for machine learning and transc
|
||||
- Populate `UPLOAD_LOCATION` with your preferred location for storing backup assets.
|
||||
- Consider changing `DB_PASSWORD` to a custom value. Postgres is not publically exposed, so this password is only used for local authentication.
|
||||
To avoid issues with Docker parsing this value, it is best to use only the characters `A-Za-z0-9`.
|
||||
- Set your timezone by uncommenting the `TZ=` line.
|
||||
|
||||
### Step 3 - Start the containers
|
||||
|
||||
|
||||
@@ -27,14 +27,23 @@ If this should not work, try running `docker compose up -d --force-recreate`.
|
||||
These environment variables are used by the `docker-compose.yml` file and do **NOT** affect the containers directly.
|
||||
:::
|
||||
|
||||
### Supported filesystems
|
||||
|
||||
The Immich Postgres database (`DB_DATA_LOCATION`) must be located on a filesystem that supports user/group
|
||||
ownership and permissions (EXT2/3/4, ZFS, APFS, BTRFS, XFS, etc.). It will not work on any filesystem formatted in NTFS or ex/FAT/32.
|
||||
It will not work in WSL (Windows Subsystem for Linux) when using a mounted host directory (commonly under `/mnt`).
|
||||
If this is an issue, you can change the bind mount to a Docker volume instead.
|
||||
|
||||
Regardless of filesystem, it is not recommended to use a network share for your database location due to performance and possible data loss issues.
|
||||
|
||||
## General
|
||||
|
||||
| Variable | Description | Default | Containers | Workers |
|
||||
| :---------------------------------- | :---------------------------------------------------------------------------------------- | :--------------------------: | :----------------------- | :----------------- |
|
||||
| `TZ` | Timezone | <sup>\*1</sup> | server | microservices |
|
||||
| `TZ` | Timezone | | server | microservices |
|
||||
| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices |
|
||||
| `IMMICH_LOG_LEVEL` | Log Level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices |
|
||||
| `IMMICH_MEDIA_LOCATION` | Media Location inside the container ⚠️**You probably shouldn't set this**<sup>\*2</sup>⚠️ | `./upload`<sup>\*3</sup> | server | api, microservices |
|
||||
| `IMMICH_MEDIA_LOCATION` | Media Location inside the container ⚠️**You probably shouldn't set this**<sup>\*1</sup>⚠️ | `./upload`<sup>\*2</sup> | server | api, microservices |
|
||||
| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices |
|
||||
| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | |
|
||||
| `CPU_CORES` | Amount of cores available to the immich server | auto-detected cpu core count | server | |
|
||||
@@ -43,14 +52,17 @@ These environment variables are used by the `docker-compose.yml` file and do **N
|
||||
| `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices |
|
||||
| `IMMICH_TRUSTED_PROXIES` | List of comma separated IPs set as trusted proxies | | server | api |
|
||||
|
||||
\*1: `TZ` should be set to a `TZ identifier` from [this list][tz-list]. For example, `TZ="Etc/UTC"`.
|
||||
`TZ` is used by `exiftool` as a fallback in case the timezone cannot be determined from the image metadata. It is also used for logfile timestamps and cron job execution.
|
||||
\*1: This path is where the Immich code looks for the files, which is internal to the docker container. Setting it to a path on your host will certainly break things, you should use the `UPLOAD_LOCATION` variable instead.
|
||||
|
||||
\*2: This path is where the Immich code looks for the files, which is internal to the docker container. Setting it to a path on your host will certainly break things, you should use the `UPLOAD_LOCATION` variable instead.
|
||||
|
||||
\*3: With the default `WORKDIR` of `/usr/src/app`, this path will resolve to `/usr/src/app/upload`.
|
||||
\*2: With the default `WORKDIR` of `/usr/src/app`, this path will resolve to `/usr/src/app/upload`.
|
||||
It only need to be set if the Immich deployment method is changing.
|
||||
|
||||
:::tip
|
||||
`TZ` should be set to a `TZ identifier` from [this list][tz-list]. For example, `TZ="Etc/UTC"`.
|
||||
|
||||
`TZ` is used by `exiftool` as a fallback in case the timezone cannot be determined from the image metadata. It is also used for logfile timestamps and cron job execution.
|
||||
:::
|
||||
|
||||
## Workers
|
||||
|
||||
| Variable | Description | Default | Containers |
|
||||
|
||||
@@ -23,33 +23,7 @@ Immich requires the command `docker compose` - the similarly named `docker-compo
|
||||
- **RAM**: Minimum 4GB, recommended 6GB.
|
||||
- **CPU**: Minimum 2 cores, recommended 4 cores.
|
||||
- **Storage**: Recommended Unix-compatible filesystem (EXT4, ZFS, APFS, etc.) with support for user/group ownership and permissions.
|
||||
- This can present an issue for Windows users. See below for details and an alternative setup.
|
||||
- This can present an issue for Windows users. See [here](/docs/install/environment-variables#supported-filesystems)
|
||||
for more details and alternatives.
|
||||
- The generation of thumbnails and transcoded video can increase the size of the photo library by 10-20% on average.
|
||||
- Network shares are supported for the storage of image and video assets only. It is not recommended to use a network share for your database location due to performance and possible data loss issues.
|
||||
|
||||
### Special requirements for Windows users
|
||||
|
||||
<details>
|
||||
<summary>Database storage on Windows systems</summary>
|
||||
|
||||
The Immich Postgres database (`DB_DATA_LOCATION`) must be located on a filesystem that supports user/group
|
||||
ownership and permissions (EXT2/3/4, ZFS, APFS, BTRFS, XFS, etc.). It will not work on any filesystem formatted in NTFS or ex/FAT/32.
|
||||
It will not work in WSL (Windows Subsystem for Linux) when using a mounted host directory (commonly under `/mnt`).
|
||||
If this is an issue, you can change the bind mount to a Docker volume instead as follows:
|
||||
|
||||
Make the following change to `.env`:
|
||||
|
||||
```diff
|
||||
- DB_DATA_LOCATION=./postgres
|
||||
+ DB_DATA_LOCATION=pgdata
|
||||
```
|
||||
|
||||
Add the following line to the bottom of `docker-compose.yml`:
|
||||
|
||||
```diff
|
||||
volumes:
|
||||
model-cache:
|
||||
+ pgdata:
|
||||
```
|
||||
|
||||
</details>
|
||||
- Network shares are supported for the storage of image and video assets only.
|
||||
|
||||
6
docs/package-lock.json
generated
6
docs/package-lock.json
generated
@@ -16091,9 +16091,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "3.4.12",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.12.tgz",
|
||||
"integrity": "sha512-Htf/gHj2+soPb9UayUNci/Ja3d8pTmu9ONTfh4QY8r3MATTZOzmv6UYWF7ZwikEIC8okpfqmGqrmDehua8mF8w==",
|
||||
"version": "3.4.11",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.11.tgz",
|
||||
"integrity": "sha512-qhEuBcLemjSJk5ajccN9xJFtM/h0AVCPaA6C92jNP+M2J8kX+eMJHI7R2HFKUvvAsMpcfLILMCFYSeDwpMmlUg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@alloc/quick-lru": "^5.2.0",
|
||||
|
||||
424
e2e/package-lock.json
generated
424
e2e/package-lock.json
generated
@@ -1149,13 +1149,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.47.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.1.tgz",
|
||||
"integrity": "sha512-dbWpcNQZ5nj16m+A5UNScYx7HX5trIy7g4phrcitn+Nk83S32EBX/CLU4hiF4RGKX/yRc93AAqtfaXB7JWBd4Q==",
|
||||
"version": "1.47.0",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.0.tgz",
|
||||
"integrity": "sha512-SgAdlSwYVpToI4e/IH19IHHWvoijAYH5hu2MWSXptRypLSnzj51PcGD+rsOXFayde4P9ZLi+loXVwArg6IUkCA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright": "1.47.1"
|
||||
"playwright": "1.47.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
@@ -1165,9 +1165,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz",
|
||||
"integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==",
|
||||
"version": "4.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.3.tgz",
|
||||
"integrity": "sha512-MmKSfaB9GX+zXl6E8z4koOr/xU63AMVleLEa64v7R0QF/ZloMs5vcD1sHgM64GXXS1csaJutG+ddtzcueI/BLg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -1179,9 +1179,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz",
|
||||
"integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==",
|
||||
"version": "4.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.3.tgz",
|
||||
"integrity": "sha512-zrt8ecH07PE3sB4jPOggweBjJMzI1JG5xI2DIsUbkA+7K+Gkjys6eV7i9pOenNSDJH3eOr/jLb/PzqtmdwDq5g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1193,9 +1193,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz",
|
||||
"integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==",
|
||||
"version": "4.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.3.tgz",
|
||||
"integrity": "sha512-P0UxIOrKNBFTQaXTxOH4RxuEBVCgEA5UTNV6Yz7z9QHnUJ7eLX9reOd/NYMO3+XZO2cco19mXTxDMXxit4R/eQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1207,9 +1207,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz",
|
||||
"integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==",
|
||||
"version": "4.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.3.tgz",
|
||||
"integrity": "sha512-L1M0vKGO5ASKntqtsFEjTq/fD91vAqnzeaF6sfNAy55aD+Hi2pBI5DKwCO+UNDQHWsDViJLqshxOahXyLSh3EA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1221,9 +1221,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz",
|
||||
"integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==",
|
||||
"version": "4.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.3.tgz",
|
||||
"integrity": "sha512-btVgIsCjuYFKUjopPoWiDqmoUXQDiW2A4C3Mtmp5vACm7/GnyuprqIDPNczeyR5W8rTXEbkmrJux7cJmD99D2g==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -1235,9 +1235,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz",
|
||||
"integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==",
|
||||
"version": "4.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.3.tgz",
|
||||
"integrity": "sha512-zmjbSphplZlau6ZTkxd3+NMtE4UKVy7U4aVFMmHcgO5CUbw17ZP6QCgyxhzGaU/wFFdTfiojjbLG3/0p9HhAqA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -1249,9 +1249,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz",
|
||||
"integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==",
|
||||
"version": "4.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.3.tgz",
|
||||
"integrity": "sha512-nSZfcZtAnQPRZmUkUQwZq2OjQciR6tEoJaZVFvLHsj0MF6QhNMg0fQ6mUOsiCUpTqxTx0/O6gX0V/nYc7LrgPw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1263,9 +1263,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz",
|
||||
"integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==",
|
||||
"version": "4.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.3.tgz",
|
||||
"integrity": "sha512-MnvSPGO8KJXIMGlQDYfvYS3IosFN2rKsvxRpPO2l2cum+Z3exiExLwVU+GExL96pn8IP+GdH8Tz70EpBhO0sIQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1277,9 +1277,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz",
|
||||
"integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==",
|
||||
"version": "4.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.3.tgz",
|
||||
"integrity": "sha512-+W+p/9QNDr2vE2AXU0qIy0qQE75E8RTwTwgqS2G5CRQ11vzq0tbnfBd6brWhS9bCRjAjepJe2fvvkvS3dno+iw==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -1291,9 +1291,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz",
|
||||
"integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==",
|
||||
"version": "4.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.3.tgz",
|
||||
"integrity": "sha512-yXH6K6KfqGXaxHrtr+Uoy+JpNlUlI46BKVyonGiaD74ravdnF9BUNC+vV+SIuB96hUMGShhKV693rF9QDfO6nQ==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -1305,9 +1305,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz",
|
||||
"integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==",
|
||||
"version": "4.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.3.tgz",
|
||||
"integrity": "sha512-R8cwY9wcnApN/KDYWTH4gV/ypvy9yZUHlbJvfaiXSB48JO3KpwSpjOGqO4jnGkLDSk1hgjYkTbTt6Q7uvPf8eg==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -1319,9 +1319,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz",
|
||||
"integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==",
|
||||
"version": "4.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.3.tgz",
|
||||
"integrity": "sha512-kZPbX/NOPh0vhS5sI+dR8L1bU2cSO9FgxwM8r7wHzGydzfSjLRCFAT87GR5U9scj2rhzN3JPYVC7NoBbl4FZ0g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1333,9 +1333,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz",
|
||||
"integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==",
|
||||
"version": "4.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.3.tgz",
|
||||
"integrity": "sha512-S0Yq+xA1VEH66uiMNhijsWAafffydd2X5b77eLHfRmfLsRSpbiAWiRHV6DEpz6aOToPsgid7TI9rGd6zB1rhbg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1347,9 +1347,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz",
|
||||
"integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==",
|
||||
"version": "4.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.3.tgz",
|
||||
"integrity": "sha512-9isNzeL34yquCPyerog+IMCNxKR8XYmGd0tHSV+OVx0TmE0aJOo9uw4fZfUuk2qxobP5sug6vNdZR6u7Mw7Q+Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1361,9 +1361,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz",
|
||||
"integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==",
|
||||
"version": "4.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.3.tgz",
|
||||
"integrity": "sha512-nMIdKnfZfzn1Vsk+RuOvl43ONTZXoAPUUxgcU0tXooqg4YrAqzfKzVenqqk2g5efWh46/D28cKFrOzDSW28gTA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -1375,9 +1375,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz",
|
||||
"integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==",
|
||||
"version": "4.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.3.tgz",
|
||||
"integrity": "sha512-fOvu7PCQjAj4eWDEuD8Xz5gpzFqXzGlxHZozHP4b9Jxv9APtdxL6STqztDzMLuRXEc4UpXGGhx029Xgm91QBeA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1471,9 +1471,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
||||
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -1596,9 +1596,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/pg": {
|
||||
"version": "8.11.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.10.tgz",
|
||||
"integrity": "sha512-LczQUW4dbOQzsH2RQ5qoeJ6qJPdrcM/DcMLoqWQkMLMsq83J5lAX3LXjdkWdpscFy67JSOWDnh7Ny/sPFykmkg==",
|
||||
"version": "8.11.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.9.tgz",
|
||||
"integrity": "sha512-M4mYeJZRBD9lCBCGa72F44uKSV9eJrAFfjlPJagdA6pgIr2OPJULFB7nqnZzOdqXG0qzHlgtZKzTdIgbmHitSg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1733,17 +1733,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.6.0.tgz",
|
||||
"integrity": "sha512-UOaz/wFowmoh2G6Mr9gw60B1mm0MzUtm6Ic8G2yM1Le6gyj5Loi/N+O5mocugRGY+8OeeKmkMmbxNqUCq3B4Sg==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.5.0.tgz",
|
||||
"integrity": "sha512-lHS5hvz33iUFQKuPFGheAB84LwcJ60G8vKnEhnfcK1l8kGVLro2SFYW6K0/tj8FUhRJ0VHyg1oAfg50QGbPPHw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.6.0",
|
||||
"@typescript-eslint/type-utils": "8.6.0",
|
||||
"@typescript-eslint/utils": "8.6.0",
|
||||
"@typescript-eslint/visitor-keys": "8.6.0",
|
||||
"@typescript-eslint/scope-manager": "8.5.0",
|
||||
"@typescript-eslint/type-utils": "8.5.0",
|
||||
"@typescript-eslint/utils": "8.5.0",
|
||||
"@typescript-eslint/visitor-keys": "8.5.0",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.3.1",
|
||||
"natural-compare": "^1.4.0",
|
||||
@@ -1767,16 +1767,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.6.0.tgz",
|
||||
"integrity": "sha512-eQcbCuA2Vmw45iGfcyG4y6rS7BhWfz9MQuk409WD47qMM+bKCGQWXxvoOs1DUp+T7UBMTtRTVT+kXr7Sh4O9Ow==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.5.0.tgz",
|
||||
"integrity": "sha512-gF77eNv0Xz2UJg/NbpWJ0kqAm35UMsvZf1GHj8D9MRFTj/V3tAciIWXfmPLsAAF/vUlpWPvUDyH1jjsr0cMVWw==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.6.0",
|
||||
"@typescript-eslint/types": "8.6.0",
|
||||
"@typescript-eslint/typescript-estree": "8.6.0",
|
||||
"@typescript-eslint/visitor-keys": "8.6.0",
|
||||
"@typescript-eslint/scope-manager": "8.5.0",
|
||||
"@typescript-eslint/types": "8.5.0",
|
||||
"@typescript-eslint/typescript-estree": "8.5.0",
|
||||
"@typescript-eslint/visitor-keys": "8.5.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1796,14 +1796,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.6.0.tgz",
|
||||
"integrity": "sha512-ZuoutoS5y9UOxKvpc/GkvF4cuEmpokda4wRg64JEia27wX+PysIE9q+lzDtlHHgblwUWwo5/Qn+/WyTUvDwBHw==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.5.0.tgz",
|
||||
"integrity": "sha512-06JOQ9Qgj33yvBEx6tpC8ecP9o860rsR22hWMEd12WcTRrfaFgHr2RB/CA/B+7BMhHkXT4chg2MyboGdFGawYg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.6.0",
|
||||
"@typescript-eslint/visitor-keys": "8.6.0"
|
||||
"@typescript-eslint/types": "8.5.0",
|
||||
"@typescript-eslint/visitor-keys": "8.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -1814,14 +1814,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.6.0.tgz",
|
||||
"integrity": "sha512-dtePl4gsuenXVwC7dVNlb4mGDcKjDT/Ropsk4za/ouMBPplCLyznIaR+W65mvCvsyS97dymoBRrioEXI7k0XIg==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.5.0.tgz",
|
||||
"integrity": "sha512-N1K8Ix+lUM+cIDhL2uekVn/ZD7TZW+9/rwz8DclQpcQ9rk4sIL5CAlBC0CugWKREmDjBzI/kQqU4wkg46jWLYA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "8.6.0",
|
||||
"@typescript-eslint/utils": "8.6.0",
|
||||
"@typescript-eslint/typescript-estree": "8.5.0",
|
||||
"@typescript-eslint/utils": "8.5.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^1.3.0"
|
||||
},
|
||||
@@ -1839,9 +1839,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.6.0.tgz",
|
||||
"integrity": "sha512-rojqFZGd4MQxw33SrOy09qIDS8WEldM8JWtKQLAjf/X5mGSeEFh5ixQlxssMNyPslVIk9yzWqXCsV2eFhYrYUw==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.5.0.tgz",
|
||||
"integrity": "sha512-qjkormnQS5wF9pjSi6q60bKUHH44j2APxfh9TQRXK8wbYVeDYYdYJGIROL87LGZZ2gz3Rbmjc736qyL8deVtdw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -1853,14 +1853,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.6.0.tgz",
|
||||
"integrity": "sha512-MOVAzsKJIPIlLK239l5s06YXjNqpKTVhBVDnqUumQJja5+Y94V3+4VUFRA0G60y2jNnTVwRCkhyGQpavfsbq/g==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.5.0.tgz",
|
||||
"integrity": "sha512-vEG2Sf9P8BPQ+d0pxdfndw3xIXaoSjliG0/Ejk7UggByZPKXmJmw3GW5jV2gHNQNawBUyfahoSiCFVov0Ruf7Q==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.6.0",
|
||||
"@typescript-eslint/visitor-keys": "8.6.0",
|
||||
"@typescript-eslint/types": "8.5.0",
|
||||
"@typescript-eslint/visitor-keys": "8.5.0",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
@@ -1908,16 +1908,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.6.0.tgz",
|
||||
"integrity": "sha512-eNp9cWnYf36NaOVjkEUznf6fEgVy1TWpE0o52e4wtojjBx7D1UV2WAWGzR+8Y5lVFtpMLPwNbC67T83DWSph4A==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.5.0.tgz",
|
||||
"integrity": "sha512-6yyGYVL0e+VzGYp60wvkBHiqDWOpT63pdMV2CVG4LVDd5uR6q1qQN/7LafBZtAtNIn/mqXjsSeS5ggv/P0iECw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@typescript-eslint/scope-manager": "8.6.0",
|
||||
"@typescript-eslint/types": "8.6.0",
|
||||
"@typescript-eslint/typescript-estree": "8.6.0"
|
||||
"@typescript-eslint/scope-manager": "8.5.0",
|
||||
"@typescript-eslint/types": "8.5.0",
|
||||
"@typescript-eslint/typescript-estree": "8.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -1931,13 +1931,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.6.0.tgz",
|
||||
"integrity": "sha512-wapVFfZg9H0qOYh4grNVQiMklJGluQrOUiOhYRrQWhx7BY/+I1IYb8BczWNbbUpO+pqy0rDciv3lQH5E1bCLrg==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.5.0.tgz",
|
||||
"integrity": "sha512-yTPqMnbAZJNy2Xq2XU8AdtOW9tJIr+UQb64aXB9f3B1498Zx9JorVgFJcZpEc9UBuCCrdzKID2RGAMkYcDtZOw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.6.0",
|
||||
"@typescript-eslint/types": "8.5.0",
|
||||
"eslint-visitor-keys": "^3.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1949,9 +1949,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/coverage-v8": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.1.tgz",
|
||||
"integrity": "sha512-md/A7A3c42oTT8JUHSqjP5uKTWJejzUW4jalpvs+rZ27gsURsMU8DEb+8Jf8C6Kj2gwfSHJqobDNBuoqlm0cFw==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.0.tgz",
|
||||
"integrity": "sha512-yqCkr2nrV4o58VcVMxTVkS6Ggxzy7pmSD8JbTbhbH5PsQfUIES1QT716VUzo33wf2lX9EcWYdT3Vl2MMmjR59g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1972,8 +1972,8 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vitest/browser": "2.1.1",
|
||||
"vitest": "2.1.1"
|
||||
"@vitest/browser": "2.1.0",
|
||||
"vitest": "2.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vitest/browser": {
|
||||
@@ -1982,14 +1982,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.1.tgz",
|
||||
"integrity": "sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.0.tgz",
|
||||
"integrity": "sha512-N3/xR4fSu0+6sVZETEtPT1orUs2+Y477JOXTcU3xKuu3uBlsgbD7/7Mz2LZ1Jr1XjwilEWlrIgSCj4N1+5ZmsQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "2.1.1",
|
||||
"@vitest/utils": "2.1.1",
|
||||
"@vitest/spy": "2.1.0",
|
||||
"@vitest/utils": "2.1.0",
|
||||
"chai": "^5.1.1",
|
||||
"tinyrainbow": "^1.2.0"
|
||||
},
|
||||
@@ -1998,9 +1998,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/mocker": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.1.tgz",
|
||||
"integrity": "sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.0.tgz",
|
||||
"integrity": "sha512-ZxENovUqhzl+QiOFpagiHUNUuZ1qPd5yYTCYHomGIZOFArzn4mgX2oxZmiAItJWAaXHG6bbpb/DpSPhlk5DgtA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2012,7 +2012,7 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vitest/spy": "2.1.1",
|
||||
"@vitest/spy": "2.1.0",
|
||||
"msw": "^2.3.5",
|
||||
"vite": "^5.0.0"
|
||||
},
|
||||
@@ -2039,13 +2039,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/runner": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.1.tgz",
|
||||
"integrity": "sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.0.tgz",
|
||||
"integrity": "sha512-D9+ZiB8MbMt7qWDRJc4CRNNUlne/8E1X7dcKhZVAbcOKG58MGGYVDqAq19xlhNfMFZsW0bpVKgztBwks38Ko0w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/utils": "2.1.1",
|
||||
"@vitest/utils": "2.1.0",
|
||||
"pathe": "^1.1.2"
|
||||
},
|
||||
"funding": {
|
||||
@@ -2053,13 +2053,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/snapshot": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.1.tgz",
|
||||
"integrity": "sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.0.tgz",
|
||||
"integrity": "sha512-x69CygGMzt9VCO283K2/FYQ+nBrOj66OTKpsPykjCR4Ac3lLV+m85hj9reaIGmjBSsKzVvbxWmjWE3kF5ha3uQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "2.1.1",
|
||||
"@vitest/pretty-format": "2.1.0",
|
||||
"magic-string": "^0.30.11",
|
||||
"pathe": "^1.1.2"
|
||||
},
|
||||
@@ -2067,10 +2067,23 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/snapshot/node_modules/@vitest/pretty-format": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.0.tgz",
|
||||
"integrity": "sha512-7sxf2F3DNYatgmzXXcTh6cq+/fxwB47RIQqZJFoSH883wnVAoccSRT6g+dTKemUBo8Q5N4OYYj1EBXLuRKvp3Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tinyrainbow": "^1.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/spy": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.1.tgz",
|
||||
"integrity": "sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.0.tgz",
|
||||
"integrity": "sha512-IXX5NkbdgTYTog3F14i2LgnBc+20YmkXMx0IWai84mcxySUDRgm0ihbOfR4L0EVRBDFG85GjmQQEZNNKVVpkZw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2081,13 +2094,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/utils": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.1.tgz",
|
||||
"integrity": "sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.0.tgz",
|
||||
"integrity": "sha512-rreyfVe0PuNqJfKYUwfPDfi6rrp0VSu0Wgvp5WBqJonP+4NvXHk48X6oBam1Lj47Hy6jbJtnMj3OcRdrkTP0tA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "2.1.1",
|
||||
"@vitest/pretty-format": "2.1.0",
|
||||
"loupe": "^3.1.1",
|
||||
"tinyrainbow": "^1.2.0"
|
||||
},
|
||||
@@ -2095,6 +2108,19 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/utils/node_modules/@vitest/pretty-format": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.0.tgz",
|
||||
"integrity": "sha512-7sxf2F3DNYatgmzXXcTh6cq+/fxwB47RIQqZJFoSH883wnVAoccSRT6g+dTKemUBo8Q5N4OYYj1EBXLuRKvp3Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tinyrainbow": "^1.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/abbrev": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
@@ -4142,9 +4168,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jose": {
|
||||
"version": "5.9.2",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-5.9.2.tgz",
|
||||
"integrity": "sha512-ILI2xx/I57b20sd7rHZvgiiQrmp2mcotwsAH+5ajbpFQbrYVQdNHYlQhoA5cFb78CgtBOxtC05TeA+mcgkuCqQ==",
|
||||
"version": "5.8.0",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-5.8.0.tgz",
|
||||
"integrity": "sha512-E7CqYpL/t7MMnfGnK/eg416OsFCVUrU/Y3Vwe7QjKhu/BkS1Ms455+2xsqZQVN57/U2MHMBvEb5SrmAZWAIntA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
@@ -5013,15 +5039,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/pg": {
|
||||
"version": "8.13.0",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.13.0.tgz",
|
||||
"integrity": "sha512-34wkUTh3SxTClfoHB3pQ7bIMvw9dpFU1audQQeZG837fmHfHpr14n/AELVDoOYVDW2h5RDWU78tFjkD+erSBsw==",
|
||||
"version": "8.12.0",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.12.0.tgz",
|
||||
"integrity": "sha512-A+LHUSnwnxrnL/tZ+OLfqR1SxLN3c/pgDztZ47Rpbsd4jUytsTtwQo/TLPRzPJMp/1pbhYVhH9cuSZLAajNfjQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pg-connection-string": "^2.7.0",
|
||||
"pg-pool": "^3.7.0",
|
||||
"pg-protocol": "^1.7.0",
|
||||
"pg-connection-string": "^2.6.4",
|
||||
"pg-pool": "^3.6.2",
|
||||
"pg-protocol": "^1.6.1",
|
||||
"pg-types": "^2.1.0",
|
||||
"pgpass": "1.x"
|
||||
},
|
||||
@@ -5048,11 +5074,10 @@
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/pg-connection-string": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz",
|
||||
"integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"version": "2.6.4",
|
||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz",
|
||||
"integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/pg-int8": {
|
||||
"version": "1.0.1",
|
||||
@@ -5073,21 +5098,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/pg-pool": {
|
||||
"version": "3.7.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.7.0.tgz",
|
||||
"integrity": "sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==",
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz",
|
||||
"integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"pg": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-protocol": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.7.0.tgz",
|
||||
"integrity": "sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz",
|
||||
"integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/pg-types": {
|
||||
"version": "2.2.0",
|
||||
@@ -5135,13 +5158,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.47.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.1.tgz",
|
||||
"integrity": "sha512-SUEKi6947IqYbKxRiqnbUobVZY4bF1uu+ZnZNJX9DfU1tlf2UhWfvVjLf01pQx9URsOr18bFVUKXmanYWhbfkw==",
|
||||
"version": "1.47.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.0.tgz",
|
||||
"integrity": "sha512-jOWiRq2pdNAX/mwLiwFYnPHpEZ4rM+fRSQpRHwEwZlP2PUANvL3+aJOF/bvISMhFD30rqMxUB4RJx9aQbfh4Ww==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.47.1"
|
||||
"playwright-core": "1.47.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
@@ -5154,9 +5177,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.47.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.1.tgz",
|
||||
"integrity": "sha512-i1iyJdLftqtt51mEk6AhYFaAJCDx0xQ/O5NU8EKaWFgMjItPVma542Nh/Aq8aLCjIJSzjaiEQGW/nyqLkGF1OQ==",
|
||||
"version": "1.47.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.0.tgz",
|
||||
"integrity": "sha512-1DyHT8OqkcfCkYUD9zzUTfg7EfTd+6a8MkD/NWOvjo0u/SCNd5YmY/lJwFvUZOxJbWNds+ei7ic2+R/cRz/PDg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
@@ -5606,9 +5629,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz",
|
||||
"integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==",
|
||||
"version": "4.21.3",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.3.tgz",
|
||||
"integrity": "sha512-7sqRtBNnEbcBtMeRVc6VRsJMmpI+JU1z9VTvW8D4gXIYQFz0aLcsE6rRkyghZkLfEgUZgVvOG7A5CVz/VW5GIA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -5622,32 +5645,25 @@
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.22.4",
|
||||
"@rollup/rollup-android-arm64": "4.22.4",
|
||||
"@rollup/rollup-darwin-arm64": "4.22.4",
|
||||
"@rollup/rollup-darwin-x64": "4.22.4",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.22.4",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.22.4",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.22.4",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.22.4",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.22.4",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.22.4",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.22.4",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.22.4",
|
||||
"@rollup/rollup-linux-x64-musl": "4.22.4",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.22.4",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.22.4",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.22.4",
|
||||
"@rollup/rollup-android-arm-eabi": "4.21.3",
|
||||
"@rollup/rollup-android-arm64": "4.21.3",
|
||||
"@rollup/rollup-darwin-arm64": "4.21.3",
|
||||
"@rollup/rollup-darwin-x64": "4.21.3",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.21.3",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.21.3",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.21.3",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.21.3",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.21.3",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.21.3",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.21.3",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.21.3",
|
||||
"@rollup/rollup-linux-x64-musl": "4.21.3",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.21.3",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.21.3",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.21.3",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/rollup/node_modules/@types/estree": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/run-parallel": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
@@ -6387,9 +6403,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.4.7",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.7.tgz",
|
||||
"integrity": "sha512-5l2zxqMEPVENgvzTuBpHer2awaetimj2BGkhBPdnwKbPNOlHsODU+oiazEZzLK7KhAnOrO+XGYJYn4ZlUhDtDQ==",
|
||||
"version": "5.4.6",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.6.tgz",
|
||||
"integrity": "sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -6447,9 +6463,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite-node": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.1.tgz",
|
||||
"integrity": "sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.0.tgz",
|
||||
"integrity": "sha512-+ybYqBVUjYyIscoLzMWodus2enQDZOpGhcU6HdOVD6n8WZdk12w1GFL3mbnxLs7hPtRtqs1Wo5YF6/Tsr6fmhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -6484,19 +6500,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.1.tgz",
|
||||
"integrity": "sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.0.tgz",
|
||||
"integrity": "sha512-XuuEeyNkqbfr0FtAvd9vFbInSSNY1ykCQTYQ0sj9wPy4hx+1gR7gqVNdW0AX2wrrM1wWlN5fnJDjF9xG6mYRSQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/expect": "2.1.1",
|
||||
"@vitest/mocker": "2.1.1",
|
||||
"@vitest/pretty-format": "^2.1.1",
|
||||
"@vitest/runner": "2.1.1",
|
||||
"@vitest/snapshot": "2.1.1",
|
||||
"@vitest/spy": "2.1.1",
|
||||
"@vitest/utils": "2.1.1",
|
||||
"@vitest/expect": "2.1.0",
|
||||
"@vitest/mocker": "2.1.0",
|
||||
"@vitest/pretty-format": "^2.1.0",
|
||||
"@vitest/runner": "2.1.0",
|
||||
"@vitest/snapshot": "2.1.0",
|
||||
"@vitest/spy": "2.1.0",
|
||||
"@vitest/utils": "2.1.0",
|
||||
"chai": "^5.1.1",
|
||||
"debug": "^4.3.6",
|
||||
"magic-string": "^0.30.11",
|
||||
@@ -6507,7 +6523,7 @@
|
||||
"tinypool": "^1.0.0",
|
||||
"tinyrainbow": "^1.2.0",
|
||||
"vite": "^5.0.0",
|
||||
"vite-node": "2.1.1",
|
||||
"vite-node": "2.1.0",
|
||||
"why-is-node-running": "^2.3.0"
|
||||
},
|
||||
"bin": {
|
||||
@@ -6522,8 +6538,8 @@
|
||||
"peerDependencies": {
|
||||
"@edge-runtime/vm": "*",
|
||||
"@types/node": "^18.0.0 || >=20.0.0",
|
||||
"@vitest/browser": "2.1.1",
|
||||
"@vitest/ui": "2.1.1",
|
||||
"@vitest/browser": "2.1.0",
|
||||
"@vitest/ui": "2.1.0",
|
||||
"happy-dom": "*",
|
||||
"jsdom": "*"
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@ from typing import Any, AsyncGenerator, Callable, Iterator
|
||||
from zipfile import BadZipFile
|
||||
|
||||
import orjson
|
||||
from fastapi import Depends, FastAPI, File, Form, HTTPException, Response
|
||||
from fastapi import Depends, FastAPI, File, Form, HTTPException
|
||||
from fastapi.responses import ORJSONResponse
|
||||
from onnxruntime.capi.onnxruntime_pybind11_state import InvalidProtobuf, NoSuchFile
|
||||
from PIL.Image import Image
|
||||
@@ -124,23 +124,6 @@ def get_entries(entries: str = Form()) -> InferenceEntries:
|
||||
raise HTTPException(422, "Invalid request format.")
|
||||
|
||||
|
||||
def get_entry(entries: str = Form()) -> InferenceEntry:
|
||||
try:
|
||||
request: PipelineRequest = orjson.loads(entries)
|
||||
for task, types in request.items():
|
||||
for type, entry in types.items():
|
||||
parsed: InferenceEntry = {
|
||||
"name": entry["modelName"],
|
||||
"task": task,
|
||||
"type": type,
|
||||
"options": entry.get("options", {}),
|
||||
}
|
||||
return parsed
|
||||
except (orjson.JSONDecodeError, ValidationError, KeyError, AttributeError) as e:
|
||||
log.error(f"Invalid request format: {e}")
|
||||
raise HTTPException(422, "Invalid request format.")
|
||||
|
||||
|
||||
app = FastAPI(lifespan=lifespan)
|
||||
|
||||
|
||||
@@ -154,20 +137,6 @@ def ping() -> str:
|
||||
return "pong"
|
||||
|
||||
|
||||
@app.post("/load", response_model=TextResponse)
|
||||
async def load_model(entry: InferenceEntry = Depends(get_entry)) -> None:
|
||||
model = await model_cache.get(entry["name"], entry["type"], entry["task"], ttl=settings.model_ttl)
|
||||
model = await load(model)
|
||||
return Response(status_code=200)
|
||||
|
||||
|
||||
@app.post("/unload", response_model=TextResponse)
|
||||
async def unload_model(entry: InferenceEntry = Depends(get_entry)) -> None:
|
||||
await model_cache.unload(entry["name"], entry["type"], entry["task"])
|
||||
print("unload")
|
||||
return Response(status_code=200)
|
||||
|
||||
|
||||
@app.post("/predict", dependencies=[Depends(update_state)])
|
||||
async def predict(
|
||||
entries: InferenceEntries = Depends(get_entries),
|
||||
|
||||
@@ -58,10 +58,3 @@ class ModelCache:
|
||||
async def revalidate(self, key: str, ttl: int | None) -> None:
|
||||
if ttl is not None and key in self.cache._handlers:
|
||||
await self.cache.expire(key, ttl)
|
||||
|
||||
async def unload(self, model_name: str, model_type: ModelType, model_task: ModelTask) -> None:
|
||||
key = f"{model_name}{model_type}{model_task}"
|
||||
async with OptimisticLock(self.cache, key):
|
||||
value = await self.cache.get(key)
|
||||
if value is not None:
|
||||
await self.cache.delete(key)
|
||||
|
||||
@@ -64,42 +64,45 @@ custom_lint:
|
||||
allowed:
|
||||
# required / wanted
|
||||
- lib/entities/*.entity.dart
|
||||
- lib/repositories/{album,asset,backup,exif_info,user}.repository.dart
|
||||
- lib/repositories/{album,asset,backup,user}.repository.dart
|
||||
# acceptable exceptions for the time being
|
||||
- integration_test/test_utils/general_helper.dart
|
||||
- lib/main.dart
|
||||
- lib/routing/router.dart
|
||||
- lib/utils/{db,migration,renderlist_generator}.dart
|
||||
- lib/utils/{db,image_url_builder,migration,renderlist_generator}.dart
|
||||
- test/**.dart
|
||||
# refactor to make the providers and services testable
|
||||
- lib/pages/common/album_asset_selection.page.dart
|
||||
- lib/pages/common/{album_asset_selection,gallery_viewer}.page.dart
|
||||
- lib/providers/{archive,asset,authentication,db,favorite,partner,trash,user}.provider.dart
|
||||
- lib/providers/{album/album,album/shared_album,asset_viewer/asset_stack,asset_viewer/render_list,backup/backup,backup/manual_upload,search/all_motion_photos,search/recently_added_asset}.provider.dart
|
||||
- lib/services/{asset,background,backup,immich_logger,sync}.service.dart
|
||||
- lib/widgets/asset_grid/asset_grid_data_structure.dart
|
||||
- lib/services/{asset,asset_description,background,backup,backup_verification,hash,immich_logger,memory,partner,person,search,stack,sync,user}.service.dart
|
||||
- lib/widgets/asset_grid/{asset_grid_data_structure,thumbnail_image}.dart
|
||||
|
||||
- import_rule_openapi:
|
||||
message: openapi must only be used through ApiRepositories
|
||||
restrict: package:openapi
|
||||
allowed:
|
||||
# requried / wanted
|
||||
- lib/repositories/*_api.repository.dart
|
||||
- lib/repositories/album_api.repository.dart
|
||||
# acceptable exceptions for the time being
|
||||
- lib/entities/{album,asset,exif_info,user}.entity.dart # to convert DTOs to entities
|
||||
- lib/utils/{image_url_builder,openapi_patching}.dart # utils are fine
|
||||
- test/modules/utils/openapi_patching_test.dart # filename is self-explanatory...
|
||||
# refactor
|
||||
- lib/models/activities/activity.model.dart
|
||||
- lib/models/map/map_marker.model.dart
|
||||
- lib/models/search/search_filter.model.dart
|
||||
- lib/models/server_info/server_{config,disk_info,features,version}.model.dart
|
||||
- lib/models/shared_link/shared_link.model.dart
|
||||
- lib/pages/search/search_input.page.dart
|
||||
- lib/providers/asset_viewer/asset_people.provider.dart
|
||||
- lib/providers/authentication.provider.dart
|
||||
- lib/providers/image/immich_remote_{image,thumbnail}_provider.dart
|
||||
- lib/providers/map/map_state.provider.dart
|
||||
- lib/providers/search/{search,search_filter}.provider.dart
|
||||
- lib/providers/search/{people,search,search_filter}.provider.dart
|
||||
- lib/providers/websocket.provider.dart
|
||||
- lib/routing/auth_guard.dart
|
||||
- lib/services/{api,asset,backup,memory,oauth,search,shared_link,stack,trash}.service.dart
|
||||
- lib/services/{activity,api,asset,asset_description,backup,memory,oauth,partner,person,search,shared_link,stack,trash,user}.service.dart
|
||||
- lib/widgets/album/album_thumbnail_listtile.dart
|
||||
- lib/widgets/forms/login/login_form.dart
|
||||
- lib/widgets/search/search_filter/{camera_picker,location_picker,people_picker}.dart
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
const int noDbId = -9223372036854775808; // from Isar
|
||||
@@ -1,16 +0,0 @@
|
||||
import 'package:immich_mobile/models/activities/activity.model.dart';
|
||||
|
||||
abstract interface class IActivityApiRepository {
|
||||
Future<List<Activity>> getAll(
|
||||
String albumId, {
|
||||
String? assetId,
|
||||
});
|
||||
Future<Activity> create(
|
||||
String albumId,
|
||||
ActivityType type, {
|
||||
String? assetId,
|
||||
String? comment,
|
||||
});
|
||||
Future<void> delete(String id);
|
||||
Future<ActivityStats> getStats(String albumId, {String? assetId});
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:immich_mobile/entities/album.entity.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/device_asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/user.entity.dart';
|
||||
|
||||
abstract interface class IAssetRepository {
|
||||
@@ -8,20 +7,4 @@ abstract interface class IAssetRepository {
|
||||
Future<List<Asset>> getAllByRemoteId(Iterable<String> ids);
|
||||
Future<List<Asset>> getByAlbum(Album album, {User? notOwnedBy});
|
||||
Future<void> deleteById(List<int> ids);
|
||||
Future<List<Asset>> getAll({
|
||||
required int ownerId,
|
||||
bool? remote,
|
||||
int limit = 100,
|
||||
});
|
||||
Future<List<Asset>> updateAll(List<Asset> assets);
|
||||
|
||||
Future<List<Asset>> getMatches({
|
||||
required List<Asset> assets,
|
||||
required int ownerId,
|
||||
bool? remote,
|
||||
int limit = 100,
|
||||
});
|
||||
|
||||
Future<List<DeviceAsset?>> getDeviceAssetsById(List<Object> ids);
|
||||
Future<void> upsertDeviceAssets(List<DeviceAsset> deviceAssets);
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
|
||||
abstract interface class IAssetApiRepository {
|
||||
// Future<Asset> get(String id);
|
||||
|
||||
// Future<List<Asset>> getAll();
|
||||
|
||||
// Future<Asset> create(Asset asset);
|
||||
|
||||
Future<Asset> update(
|
||||
String id, {
|
||||
String? description,
|
||||
});
|
||||
|
||||
// Future<void> delete(String id);
|
||||
|
||||
Future<List<Asset>> search({List<String> personIds = const []});
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
||||
|
||||
abstract interface class IExifInfoRepository {
|
||||
Future<ExifInfo?> get(int id);
|
||||
|
||||
Future<ExifInfo> update(ExifInfo exifInfo);
|
||||
|
||||
Future<void> delete(int id);
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import 'package:immich_mobile/entities/user.entity.dart';
|
||||
|
||||
abstract interface class IPartnerApiRepository {
|
||||
Future<List<User>> getAll(Direction direction);
|
||||
Future<User> create(String id);
|
||||
Future<User> update(String id, {required bool inTimeline});
|
||||
Future<void> delete(String id);
|
||||
}
|
||||
|
||||
enum Direction {
|
||||
sharedWithMe,
|
||||
sharedByMe,
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
abstract interface class IPersonApiRepository {
|
||||
Future<List<Person>> getAll();
|
||||
Future<Person> update(String id, {String? name});
|
||||
}
|
||||
|
||||
class Person {
|
||||
Person({
|
||||
required this.id,
|
||||
required this.isHidden,
|
||||
required this.name,
|
||||
required this.thumbnailPath,
|
||||
this.birthDate,
|
||||
this.updatedAt,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final DateTime? birthDate;
|
||||
final bool isHidden;
|
||||
final String name;
|
||||
final String thumbnailPath;
|
||||
final DateTime? updatedAt;
|
||||
}
|
||||
@@ -3,6 +3,4 @@ import 'package:immich_mobile/entities/user.entity.dart';
|
||||
abstract interface class IUserRepository {
|
||||
Future<List<User>> getByIds(List<String> ids);
|
||||
Future<User?> get(String id);
|
||||
Future<List<User>> getAll({bool self = true});
|
||||
Future<User> update(User user);
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:immich_mobile/entities/user.entity.dart';
|
||||
|
||||
abstract interface class IUserApiRepository {
|
||||
Future<List<User>> getAll();
|
||||
Future<({String profileImagePath})> createProfileImage({
|
||||
required String name,
|
||||
required Uint8List data,
|
||||
});
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:immich_mobile/entities/user.entity.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
enum ActivityType { comment, like }
|
||||
|
||||
@@ -37,6 +38,16 @@ class Activity {
|
||||
);
|
||||
}
|
||||
|
||||
Activity.fromDto(ActivityResponseDto dto)
|
||||
: id = dto.id,
|
||||
assetId = dto.assetId,
|
||||
comment = dto.comment,
|
||||
createdAt = dto.createdAt,
|
||||
type = dto.type == ReactionType.comment
|
||||
? ActivityType.comment
|
||||
: ActivityType.like,
|
||||
user = User.fromSimpleUserDto(dto.user);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Activity(id: $id, assetId: $assetId, comment: $comment, createdAt: $createdAt, type: $type, user: $user)';
|
||||
@@ -64,9 +75,3 @@ class Activity {
|
||||
user.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
class ActivityStats {
|
||||
final int comments;
|
||||
|
||||
const ActivityStats({required this.comments});
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/person_api.interface.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class SearchLocationFilter {
|
||||
String? country;
|
||||
@@ -235,7 +235,7 @@ class SearchDisplayFilters {
|
||||
class SearchFilter {
|
||||
String? context;
|
||||
String? filename;
|
||||
Set<Person> people;
|
||||
Set<PersonResponseDto> people;
|
||||
SearchLocationFilter location;
|
||||
SearchCameraFilter camera;
|
||||
SearchDateFilter date;
|
||||
@@ -258,7 +258,7 @@ class SearchFilter {
|
||||
SearchFilter copyWith({
|
||||
String? context,
|
||||
String? filename,
|
||||
Set<Person>? people,
|
||||
Set<PersonResponseDto>? people,
|
||||
SearchLocationFilter? location,
|
||||
SearchCameraFilter? camera,
|
||||
SearchDateFilter? date,
|
||||
|
||||
@@ -8,7 +8,6 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/constants.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/pages/common/video_viewer.page.dart';
|
||||
@@ -31,6 +30,7 @@ import 'package:immich_mobile/widgets/photo_view/photo_view_gallery.dart';
|
||||
import 'package:immich_mobile/widgets/photo_view/src/photo_view_computed_scale.dart';
|
||||
import 'package:immich_mobile/widgets/photo_view/src/photo_view_scale_state.dart';
|
||||
import 'package:immich_mobile/widgets/photo_view/src/utils/photo_view_hero_attributes.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@RoutePage()
|
||||
// ignore: must_be_immutable
|
||||
@@ -73,7 +73,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||
: <Asset>[];
|
||||
final stackElements = showStack ? [currentAsset, ...stack] : <Asset>[];
|
||||
// Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id
|
||||
final isFromDto = currentAsset.id == noDbId;
|
||||
final isFromDto = currentAsset.id == Isar.autoIncrement;
|
||||
|
||||
Asset asset = stackIndex.value == -1
|
||||
? currentAsset
|
||||
|
||||
@@ -8,7 +8,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||
import 'package:immich_mobile/interfaces/person_api.interface.dart';
|
||||
import 'package:immich_mobile/models/search/search_filter.model.dart';
|
||||
import 'package:immich_mobile/providers/search/paginated_search.provider.dart';
|
||||
import 'package:immich_mobile/widgets/asset_grid/multiselect_grid.dart';
|
||||
@@ -20,6 +19,7 @@ import 'package:immich_mobile/widgets/search/search_filter/media_type_picker.dar
|
||||
import 'package:immich_mobile/widgets/search/search_filter/people_picker.dart';
|
||||
import 'package:immich_mobile/widgets/search/search_filter/search_filter_chip.dart';
|
||||
import 'package:immich_mobile/widgets/search/search_filter/search_filter_utils.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
@RoutePage()
|
||||
class SearchInputPage extends HookConsumerWidget {
|
||||
@@ -110,7 +110,7 @@ class SearchInputPage extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
showPeoplePicker() {
|
||||
handleOnSelect(Set<Person> value) {
|
||||
handleOnSelect(Set<PersonResponseDto> value) {
|
||||
filter.value = filter.value.copyWith(
|
||||
people: value,
|
||||
);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:immich_mobile/repositories/activity_api.repository.dart';
|
||||
import 'package:immich_mobile/services/activity.service.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'activity_service.provider.g.dart';
|
||||
|
||||
@riverpod
|
||||
ActivityService activityService(ActivityServiceRef ref) =>
|
||||
ActivityService(ref.watch(activityApiRepositoryProvider));
|
||||
ActivityService(ref.watch(apiServiceProvider));
|
||||
|
||||
@@ -6,7 +6,7 @@ part of 'activity_service.provider.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$activityServiceHash() => r'23a3ee7db71676d2719daa64217a683cc5c7eab0';
|
||||
String _$activityServiceHash() => r'5dd4955d14f5bf01c00d7f8750d07e7ace7cc4b0';
|
||||
|
||||
/// See also [activityService].
|
||||
@ProviderFor(activityService)
|
||||
|
||||
@@ -11,7 +11,7 @@ class ActivityStatistics extends _$ActivityStatistics {
|
||||
ref
|
||||
.watch(activityServiceProvider)
|
||||
.getStatistics(albumId, assetId: assetId)
|
||||
.then((stats) => state = stats.comments);
|
||||
.then((comments) => state = comments);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ part of 'activity_statistics.provider.dart';
|
||||
// **************************************************************************
|
||||
|
||||
String _$activityStatisticsHash() =>
|
||||
r'1f43f0bcb11c754ca3cb586a13570db25023b9a8';
|
||||
r'a5f7bbee1891c33b72919a34e632ca9ef9cd8dbf';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
||||
@@ -5,5 +5,5 @@ import 'package:immich_mobile/services/user.service.dart';
|
||||
final otherUsersProvider = FutureProvider.autoDispose<List<User>>((ref) {
|
||||
UserService userService = ref.watch(userServiceProvider);
|
||||
|
||||
return userService.getUsers();
|
||||
return userService.getUsersInDb();
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@ part of 'map_state.provider.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$mapStateNotifierHash() => r'22e4e571bd0730dbc34b109255a62b920e9c7d66';
|
||||
String _$mapStateNotifierHash() => r'31fafe17aa85c48379a22ed3db3cc94af59ce5b8';
|
||||
|
||||
/// See also [MapStateNotifier].
|
||||
@ProviderFor(MapStateNotifier)
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import 'package:immich_mobile/interfaces/person_api.interface.dart';
|
||||
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
||||
import 'package:immich_mobile/services/person.service.dart';
|
||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'people.provider.g.dart';
|
||||
|
||||
@riverpod
|
||||
Future<List<Person>> getAllPeople(
|
||||
Future<List<PersonResponseDto>> getAllPeople(
|
||||
GetAllPeopleRef ref,
|
||||
) async {
|
||||
final PersonService personService = ref.read(personServiceProvider);
|
||||
|
||||
@@ -6,11 +6,12 @@ part of 'people.provider.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$getAllPeopleHash() => r'3417b7e0c211382d4480a415e352139995d57b6d';
|
||||
String _$getAllPeopleHash() => r'4eff6666be5a74710d1e8587e01d8154310d85bd';
|
||||
|
||||
/// See also [getAllPeople].
|
||||
@ProviderFor(getAllPeople)
|
||||
final getAllPeopleProvider = AutoDisposeFutureProvider<List<Person>>.internal(
|
||||
final getAllPeopleProvider =
|
||||
AutoDisposeFutureProvider<List<PersonResponseDto>>.internal(
|
||||
getAllPeople,
|
||||
name: r'getAllPeopleProvider',
|
||||
debugGetCreateSourceHash:
|
||||
@@ -19,7 +20,7 @@ final getAllPeopleProvider = AutoDisposeFutureProvider<List<Person>>.internal(
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef GetAllPeopleRef = AutoDisposeFutureProviderRef<List<Person>>;
|
||||
typedef GetAllPeopleRef = AutoDisposeFutureProviderRef<List<PersonResponseDto>>;
|
||||
String _$personAssetsHash() => r'3dfecb67a54d07e4208bcb9581b2625acd2e1832';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/activity_api.interface.dart';
|
||||
import 'package:immich_mobile/models/activities/activity.model.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/repositories/base_api.repository.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
final activityApiRepositoryProvider = Provider(
|
||||
(ref) => ActivityApiRepository(ref.watch(apiServiceProvider).activitiesApi),
|
||||
);
|
||||
|
||||
class ActivityApiRepository extends BaseApiRepository
|
||||
implements IActivityApiRepository {
|
||||
final ActivitiesApi _api;
|
||||
|
||||
ActivityApiRepository(this._api);
|
||||
|
||||
@override
|
||||
Future<List<Activity>> getAll(String albumId, {String? assetId}) async {
|
||||
final response =
|
||||
await checkNull(_api.getActivities(albumId, assetId: assetId));
|
||||
return response.map(_toActivity).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Activity> create(
|
||||
String albumId,
|
||||
ActivityType type, {
|
||||
String? assetId,
|
||||
String? comment,
|
||||
}) async {
|
||||
final dto = ActivityCreateDto(
|
||||
albumId: albumId,
|
||||
type: type == ActivityType.comment
|
||||
? ReactionType.comment
|
||||
: ReactionType.like,
|
||||
assetId: assetId,
|
||||
comment: comment,
|
||||
);
|
||||
final response = await checkNull(_api.createActivity(dto));
|
||||
return _toActivity(response);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> delete(String id) {
|
||||
return checkNull(_api.deleteActivity(id));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ActivityStats> getStats(String albumId, {String? assetId}) async {
|
||||
final response =
|
||||
await checkNull(_api.getActivityStatistics(albumId, assetId: assetId));
|
||||
return ActivityStats(comments: response.comments);
|
||||
}
|
||||
|
||||
static Activity _toActivity(ActivityResponseDto dto) => Activity(
|
||||
id: dto.id,
|
||||
createdAt: dto.createdAt,
|
||||
type: dto.type == ReactionType.comment
|
||||
? ActivityType.comment
|
||||
: ActivityType.like,
|
||||
user: User.fromSimpleUserDto(dto.user),
|
||||
assetId: dto.assetId,
|
||||
comment: dto.comment,
|
||||
);
|
||||
}
|
||||
@@ -1,31 +1,30 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/errors.dart';
|
||||
import 'package:immich_mobile/entities/album.entity.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/album_api.interface.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/repositories/base_api.repository.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
final albumApiRepositoryProvider = Provider(
|
||||
(ref) => AlbumApiRepository(ref.watch(apiServiceProvider).albumsApi),
|
||||
);
|
||||
|
||||
class AlbumApiRepository extends BaseApiRepository
|
||||
implements IAlbumApiRepository {
|
||||
class AlbumApiRepository implements IAlbumApiRepository {
|
||||
final AlbumsApi _api;
|
||||
|
||||
AlbumApiRepository(this._api);
|
||||
|
||||
@override
|
||||
Future<Album> get(String id) async {
|
||||
final dto = await checkNull(_api.getAlbumInfo(id));
|
||||
final dto = await _checkNull(_api.getAlbumInfo(id));
|
||||
return _toAlbum(dto);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Album>> getAll({bool? shared}) async {
|
||||
final dtos = await checkNull(_api.getAllAlbums(shared: shared));
|
||||
final dtos = await _checkNull(_api.getAllAlbums(shared: shared));
|
||||
return dtos.map(_toAlbum).toList().cast();
|
||||
}
|
||||
|
||||
@@ -38,7 +37,7 @@ class AlbumApiRepository extends BaseApiRepository
|
||||
final users = sharedUserIds.map(
|
||||
(id) => AlbumUserCreateDto(userId: id, role: AlbumUserRole.editor),
|
||||
);
|
||||
final responseDto = await checkNull(
|
||||
final responseDto = await _checkNull(
|
||||
_api.createAlbum(
|
||||
CreateAlbumDto(
|
||||
albumName: name,
|
||||
@@ -58,7 +57,7 @@ class AlbumApiRepository extends BaseApiRepository
|
||||
String? description,
|
||||
bool? activityEnabled,
|
||||
}) async {
|
||||
final response = await checkNull(
|
||||
final response = await _checkNull(
|
||||
_api.updateAlbumInfo(
|
||||
albumId,
|
||||
UpdateAlbumDto(
|
||||
@@ -82,7 +81,7 @@ class AlbumApiRepository extends BaseApiRepository
|
||||
String albumId,
|
||||
Iterable<String> assetIds,
|
||||
) async {
|
||||
final response = await checkNull(
|
||||
final response = await _checkNull(
|
||||
_api.addAssetsToAlbum(
|
||||
albumId,
|
||||
BulkIdsDto(ids: assetIds.toList()),
|
||||
@@ -107,7 +106,7 @@ class AlbumApiRepository extends BaseApiRepository
|
||||
String albumId,
|
||||
Iterable<String> assetIds,
|
||||
) async {
|
||||
final response = await checkNull(
|
||||
final response = await _checkNull(
|
||||
_api.removeAssetFromAlbum(
|
||||
albumId,
|
||||
BulkIdsDto(ids: assetIds.toList()),
|
||||
@@ -128,7 +127,7 @@ class AlbumApiRepository extends BaseApiRepository
|
||||
Future<Album> addUsers(String albumId, Iterable<String> userIds) async {
|
||||
final albumUsers =
|
||||
userIds.map((userId) => AlbumUserAddDto(userId: userId)).toList();
|
||||
final response = await checkNull(
|
||||
final response = await _checkNull(
|
||||
_api.addUsersToAlbum(
|
||||
albumId,
|
||||
AddUsersDto(albumUsers: albumUsers),
|
||||
@@ -142,6 +141,12 @@ class AlbumApiRepository extends BaseApiRepository
|
||||
return _api.removeUserFromAlbum(albumId, userId);
|
||||
}
|
||||
|
||||
static Future<T> _checkNull<T>(Future<T?> future) async {
|
||||
final response = await future;
|
||||
if (response == null) throw NoResponseDtoError();
|
||||
return response;
|
||||
}
|
||||
|
||||
static Album _toAlbum(AlbumResponseDto dto) {
|
||||
final Album album = Album(
|
||||
remoteId: dto.id,
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/entities/album.entity.dart';
|
||||
import 'package:immich_mobile/entities/android_device_asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/device_asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/ios_device_asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/providers/db.provider.dart';
|
||||
@@ -40,104 +35,4 @@ class AssetRepository implements IAssetRepository {
|
||||
@override
|
||||
Future<List<Asset>> getAllByRemoteId(Iterable<String> ids) =>
|
||||
_db.assets.getAllByRemoteId(ids);
|
||||
|
||||
@override
|
||||
Future<List<Asset>> getAll({
|
||||
required int ownerId,
|
||||
bool? remote,
|
||||
int limit = 100,
|
||||
}) {
|
||||
if (remote == null) {
|
||||
return _db.assets
|
||||
.where()
|
||||
.ownerIdEqualToAnyChecksum(ownerId)
|
||||
.limit(limit)
|
||||
.findAll();
|
||||
}
|
||||
final QueryBuilder<Asset, Asset, QAfterFilterCondition> query;
|
||||
if (remote) {
|
||||
query = _db.assets
|
||||
.where()
|
||||
.localIdIsNull()
|
||||
.filter()
|
||||
.remoteIdIsNotNull()
|
||||
.ownerIdEqualTo(ownerId);
|
||||
} else {
|
||||
query = _db.assets
|
||||
.where()
|
||||
.remoteIdIsNull()
|
||||
.filter()
|
||||
.localIdIsNotNull()
|
||||
.ownerIdEqualTo(ownerId);
|
||||
}
|
||||
|
||||
return query.limit(limit).findAll();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Asset>> updateAll(List<Asset> assets) async {
|
||||
await _db.writeTxn(() => _db.assets.putAll(assets));
|
||||
return assets;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Asset>> getMatches({
|
||||
required List<Asset> assets,
|
||||
required int ownerId,
|
||||
bool? remote,
|
||||
int limit = 100,
|
||||
}) {
|
||||
final QueryBuilder<Asset, Asset, QAfterFilterCondition> query;
|
||||
if (remote == null) {
|
||||
query = _db.assets.filter().remoteIdIsNotNull().or().localIdIsNotNull();
|
||||
} else if (remote) {
|
||||
query = _db.assets.where().localIdIsNull().filter().remoteIdIsNotNull();
|
||||
} else {
|
||||
query = _db.assets.where().remoteIdIsNull().filter().localIdIsNotNull();
|
||||
}
|
||||
return _getMatchesImpl(query, ownerId, assets, limit);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<DeviceAsset?>> getDeviceAssetsById(List<Object> ids) =>
|
||||
Platform.isAndroid
|
||||
? _db.androidDeviceAssets.getAll(ids.cast())
|
||||
: _db.iOSDeviceAssets.getAllById(ids.cast());
|
||||
|
||||
@override
|
||||
Future<void> upsertDeviceAssets(List<DeviceAsset> deviceAssets) =>
|
||||
_db.writeTxn(
|
||||
() => Platform.isAndroid
|
||||
? _db.androidDeviceAssets.putAll(deviceAssets.cast())
|
||||
: _db.iOSDeviceAssets.putAll(deviceAssets.cast()),
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<Asset>> _getMatchesImpl(
|
||||
QueryBuilder<Asset, Asset, QAfterFilterCondition> query,
|
||||
int ownerId,
|
||||
List<Asset> assets,
|
||||
int limit,
|
||||
) =>
|
||||
query
|
||||
.ownerIdEqualTo(ownerId)
|
||||
.anyOf(
|
||||
assets,
|
||||
(q, Asset a) => q
|
||||
.fileNameEqualTo(a.fileName)
|
||||
.and()
|
||||
.durationInSecondsEqualTo(a.durationInSeconds)
|
||||
.and()
|
||||
.fileCreatedAtBetween(
|
||||
a.fileCreatedAt.subtract(const Duration(hours: 12)),
|
||||
a.fileCreatedAt.add(const Duration(hours: 12)),
|
||||
)
|
||||
.and()
|
||||
.not()
|
||||
.checksumEqualTo(a.checksum),
|
||||
)
|
||||
.sortByFileName()
|
||||
.thenByFileCreatedAt()
|
||||
.thenByFileModifiedAt()
|
||||
.limit(limit)
|
||||
.findAll();
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/asset_api.interface.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/repositories/base_api.repository.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
final assetApiRepositoryProvider = Provider(
|
||||
(ref) => AssetApiRepository(
|
||||
ref.watch(apiServiceProvider).assetsApi,
|
||||
ref.watch(apiServiceProvider).searchApi,
|
||||
),
|
||||
);
|
||||
|
||||
class AssetApiRepository extends BaseApiRepository
|
||||
implements IAssetApiRepository {
|
||||
final AssetsApi _api;
|
||||
final SearchApi _searchApi;
|
||||
|
||||
AssetApiRepository(this._api, this._searchApi);
|
||||
|
||||
@override
|
||||
Future<Asset> update(String id, {String? description}) async {
|
||||
final response = await checkNull(
|
||||
_api.updateAsset(id, UpdateAssetDto(description: description)),
|
||||
);
|
||||
return Asset.remote(response);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Asset>> search({List<String> personIds = const []}) async {
|
||||
// TODO this always fetches all assets, change API and usage to actually do pagination
|
||||
final List<Asset> result = [];
|
||||
bool hasNext = true;
|
||||
int currentPage = 1;
|
||||
while (hasNext) {
|
||||
final response = await checkNull(
|
||||
_searchApi.searchMetadata(
|
||||
MetadataSearchDto(
|
||||
personIds: personIds,
|
||||
page: currentPage,
|
||||
size: 1000,
|
||||
),
|
||||
),
|
||||
);
|
||||
result.addAll(response.assets.items.map(Asset.remote));
|
||||
hasNext = response.assets.nextPage != null;
|
||||
currentPage++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:immich_mobile/constants/errors.dart';
|
||||
|
||||
abstract class BaseApiRepository {
|
||||
@protected
|
||||
Future<T> checkNull<T>(Future<T?> future) async {
|
||||
final response = await future;
|
||||
if (response == null) throw NoResponseDtoError();
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/exif_info.interface.dart';
|
||||
import 'package:immich_mobile/providers/db.provider.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
final exifInfoRepositoryProvider =
|
||||
Provider((ref) => ExifInfoRepository(ref.watch(dbProvider)));
|
||||
|
||||
class ExifInfoRepository implements IExifInfoRepository {
|
||||
final Isar _db;
|
||||
|
||||
ExifInfoRepository(
|
||||
this._db,
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> delete(int id) => _db.exifInfos.delete(id);
|
||||
|
||||
@override
|
||||
Future<ExifInfo?> get(int id) => _db.exifInfos.get(id);
|
||||
|
||||
@override
|
||||
Future<ExifInfo> update(ExifInfo exifInfo) async {
|
||||
await _db.writeTxn(() => _db.exifInfos.put(exifInfo));
|
||||
return exifInfo;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/partner_api.interface.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/repositories/base_api.repository.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
final partnerApiRepositoryProvider = Provider(
|
||||
(ref) => PartnerApiRepository(
|
||||
ref.watch(apiServiceProvider).partnersApi,
|
||||
),
|
||||
);
|
||||
|
||||
class PartnerApiRepository extends BaseApiRepository
|
||||
implements IPartnerApiRepository {
|
||||
final PartnersApi _api;
|
||||
|
||||
PartnerApiRepository(this._api);
|
||||
|
||||
@override
|
||||
Future<List<User>> getAll(Direction direction) async {
|
||||
final response = await checkNull(
|
||||
_api.getPartners(
|
||||
direction == Direction.sharedByMe
|
||||
? PartnerDirection.by
|
||||
: PartnerDirection.with_,
|
||||
),
|
||||
);
|
||||
return response.map(User.fromPartnerDto).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<User> create(String id) async {
|
||||
final dto = await checkNull(_api.createPartner(id));
|
||||
return User.fromPartnerDto(dto);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> delete(String id) => checkNull(_api.removePartner(id));
|
||||
|
||||
@override
|
||||
Future<User> update(String id, {required bool inTimeline}) async {
|
||||
final dto = await checkNull(
|
||||
_api.updatePartner(
|
||||
id,
|
||||
UpdatePartnerDto(inTimeline: inTimeline),
|
||||
),
|
||||
);
|
||||
return User.fromPartnerDto(dto);
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/interfaces/person_api.interface.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/repositories/base_api.repository.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
final personApiRepositoryProvider = Provider(
|
||||
(ref) => PersonApiRepository(ref.watch(apiServiceProvider).peopleApi),
|
||||
);
|
||||
|
||||
class PersonApiRepository extends BaseApiRepository
|
||||
implements IPersonApiRepository {
|
||||
final PeopleApi _api;
|
||||
|
||||
PersonApiRepository(this._api);
|
||||
|
||||
@override
|
||||
Future<List<Person>> getAll() async {
|
||||
final dto = await checkNull(_api.getAllPeople());
|
||||
return dto.people.map(_toPerson).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Person> update(String id, {String? name}) async {
|
||||
final dto = await checkNull(
|
||||
_api.updatePerson(id, PersonUpdateDto(name: name)),
|
||||
);
|
||||
return _toPerson(dto);
|
||||
}
|
||||
|
||||
static Person _toPerson(PersonResponseDto dto) => Person(
|
||||
birthDate: dto.birthDate,
|
||||
id: dto.id,
|
||||
isHidden: dto.isHidden,
|
||||
name: dto.name,
|
||||
thumbnailPath: dto.thumbnailPath,
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/providers/db.provider.dart';
|
||||
@@ -21,19 +20,4 @@ class UserRepository implements IUserRepository {
|
||||
|
||||
@override
|
||||
Future<User?> get(String id) => _db.users.getById(id);
|
||||
|
||||
@override
|
||||
Future<List<User>> getAll({bool self = true}) {
|
||||
if (self) {
|
||||
return _db.users.where().findAll();
|
||||
}
|
||||
final int userId = Store.get(StoreKey.currentUser).isarId;
|
||||
return _db.users.where().isarIdNotEqualTo(userId).findAll();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<User> update(User user) async {
|
||||
await _db.writeTxn(() => _db.users.put(user));
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:immich_mobile/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/user_api.interface.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/repositories/base_api.repository.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
final userApiRepositoryProvider = Provider(
|
||||
(ref) => UserApiRepository(
|
||||
ref.watch(apiServiceProvider).usersApi,
|
||||
),
|
||||
);
|
||||
|
||||
class UserApiRepository extends BaseApiRepository
|
||||
implements IUserApiRepository {
|
||||
final UsersApi _api;
|
||||
|
||||
UserApiRepository(this._api);
|
||||
|
||||
@override
|
||||
Future<List<User>> getAll() async {
|
||||
final dto = await checkNull(_api.searchUsers());
|
||||
return dto.map(User.fromSimpleUserDto).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<({String profileImagePath})> createProfileImage({
|
||||
required String name,
|
||||
required Uint8List data,
|
||||
}) async {
|
||||
final response = await checkNull(
|
||||
_api.createProfileImage(
|
||||
MultipartFile.fromBytes('file', data, filename: name),
|
||||
),
|
||||
);
|
||||
return (profileImagePath: response.profileImagePath);
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,41 @@
|
||||
import 'package:immich_mobile/interfaces/activity_api.interface.dart';
|
||||
import 'package:immich_mobile/constants/errors.dart';
|
||||
import 'package:immich_mobile/mixins/error_logger.mixin.dart';
|
||||
import 'package:immich_mobile/models/activities/activity.model.dart';
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class ActivityService with ErrorLoggerMixin {
|
||||
final IActivityApiRepository _activityApiRepository;
|
||||
final ApiService _apiService;
|
||||
|
||||
@override
|
||||
final Logger logger = Logger("ActivityService");
|
||||
|
||||
ActivityService(this._activityApiRepository);
|
||||
ActivityService(this._apiService);
|
||||
|
||||
Future<List<Activity>> getAllActivities(
|
||||
String albumId, {
|
||||
String? assetId,
|
||||
}) async {
|
||||
return logError(
|
||||
() => _activityApiRepository.getAll(albumId, assetId: assetId),
|
||||
() async {
|
||||
final list = await _apiService.activitiesApi
|
||||
.getActivities(albumId, assetId: assetId);
|
||||
return list != null ? list.map(Activity.fromDto).toList() : [];
|
||||
},
|
||||
defaultValue: [],
|
||||
errorMessage: "Failed to get all activities for album $albumId",
|
||||
);
|
||||
}
|
||||
|
||||
Future<ActivityStats> getStatistics(String albumId, {String? assetId}) async {
|
||||
Future<int> getStatistics(String albumId, {String? assetId}) async {
|
||||
return logError(
|
||||
() => _activityApiRepository.getStats(albumId, assetId: assetId),
|
||||
defaultValue: const ActivityStats(comments: 0),
|
||||
() async {
|
||||
final dto = await _apiService.activitiesApi
|
||||
.getActivityStatistics(albumId, assetId: assetId);
|
||||
return dto?.comments ?? 0;
|
||||
},
|
||||
defaultValue: 0,
|
||||
errorMessage: "Failed to statistics for album $albumId",
|
||||
);
|
||||
}
|
||||
@@ -33,7 +43,7 @@ class ActivityService with ErrorLoggerMixin {
|
||||
Future<bool> removeActivity(String id) async {
|
||||
return logError(
|
||||
() async {
|
||||
await _activityApiRepository.delete(id);
|
||||
await _apiService.activitiesApi.deleteActivity(id);
|
||||
return true;
|
||||
},
|
||||
defaultValue: false,
|
||||
@@ -48,12 +58,22 @@ class ActivityService with ErrorLoggerMixin {
|
||||
String? comment,
|
||||
}) async {
|
||||
return guardError(
|
||||
() => _activityApiRepository.create(
|
||||
albumId,
|
||||
type,
|
||||
assetId: assetId,
|
||||
comment: comment,
|
||||
),
|
||||
() async {
|
||||
final dto = await _apiService.activitiesApi.createActivity(
|
||||
ActivityCreateDto(
|
||||
albumId: albumId,
|
||||
type: type == ActivityType.comment
|
||||
? ReactionType.comment
|
||||
: ReactionType.like,
|
||||
assetId: assetId,
|
||||
comment: comment,
|
||||
),
|
||||
);
|
||||
if (dto != null) {
|
||||
return Activity.fromDto(dto);
|
||||
}
|
||||
throw NoResponseDtoError();
|
||||
},
|
||||
errorMessage: "Failed to create $type for album $albumId",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,13 +9,9 @@ import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/etag.entity.dart';
|
||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
||||
import 'package:immich_mobile/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/asset_api.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/exif_info.interface.dart';
|
||||
import 'package:immich_mobile/models/backup/backup_candidate.model.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/providers/db.provider.dart';
|
||||
import 'package:immich_mobile/repositories/asset_api.repository.dart';
|
||||
import 'package:immich_mobile/repositories/exif_info.repository.dart';
|
||||
import 'package:immich_mobile/services/album.service.dart';
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
import 'package:immich_mobile/services/backup.service.dart';
|
||||
@@ -28,8 +24,6 @@ import 'package:openapi/api.dart';
|
||||
|
||||
final assetServiceProvider = Provider(
|
||||
(ref) => AssetService(
|
||||
ref.watch(assetApiRepositoryProvider),
|
||||
ref.watch(exifInfoRepositoryProvider),
|
||||
ref.watch(apiServiceProvider),
|
||||
ref.watch(syncServiceProvider),
|
||||
ref.watch(userServiceProvider),
|
||||
@@ -40,8 +34,6 @@ final assetServiceProvider = Provider(
|
||||
);
|
||||
|
||||
class AssetService {
|
||||
final IAssetApiRepository _assetApiRepository;
|
||||
final IExifInfoRepository _exifInfoRepository;
|
||||
final ApiService _apiService;
|
||||
final SyncService _syncService;
|
||||
final UserService _userService;
|
||||
@@ -51,8 +43,6 @@ class AssetService {
|
||||
final Isar _db;
|
||||
|
||||
AssetService(
|
||||
this._assetApiRepository,
|
||||
this._exifInfoRepository,
|
||||
this._apiService,
|
||||
this._syncService,
|
||||
this._userService,
|
||||
@@ -352,46 +342,4 @@ class AssetService {
|
||||
log.severe("Error while syncing uploaded asset to albums", error, stack);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setDescription(
|
||||
Asset asset,
|
||||
String newDescription,
|
||||
) async {
|
||||
final remoteAssetId = asset.remoteId;
|
||||
final localExifId = asset.exifInfo?.id;
|
||||
|
||||
// Guard [remoteAssetId] and [localExifId] null
|
||||
if (remoteAssetId == null || localExifId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final result = await _assetApiRepository.update(
|
||||
remoteAssetId,
|
||||
description: newDescription,
|
||||
);
|
||||
|
||||
final description = result.exifInfo?.description;
|
||||
|
||||
if (description != null) {
|
||||
var exifInfo = await _exifInfoRepository.get(localExifId);
|
||||
|
||||
if (exifInfo != null) {
|
||||
exifInfo.description = description;
|
||||
await _exifInfoRepository.update(exifInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> getDescription(Asset asset) async {
|
||||
final localExifId = asset.exifInfo?.id;
|
||||
|
||||
// Guard [remoteAssetId] and [localExifId] null
|
||||
if (localExifId == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
final exifInfo = await _exifInfoRepository.get(localExifId);
|
||||
|
||||
return exifInfo?.description ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
66
mobile/lib/services/asset_description.service.dart
Normal file
66
mobile/lib/services/asset_description.service.dart
Normal file
@@ -0,0 +1,66 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/providers/db.provider.dart';
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class AssetDescriptionService {
|
||||
AssetDescriptionService(this._db, this._api);
|
||||
|
||||
final Isar _db;
|
||||
final ApiService _api;
|
||||
|
||||
Future<void> setDescription(
|
||||
Asset asset,
|
||||
String newDescription,
|
||||
) async {
|
||||
final remoteAssetId = asset.remoteId;
|
||||
final localExifId = asset.exifInfo?.id;
|
||||
|
||||
// Guard [remoteAssetId] and [localExifId] null
|
||||
if (remoteAssetId == null || localExifId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final result = await _api.assetsApi.updateAsset(
|
||||
remoteAssetId,
|
||||
UpdateAssetDto(description: newDescription),
|
||||
);
|
||||
|
||||
final description = result?.exifInfo?.description;
|
||||
|
||||
if (description != null) {
|
||||
var exifInfo = await _db.exifInfos.get(localExifId);
|
||||
|
||||
if (exifInfo != null) {
|
||||
exifInfo.description = description;
|
||||
await _db.writeTxn(
|
||||
() => _db.exifInfos.put(exifInfo),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String getAssetDescription(Asset asset) {
|
||||
final localExifId = asset.exifInfo?.id;
|
||||
|
||||
// Guard [remoteAssetId] and [localExifId] null
|
||||
if (localExifId == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
final exifInfo = _db.exifInfos.getSync(localExifId);
|
||||
|
||||
return exifInfo?.description ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
final assetDescriptionServiceProvider = Provider(
|
||||
(ref) => AssetDescriptionService(
|
||||
ref.watch(dbProvider),
|
||||
ref.watch(apiServiceProvider),
|
||||
),
|
||||
);
|
||||
@@ -18,9 +18,7 @@ import 'package:immich_mobile/repositories/asset.repository.dart';
|
||||
import 'package:immich_mobile/repositories/backup.repository.dart';
|
||||
import 'package:immich_mobile/repositories/album_media.repository.dart';
|
||||
import 'package:immich_mobile/repositories/file_media.repository.dart';
|
||||
import 'package:immich_mobile/repositories/partner_api.repository.dart';
|
||||
import 'package:immich_mobile/repositories/user.repository.dart';
|
||||
import 'package:immich_mobile/repositories/user_api.repository.dart';
|
||||
import 'package:immich_mobile/services/album.service.dart';
|
||||
import 'package:immich_mobile/services/entity.service.dart';
|
||||
import 'package:immich_mobile/services/hash.service.dart';
|
||||
@@ -32,6 +30,7 @@ import 'package:immich_mobile/services/backup.service.dart';
|
||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
import 'package:immich_mobile/services/partner.service.dart';
|
||||
import 'package:immich_mobile/services/sync.service.dart';
|
||||
import 'package:immich_mobile/services/user.service.dart';
|
||||
import 'package:immich_mobile/utils/backup_progress.dart';
|
||||
@@ -363,20 +362,16 @@ class BackgroundService {
|
||||
apiService.setAccessToken(Store.get(StoreKey.accessToken));
|
||||
AppSettingsService settingService = AppSettingsService();
|
||||
AppSettingsService settingsService = AppSettingsService();
|
||||
PartnerService partnerService = PartnerService(apiService, db);
|
||||
AlbumRepository albumRepository = AlbumRepository(db);
|
||||
AssetRepository assetRepository = AssetRepository(db);
|
||||
BackupRepository backupAlbumRepository = BackupRepository(db);
|
||||
AlbumMediaRepository albumMediaRepository = AlbumMediaRepository();
|
||||
FileMediaRepository fileMediaRepository = FileMediaRepository();
|
||||
UserRepository userRepository = UserRepository(db);
|
||||
UserApiRepository userApiRepository =
|
||||
UserApiRepository(apiService.usersApi);
|
||||
AlbumApiRepository albumApiRepository =
|
||||
AlbumApiRepository(apiService.albumsApi);
|
||||
PartnerApiRepository partnerApiRepository =
|
||||
PartnerApiRepository(apiService.partnersApi);
|
||||
HashService hashService =
|
||||
HashService(assetRepository, this, albumMediaRepository);
|
||||
HashService hashService = HashService(db, this, albumMediaRepository);
|
||||
EntityService entityService =
|
||||
EntityService(assetRepository, userRepository);
|
||||
SyncService syncSerive = SyncService(
|
||||
@@ -386,12 +381,8 @@ class BackgroundService {
|
||||
albumMediaRepository,
|
||||
albumApiRepository,
|
||||
);
|
||||
UserService userService = UserService(
|
||||
partnerApiRepository,
|
||||
userApiRepository,
|
||||
userRepository,
|
||||
syncSerive,
|
||||
);
|
||||
UserService userService =
|
||||
UserService(apiService, db, syncSerive, partnerService);
|
||||
AlbumService albumService = AlbumService(
|
||||
userService,
|
||||
syncSerive,
|
||||
|
||||
@@ -8,46 +8,41 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/exif_info.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/file_media.interface.dart';
|
||||
import 'package:immich_mobile/repositories/asset.repository.dart';
|
||||
import 'package:immich_mobile/repositories/exif_info.repository.dart';
|
||||
import 'package:immich_mobile/providers/db.provider.dart';
|
||||
import 'package:immich_mobile/repositories/file_media.repository.dart';
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
import 'package:immich_mobile/utils/diff.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
/// Finds duplicates originating from missing EXIF information
|
||||
class BackupVerificationService {
|
||||
final Isar _db;
|
||||
final IFileMediaRepository _fileMediaRepository;
|
||||
final IAssetRepository _assetRepository;
|
||||
final IExifInfoRepository _exifInfoRepository;
|
||||
|
||||
BackupVerificationService(
|
||||
this._fileMediaRepository,
|
||||
this._assetRepository,
|
||||
this._exifInfoRepository,
|
||||
);
|
||||
BackupVerificationService(this._db, this._fileMediaRepository);
|
||||
|
||||
/// Returns at most [limit] assets that were backed up without exif
|
||||
Future<List<Asset>> findWronglyBackedUpAssets({int limit = 100}) async {
|
||||
final owner = Store.get(StoreKey.currentUser).isarId;
|
||||
final List<Asset> onlyLocal = await _assetRepository.getAll(
|
||||
ownerId: owner,
|
||||
remote: false,
|
||||
limit: limit,
|
||||
final List<Asset> onlyLocal = await _db.assets
|
||||
.where()
|
||||
.remoteIdIsNull()
|
||||
.filter()
|
||||
.ownerIdEqualTo(owner)
|
||||
.localIdIsNotNull()
|
||||
.findAll();
|
||||
final List<Asset> remoteMatches = await _getMatches(
|
||||
_db.assets.where().localIdIsNull().filter().remoteIdIsNotNull(),
|
||||
owner,
|
||||
onlyLocal,
|
||||
limit,
|
||||
);
|
||||
final List<Asset> remoteMatches = await _assetRepository.getMatches(
|
||||
assets: onlyLocal,
|
||||
ownerId: owner,
|
||||
remote: true,
|
||||
limit: limit,
|
||||
);
|
||||
final List<Asset> localMatches = await _assetRepository.getMatches(
|
||||
assets: remoteMatches,
|
||||
ownerId: owner,
|
||||
remote: false,
|
||||
limit: limit,
|
||||
final List<Asset> localMatches = await _getMatches(
|
||||
_db.assets.where().remoteIdIsNull().filter().localIdIsNotNull(),
|
||||
owner,
|
||||
remoteMatches,
|
||||
limit,
|
||||
);
|
||||
|
||||
final List<Asset> deleteCandidates = [], originals = [];
|
||||
@@ -57,7 +52,7 @@ class BackupVerificationService {
|
||||
localMatches,
|
||||
compare: (a, b) => a.fileName.compareTo(b.fileName),
|
||||
both: (a, b) async {
|
||||
a.exifInfo = await _exifInfoRepository.get(a.id);
|
||||
a.exifInfo = await _db.exifInfos.get(a.id);
|
||||
deleteCandidates.add(a);
|
||||
originals.add(b);
|
||||
return false;
|
||||
@@ -197,6 +192,35 @@ class BackupVerificationService {
|
||||
return bytes.buffer.asUint64List(start);
|
||||
}
|
||||
|
||||
static Future<List<Asset>> _getMatches(
|
||||
QueryBuilder<Asset, Asset, QAfterFilterCondition> query,
|
||||
int ownerId,
|
||||
List<Asset> assets,
|
||||
int limit,
|
||||
) =>
|
||||
query
|
||||
.ownerIdEqualTo(ownerId)
|
||||
.anyOf(
|
||||
assets,
|
||||
(q, Asset a) => q
|
||||
.fileNameEqualTo(a.fileName)
|
||||
.and()
|
||||
.durationInSecondsEqualTo(a.durationInSeconds)
|
||||
.and()
|
||||
.fileCreatedAtBetween(
|
||||
a.fileCreatedAt.subtract(const Duration(hours: 12)),
|
||||
a.fileCreatedAt.add(const Duration(hours: 12)),
|
||||
)
|
||||
.and()
|
||||
.not()
|
||||
.checksumEqualTo(a.checksum),
|
||||
)
|
||||
.sortByFileName()
|
||||
.thenByFileCreatedAt()
|
||||
.thenByFileModifiedAt()
|
||||
.limit(limit)
|
||||
.findAll();
|
||||
|
||||
static bool _sameExceptTimeZone(DateTime a, DateTime b) {
|
||||
final ms = a.isAfter(b)
|
||||
? a.millisecondsSinceEpoch - b.millisecondsSinceEpoch
|
||||
@@ -209,8 +233,7 @@ class BackupVerificationService {
|
||||
|
||||
final backupVerificationServiceProvider = Provider(
|
||||
(ref) => BackupVerificationService(
|
||||
ref.watch(dbProvider),
|
||||
ref.watch(fileMediaRepositoryProvider),
|
||||
ref.watch(assetRepositoryProvider),
|
||||
ref.watch(exifInfoRepositoryProvider),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -4,24 +4,20 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/entities/album.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/album_media.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/repositories/album_media.repository.dart';
|
||||
import 'package:immich_mobile/repositories/asset.repository.dart';
|
||||
import 'package:immich_mobile/services/background.service.dart';
|
||||
import 'package:immich_mobile/entities/android_device_asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/device_asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/ios_device_asset.entity.dart';
|
||||
import 'package:immich_mobile/providers/db.provider.dart';
|
||||
import 'package:immich_mobile/extensions/string_extensions.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
class HashService {
|
||||
HashService(
|
||||
this._assetRepository,
|
||||
this._backgroundService,
|
||||
this._albumMediaRepository,
|
||||
);
|
||||
final IAssetRepository _assetRepository;
|
||||
HashService(this._db, this._backgroundService, this._albumMediaRepository);
|
||||
final Isar _db;
|
||||
final BackgroundService _backgroundService;
|
||||
final IAlbumMediaRepository _albumMediaRepository;
|
||||
final _log = Logger('HashService');
|
||||
@@ -59,8 +55,7 @@ class HashService {
|
||||
final ids = assets
|
||||
.map(Platform.isAndroid ? (a) => a.localId!.toInt() : (a) => a.localId!)
|
||||
.toList();
|
||||
final List<DeviceAsset?> hashes =
|
||||
await _assetRepository.getDeviceAssetsById(ids);
|
||||
final List<DeviceAsset?> hashes = await _lookupHashes(ids);
|
||||
final List<DeviceAsset> toAdd = [];
|
||||
final List<String> toHash = [];
|
||||
|
||||
@@ -111,6 +106,12 @@ class HashService {
|
||||
return _getHashedAssets(assets, hashes);
|
||||
}
|
||||
|
||||
/// Lookup hashes of assets by their local ID
|
||||
Future<List<DeviceAsset?>> _lookupHashes(List<Object> ids) =>
|
||||
Platform.isAndroid
|
||||
? _db.androidDeviceAssets.getAll(ids.cast())
|
||||
: _db.iOSDeviceAssets.getAllById(ids.cast());
|
||||
|
||||
/// Processes a batch of files and saves any successfully hashed
|
||||
/// values to the DB table.
|
||||
Future<void> _processBatch(
|
||||
@@ -130,7 +131,11 @@ class HashService {
|
||||
final validHashes = anyNull
|
||||
? toAdd.where((e) => e.hash.length == 20).toList(growable: false)
|
||||
: toAdd;
|
||||
await _assetRepository.upsertDeviceAssets(validHashes);
|
||||
await _db.writeTxn(
|
||||
() => Platform.isAndroid
|
||||
? _db.androidDeviceAssets.putAll(validHashes.cast())
|
||||
: _db.iOSDeviceAssets.putAll(validHashes.cast()),
|
||||
);
|
||||
_log.fine("Hashed ${validHashes.length}/${toHash.length} assets");
|
||||
}
|
||||
|
||||
@@ -163,7 +168,7 @@ class HashService {
|
||||
|
||||
final hashServiceProvider = Provider(
|
||||
(ref) => HashService(
|
||||
ref.watch(assetRepositoryProvider),
|
||||
ref.watch(dbProvider),
|
||||
ref.watch(backgroundServiceProvider),
|
||||
ref.watch(albumMediaRepositoryProvider),
|
||||
),
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/models/memories/memory.model.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/repositories/asset.repository.dart';
|
||||
import 'package:immich_mobile/providers/db.provider.dart';
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
final memoryServiceProvider = StateProvider<MemoryService>((ref) {
|
||||
return MemoryService(
|
||||
ref.watch(apiServiceProvider),
|
||||
ref.watch(assetRepositoryProvider),
|
||||
ref.watch(dbProvider),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -19,9 +20,9 @@ class MemoryService {
|
||||
final log = Logger("MemoryService");
|
||||
|
||||
final ApiService _apiService;
|
||||
final IAssetRepository _assetRepository;
|
||||
final Isar _db;
|
||||
|
||||
MemoryService(this._apiService, this._assetRepository);
|
||||
MemoryService(this._apiService, this._db);
|
||||
|
||||
Future<List<Memory>?> getMemoryLane() async {
|
||||
try {
|
||||
@@ -38,7 +39,7 @@ class MemoryService {
|
||||
List<Memory> memories = [];
|
||||
for (final MemoryLaneResponseDto(:yearsAgo, :assets) in data) {
|
||||
final dbAssets =
|
||||
await _assetRepository.getAllByRemoteId(assets.map((e) => e.id));
|
||||
await _db.assets.getAllByRemoteId(assets.map((e) => e.id));
|
||||
if (dbAssets.isNotEmpty) {
|
||||
final String title = yearsAgo <= 1
|
||||
? 'memories_year_ago'.tr()
|
||||
|
||||
@@ -1,33 +1,43 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/partner_api.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/repositories/partner_api.repository.dart';
|
||||
import 'package:immich_mobile/repositories/user.repository.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/providers/db.provider.dart';
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
final partnerServiceProvider = Provider(
|
||||
(ref) => PartnerService(
|
||||
ref.watch(partnerApiRepositoryProvider),
|
||||
ref.watch(userRepositoryProvider),
|
||||
ref.watch(apiServiceProvider),
|
||||
ref.watch(dbProvider),
|
||||
),
|
||||
);
|
||||
|
||||
class PartnerService {
|
||||
final IPartnerApiRepository _partnerApiRepository;
|
||||
final IUserRepository _userRepository;
|
||||
final ApiService _apiService;
|
||||
final Isar _db;
|
||||
final Logger _log = Logger("PartnerService");
|
||||
|
||||
PartnerService(
|
||||
this._partnerApiRepository,
|
||||
this._userRepository,
|
||||
);
|
||||
PartnerService(this._apiService, this._db);
|
||||
|
||||
Future<List<User>?> getPartners(PartnerDirection direction) async {
|
||||
try {
|
||||
final userDtos = await _apiService.partnersApi.getPartners(direction);
|
||||
if (userDtos != null) {
|
||||
return userDtos.map((u) => User.fromPartnerDto(u)).toList();
|
||||
}
|
||||
} catch (e) {
|
||||
_log.warning("Failed to get partners for direction $direction", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<bool> removePartner(User partner) async {
|
||||
try {
|
||||
await _partnerApiRepository.delete(partner.id);
|
||||
await _apiService.partnersApi.removePartner(partner.id);
|
||||
partner.isPartnerSharedBy = false;
|
||||
await _userRepository.update(partner);
|
||||
await _db.writeTxn(() => _db.users.put(partner));
|
||||
} catch (e) {
|
||||
_log.warning("Failed to remove partner ${partner.id}", e);
|
||||
return false;
|
||||
@@ -37,10 +47,12 @@ class PartnerService {
|
||||
|
||||
Future<bool> addPartner(User partner) async {
|
||||
try {
|
||||
await _partnerApiRepository.create(partner.id);
|
||||
partner.isPartnerSharedBy = true;
|
||||
await _userRepository.update(partner);
|
||||
return true;
|
||||
final dto = await _apiService.partnersApi.createPartner(partner.id);
|
||||
if (dto != null) {
|
||||
partner.isPartnerSharedBy = true;
|
||||
await _db.writeTxn(() => _db.users.put(partner));
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
_log.warning("Failed to add partner ${partner.id}", e);
|
||||
}
|
||||
@@ -49,13 +61,13 @@ class PartnerService {
|
||||
|
||||
Future<bool> updatePartner(User partner, {required bool inTimeline}) async {
|
||||
try {
|
||||
final dto = await _partnerApiRepository.update(
|
||||
partner.id,
|
||||
inTimeline: inTimeline,
|
||||
);
|
||||
partner.inTimeline = dto.inTimeline;
|
||||
await _userRepository.update(partner);
|
||||
return true;
|
||||
final dto = await _apiService.partnersApi
|
||||
.updatePartner(partner.id, UpdatePartnerDto(inTimeline: inTimeline));
|
||||
if (dto != null) {
|
||||
partner.inTimeline = dto.inTimeline ?? partner.inTimeline;
|
||||
await _db.writeTxn(() => _db.users.put(partner));
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
_log.warning("Failed to update partner ${partner.id}", e);
|
||||
}
|
||||
|
||||
@@ -1,37 +1,29 @@
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/asset_api.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/person_api.interface.dart';
|
||||
import 'package:immich_mobile/repositories/asset.repository.dart';
|
||||
import 'package:immich_mobile/repositories/asset_api.repository.dart';
|
||||
import 'package:immich_mobile/repositories/person_api.repository.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/providers/db.provider.dart';
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'person.service.g.dart';
|
||||
|
||||
@riverpod
|
||||
PersonService personService(PersonServiceRef ref) => PersonService(
|
||||
ref.watch(personApiRepositoryProvider),
|
||||
ref.watch(assetApiRepositoryProvider),
|
||||
ref.read(assetRepositoryProvider),
|
||||
);
|
||||
PersonService personService(PersonServiceRef ref) =>
|
||||
PersonService(ref.read(apiServiceProvider), ref.read(dbProvider));
|
||||
|
||||
class PersonService {
|
||||
final Logger _log = Logger("PersonService");
|
||||
final IPersonApiRepository _personApiRepository;
|
||||
final IAssetApiRepository _assetApiRepository;
|
||||
final IAssetRepository _assetRepository;
|
||||
final ApiService _apiService;
|
||||
final Isar _db;
|
||||
|
||||
PersonService(
|
||||
this._personApiRepository,
|
||||
this._assetApiRepository,
|
||||
this._assetRepository,
|
||||
);
|
||||
PersonService(this._apiService, this._db);
|
||||
|
||||
Future<List<Person>> getAllPeople() async {
|
||||
Future<List<PersonResponseDto>> getAllPeople() async {
|
||||
try {
|
||||
return await _personApiRepository.getAll();
|
||||
final peopleResponseDto = await _apiService.peopleApi.getAllPeople();
|
||||
return peopleResponseDto?.people ?? [];
|
||||
} catch (error, stack) {
|
||||
_log.severe("Error while fetching curated people", error, stack);
|
||||
return [];
|
||||
@@ -39,19 +31,50 @@ class PersonService {
|
||||
}
|
||||
|
||||
Future<List<Asset>> getPersonAssets(String id) async {
|
||||
List<Asset> result = [];
|
||||
var hasNext = true;
|
||||
var currentPage = 1;
|
||||
|
||||
try {
|
||||
final assets = await _assetApiRepository.search(personIds: [id]);
|
||||
return await _assetRepository
|
||||
.getAllByRemoteId(assets.map((a) => a.remoteId!));
|
||||
while (hasNext) {
|
||||
final response = await _apiService.searchApi.searchMetadata(
|
||||
MetadataSearchDto(
|
||||
personIds: [id],
|
||||
page: currentPage,
|
||||
size: 1000,
|
||||
),
|
||||
);
|
||||
|
||||
if (response == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (response.assets.nextPage == null) {
|
||||
hasNext = false;
|
||||
}
|
||||
|
||||
final assets = response.assets.items;
|
||||
final mapAssets =
|
||||
await _db.assets.getAllByRemoteId(assets.map((e) => e.id));
|
||||
result.addAll(mapAssets);
|
||||
|
||||
currentPage++;
|
||||
}
|
||||
} catch (error, stack) {
|
||||
_log.severe("Error while fetching person assets", error, stack);
|
||||
}
|
||||
return [];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<Person?> updateName(String id, String name) async {
|
||||
Future<PersonResponseDto?> updateName(String id, String name) async {
|
||||
try {
|
||||
return await _personApiRepository.update(id, name: name);
|
||||
return await _apiService.peopleApi.updatePerson(
|
||||
id,
|
||||
PersonUpdateDto(
|
||||
name: name,
|
||||
),
|
||||
);
|
||||
} catch (error, stack) {
|
||||
_log.severe("Error while updating person name", error, stack);
|
||||
}
|
||||
|
||||
2
mobile/lib/services/person.service.g.dart
generated
2
mobile/lib/services/person.service.g.dart
generated
@@ -6,7 +6,7 @@ part of 'person.service.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$personServiceHash() => r'32f28cb5a3de0553c17447e33a0efde7409a43ed';
|
||||
String _$personServiceHash() => r'54e6df4b8eea744f6de009f8315c9fe6230f6798';
|
||||
|
||||
/// See also [personService].
|
||||
@ProviderFor(personService)
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/models/search/search_filter.model.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/repositories/asset.repository.dart';
|
||||
import 'package:immich_mobile/providers/db.provider.dart';
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
final searchServiceProvider = Provider(
|
||||
(ref) => SearchService(
|
||||
ref.watch(apiServiceProvider),
|
||||
ref.watch(assetRepositoryProvider),
|
||||
ref.watch(dbProvider),
|
||||
),
|
||||
);
|
||||
|
||||
class SearchService {
|
||||
final ApiService _apiService;
|
||||
final IAssetRepository _assetRepository;
|
||||
final Isar _db;
|
||||
|
||||
final _log = Logger("SearchService");
|
||||
SearchService(this._apiService, this._assetRepository);
|
||||
SearchService(this._apiService, this._db);
|
||||
|
||||
Future<List<String>?> getSearchSuggestions(
|
||||
SearchSuggestionType type, {
|
||||
@@ -103,7 +103,7 @@ class SearchService {
|
||||
return null;
|
||||
}
|
||||
|
||||
return _assetRepository
|
||||
return _db.assets
|
||||
.getAllByRemoteId(response.assets.items.map((e) => e.id));
|
||||
} catch (error, stackTrace) {
|
||||
_log.severe("Failed to search for assets", error, stackTrace);
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/repositories/asset.repository.dart';
|
||||
import 'package:immich_mobile/providers/db.provider.dart';
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class StackService {
|
||||
StackService(this._api, this._assetRepository);
|
||||
StackService(this._api, this._db);
|
||||
|
||||
final ApiService _api;
|
||||
final IAssetRepository _assetRepository;
|
||||
final Isar _db;
|
||||
|
||||
Future<StackResponseDto?> getStack(String stackId) async {
|
||||
try {
|
||||
@@ -61,7 +61,10 @@ class StackService {
|
||||
|
||||
removeAssets.add(asset);
|
||||
}
|
||||
await _assetRepository.updateAll(removeAssets);
|
||||
|
||||
_db.writeTxn(() async {
|
||||
await _db.assets.putAll(removeAssets);
|
||||
});
|
||||
} catch (error) {
|
||||
debugPrint("Error while deleting stack: $error");
|
||||
}
|
||||
@@ -71,6 +74,6 @@ class StackService {
|
||||
final stackServiceProvider = Provider(
|
||||
(ref) => StackService(
|
||||
ref.watch(apiServiceProvider),
|
||||
ref.watch(assetRepositoryProvider),
|
||||
ref.watch(dbProvider),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,48 +1,68 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:immich_mobile/interfaces/partner_api.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/user_api.interface.dart';
|
||||
import 'package:immich_mobile/repositories/partner_api.repository.dart';
|
||||
import 'package:immich_mobile/repositories/user.repository.dart';
|
||||
import 'package:immich_mobile/repositories/user_api.repository.dart';
|
||||
import 'package:immich_mobile/services/partner.service.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/providers/db.provider.dart';
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
import 'package:immich_mobile/services/sync.service.dart';
|
||||
import 'package:immich_mobile/utils/diff.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
final userServiceProvider = Provider(
|
||||
(ref) => UserService(
|
||||
ref.watch(partnerApiRepositoryProvider),
|
||||
ref.watch(userApiRepositoryProvider),
|
||||
ref.watch(userRepositoryProvider),
|
||||
ref.watch(apiServiceProvider),
|
||||
ref.watch(dbProvider),
|
||||
ref.watch(syncServiceProvider),
|
||||
ref.watch(partnerServiceProvider),
|
||||
),
|
||||
);
|
||||
|
||||
class UserService {
|
||||
final IPartnerApiRepository _partnerApiRepository;
|
||||
final IUserApiRepository _userApiRepository;
|
||||
final IUserRepository _userRepository;
|
||||
final ApiService _apiService;
|
||||
final Isar _db;
|
||||
final SyncService _syncService;
|
||||
final PartnerService _partnerService;
|
||||
final Logger _log = Logger("UserService");
|
||||
|
||||
UserService(
|
||||
this._partnerApiRepository,
|
||||
this._userApiRepository,
|
||||
this._userRepository,
|
||||
this._apiService,
|
||||
this._db,
|
||||
this._syncService,
|
||||
this._partnerService,
|
||||
);
|
||||
|
||||
Future<List<User>> getUsers({bool self = false}) =>
|
||||
_userRepository.getAll(self: self);
|
||||
|
||||
Future<({String profileImagePath})?> uploadProfileImage(XFile image) async {
|
||||
Future<List<User>?> _getAllUsers() async {
|
||||
try {
|
||||
return await _userApiRepository.createProfileImage(
|
||||
name: image.name,
|
||||
data: await image.readAsBytes(),
|
||||
final dto = await _apiService.usersApi.searchUsers();
|
||||
return dto?.map(User.fromSimpleUserDto).toList();
|
||||
} catch (e) {
|
||||
_log.warning("Failed get all users", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<User>> getUsersInDb({bool self = false}) async {
|
||||
if (self) {
|
||||
return _db.users.where().findAll();
|
||||
}
|
||||
final int userId = Store.get(StoreKey.currentUser).isarId;
|
||||
return _db.users.where().isarIdNotEqualTo(userId).findAll();
|
||||
}
|
||||
|
||||
Future<CreateProfileImageResponseDto?> uploadProfileImage(XFile image) async {
|
||||
try {
|
||||
return await _apiService.usersApi.createProfileImage(
|
||||
MultipartFile.fromBytes(
|
||||
'file',
|
||||
await image.readAsBytes(),
|
||||
filename: image.name,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
_log.warning("Failed to upload profile image", e);
|
||||
@@ -51,19 +71,13 @@ class UserService {
|
||||
}
|
||||
|
||||
Future<List<User>?> getUsersFromServer() async {
|
||||
List<User>? users;
|
||||
try {
|
||||
users = await _userApiRepository.getAll();
|
||||
} catch (e) {
|
||||
_log.warning("Failed to fetch users", e);
|
||||
users = null;
|
||||
}
|
||||
final List<User> sharedBy =
|
||||
await _partnerApiRepository.getAll(Direction.sharedByMe);
|
||||
final List<User> sharedWith =
|
||||
await _partnerApiRepository.getAll(Direction.sharedWithMe);
|
||||
final List<User>? users = await _getAllUsers();
|
||||
final List<User>? sharedBy =
|
||||
await _partnerService.getPartners(PartnerDirection.by);
|
||||
final List<User>? sharedWith =
|
||||
await _partnerService.getPartners(PartnerDirection.with_);
|
||||
|
||||
if (users == null) {
|
||||
if (users == null || sharedBy == null || sharedWith == null) {
|
||||
_log.warning("Failed to refresh users");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:immich_mobile/constants/constants.dart';
|
||||
import 'package:immich_mobile/entities/album.entity.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
String getThumbnailUrl(
|
||||
@@ -61,7 +61,7 @@ String getOriginalUrlForRemoteId(final String id) {
|
||||
|
||||
String getImageCacheKey(final Asset asset) {
|
||||
// Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id
|
||||
final isFromDto = asset.id == noDbId;
|
||||
final isFromDto = asset.id == Isar.autoIncrement;
|
||||
return '${isFromDto ? asset.remoteId : asset.id}_fullStage';
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/constants.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||
import 'package:immich_mobile/widgets/common/immich_thumbnail.dart';
|
||||
import 'package:immich_mobile/utils/storage_indicator.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
class ThumbnailImage extends ConsumerWidget {
|
||||
/// The asset to show the thumbnail image for
|
||||
@@ -46,7 +46,7 @@ class ThumbnailImage extends ConsumerWidget {
|
||||
? context.primaryColor.darken(amount: 0.6)
|
||||
: context.primaryColor.lighten(amount: 0.8);
|
||||
// Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id
|
||||
final isFromDto = asset.id == noDbId;
|
||||
final isFromDto = asset.id == Isar.autoIncrement;
|
||||
|
||||
Widget buildSelectionIcon(Asset asset) {
|
||||
if (isSelected) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||
import 'package:immich_mobile/providers/asset.provider.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
import 'package:immich_mobile/services/asset.service.dart';
|
||||
import 'package:immich_mobile/services/asset_description.service.dart';
|
||||
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
@@ -29,16 +29,14 @@ class DescriptionInput extends HookConsumerWidget {
|
||||
final focusNode = useFocusNode();
|
||||
final isFocus = useState(false);
|
||||
final isTextEmpty = useState(controller.text.isEmpty);
|
||||
final assetService = ref.watch(assetServiceProvider);
|
||||
final descriptionProvider = ref.watch(assetDescriptionServiceProvider);
|
||||
final owner = ref.watch(currentUserProvider);
|
||||
final hasError = useState(false);
|
||||
final assetWithExif = ref.watch(assetDetailProvider(asset));
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
assetService
|
||||
.getDescription(asset)
|
||||
.then((value) => controller.text = value);
|
||||
controller.text = descriptionProvider.getAssetDescription(asset);
|
||||
return null;
|
||||
},
|
||||
[assetWithExif.value],
|
||||
@@ -47,7 +45,7 @@ class DescriptionInput extends HookConsumerWidget {
|
||||
submitDescription(String description) async {
|
||||
hasError.value = false;
|
||||
try {
|
||||
await assetService.setDescription(
|
||||
await descriptionProvider.setDescription(
|
||||
asset,
|
||||
description,
|
||||
);
|
||||
|
||||
@@ -3,23 +3,23 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/interfaces/person_api.interface.dart';
|
||||
import 'package:immich_mobile/providers/search/people.provider.dart';
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
import 'package:immich_mobile/utils/image_url_builder.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class PeoplePicker extends HookConsumerWidget {
|
||||
const PeoplePicker({super.key, required this.onSelect, this.filter});
|
||||
|
||||
final Function(Set<Person>) onSelect;
|
||||
final Set<Person>? filter;
|
||||
final Function(Set<PersonResponseDto>) onSelect;
|
||||
final Set<PersonResponseDto>? filter;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
var imageSize = 45.0;
|
||||
final people = ref.watch(getAllPeopleProvider);
|
||||
final headers = ApiService.getRequestHeaders();
|
||||
final selectedPeople = useState<Set<Person>>(filter ?? {});
|
||||
final selectedPeople = useState<Set<PersonResponseDto>>(filter ?? {});
|
||||
|
||||
return people.widgetWhen(
|
||||
onData: (people) {
|
||||
|
||||
10
mobile/openapi/README.md
generated
10
mobile/openapi/README.md
generated
@@ -116,6 +116,7 @@ Class | Method | HTTP request | Description
|
||||
*AuthenticationApi* | [**signUpAdmin**](doc//AuthenticationApi.md#signupadmin) | **POST** /auth/admin-sign-up |
|
||||
*AuthenticationApi* | [**validateAccessToken**](doc//AuthenticationApi.md#validateaccesstoken) | **POST** /auth/validateToken |
|
||||
*DeprecatedApi* | [**getPersonAssets**](doc//DeprecatedApi.md#getpersonassets) | **GET** /people/{id}/assets |
|
||||
*DeprecatedApi* | [**getRandom**](doc//DeprecatedApi.md#getrandom) | **GET** /assets/random |
|
||||
*DownloadApi* | [**downloadArchive**](doc//DownloadApi.md#downloadarchive) | **POST** /download/archive |
|
||||
*DownloadApi* | [**getDownloadInfo**](doc//DownloadApi.md#getdownloadinfo) | **POST** /download/info |
|
||||
*DuplicatesApi* | [**getAssetDuplicates**](doc//DuplicatesApi.md#getassetduplicates) | **GET** /duplicates |
|
||||
@@ -124,6 +125,7 @@ Class | Method | HTTP request | Description
|
||||
*FileReportsApi* | [**fixAuditFiles**](doc//FileReportsApi.md#fixauditfiles) | **POST** /reports/fix |
|
||||
*FileReportsApi* | [**getAuditFiles**](doc//FileReportsApi.md#getauditfiles) | **GET** /reports |
|
||||
*FileReportsApi* | [**getFileChecksums**](doc//FileReportsApi.md#getfilechecksums) | **POST** /reports/checksum |
|
||||
*JobsApi* | [**createJob**](doc//JobsApi.md#createjob) | **POST** /jobs |
|
||||
*JobsApi* | [**getAllJobsStatus**](doc//JobsApi.md#getalljobsstatus) | **GET** /jobs |
|
||||
*JobsApi* | [**sendJobCommand**](doc//JobsApi.md#sendjobcommand) | **PUT** /jobs/{id} |
|
||||
*LibrariesApi* | [**createLibrary**](doc//LibrariesApi.md#createlibrary) | **POST** /libraries |
|
||||
@@ -136,7 +138,6 @@ Class | Method | HTTP request | Description
|
||||
*LibrariesApi* | [**updateLibrary**](doc//LibrariesApi.md#updatelibrary) | **PUT** /libraries/{id} |
|
||||
*LibrariesApi* | [**validate**](doc//LibrariesApi.md#validate) | **POST** /libraries/{id}/validate |
|
||||
*MapApi* | [**getMapMarkers**](doc//MapApi.md#getmapmarkers) | **GET** /map/markers |
|
||||
*MapApi* | [**getMapStyle**](doc//MapApi.md#getmapstyle) | **GET** /map/style.json |
|
||||
*MapApi* | [**reverseGeocode**](doc//MapApi.md#reversegeocode) | **GET** /map/reverse-geocode |
|
||||
*MemoriesApi* | [**addMemoryAssets**](doc//MemoriesApi.md#addmemoryassets) | **PUT** /memories/{id}/assets |
|
||||
*MemoriesApi* | [**createMemory**](doc//MemoriesApi.md#creatememory) | **POST** /memories |
|
||||
@@ -171,6 +172,7 @@ Class | Method | HTTP request | Description
|
||||
*SearchApi* | [**searchMetadata**](doc//SearchApi.md#searchmetadata) | **POST** /search/metadata |
|
||||
*SearchApi* | [**searchPerson**](doc//SearchApi.md#searchperson) | **GET** /search/person |
|
||||
*SearchApi* | [**searchPlaces**](doc//SearchApi.md#searchplaces) | **GET** /search/places |
|
||||
*SearchApi* | [**searchRandom**](doc//SearchApi.md#searchrandom) | **POST** /search/random |
|
||||
*SearchApi* | [**searchSmart**](doc//SearchApi.md#searchsmart) | **POST** /search/smart |
|
||||
*ServerApi* | [**deleteServerLicense**](doc//ServerApi.md#deleteserverlicense) | **DELETE** /server/license |
|
||||
*ServerApi* | [**getAboutInfo**](doc//ServerApi.md#getaboutinfo) | **GET** /server/about |
|
||||
@@ -330,6 +332,7 @@ Class | Method | HTTP request | Description
|
||||
- [JobCommand](doc//JobCommand.md)
|
||||
- [JobCommandDto](doc//JobCommandDto.md)
|
||||
- [JobCountsDto](doc//JobCountsDto.md)
|
||||
- [JobCreateDto](doc//JobCreateDto.md)
|
||||
- [JobName](doc//JobName.md)
|
||||
- [JobSettingsDto](doc//JobSettingsDto.md)
|
||||
- [JobStatusDto](doc//JobStatusDto.md)
|
||||
@@ -337,14 +340,13 @@ Class | Method | HTTP request | Description
|
||||
- [LibraryStatsResponseDto](doc//LibraryStatsResponseDto.md)
|
||||
- [LicenseKeyDto](doc//LicenseKeyDto.md)
|
||||
- [LicenseResponseDto](doc//LicenseResponseDto.md)
|
||||
- [LoadTextualModelOnConnection](doc//LoadTextualModelOnConnection.md)
|
||||
- [LogLevel](doc//LogLevel.md)
|
||||
- [LoginCredentialDto](doc//LoginCredentialDto.md)
|
||||
- [LoginResponseDto](doc//LoginResponseDto.md)
|
||||
- [LogoutResponseDto](doc//LogoutResponseDto.md)
|
||||
- [ManualJobName](doc//ManualJobName.md)
|
||||
- [MapMarkerResponseDto](doc//MapMarkerResponseDto.md)
|
||||
- [MapReverseGeocodeResponseDto](doc//MapReverseGeocodeResponseDto.md)
|
||||
- [MapTheme](doc//MapTheme.md)
|
||||
- [MemoriesResponse](doc//MemoriesResponse.md)
|
||||
- [MemoriesUpdate](doc//MemoriesUpdate.md)
|
||||
- [MemoryCreateDto](doc//MemoryCreateDto.md)
|
||||
@@ -377,6 +379,7 @@ Class | Method | HTTP request | Description
|
||||
- [PurchaseResponse](doc//PurchaseResponse.md)
|
||||
- [PurchaseUpdate](doc//PurchaseUpdate.md)
|
||||
- [QueueStatusDto](doc//QueueStatusDto.md)
|
||||
- [RandomSearchDto](doc//RandomSearchDto.md)
|
||||
- [RatingsResponse](doc//RatingsResponse.md)
|
||||
- [RatingsUpdate](doc//RatingsUpdate.md)
|
||||
- [ReactionLevel](doc//ReactionLevel.md)
|
||||
@@ -450,6 +453,7 @@ Class | Method | HTTP request | Description
|
||||
- [ToneMapping](doc//ToneMapping.md)
|
||||
- [TranscodeHWAccel](doc//TranscodeHWAccel.md)
|
||||
- [TranscodePolicy](doc//TranscodePolicy.md)
|
||||
- [TrashResponseDto](doc//TrashResponseDto.md)
|
||||
- [UpdateAlbumDto](doc//UpdateAlbumDto.md)
|
||||
- [UpdateAlbumUserDto](doc//UpdateAlbumUserDto.md)
|
||||
- [UpdateAssetDto](doc//UpdateAssetDto.md)
|
||||
|
||||
6
mobile/openapi/lib/api.dart
generated
6
mobile/openapi/lib/api.dart
generated
@@ -144,6 +144,7 @@ part 'model/image_format.dart';
|
||||
part 'model/job_command.dart';
|
||||
part 'model/job_command_dto.dart';
|
||||
part 'model/job_counts_dto.dart';
|
||||
part 'model/job_create_dto.dart';
|
||||
part 'model/job_name.dart';
|
||||
part 'model/job_settings_dto.dart';
|
||||
part 'model/job_status_dto.dart';
|
||||
@@ -151,14 +152,13 @@ part 'model/library_response_dto.dart';
|
||||
part 'model/library_stats_response_dto.dart';
|
||||
part 'model/license_key_dto.dart';
|
||||
part 'model/license_response_dto.dart';
|
||||
part 'model/load_textual_model_on_connection.dart';
|
||||
part 'model/log_level.dart';
|
||||
part 'model/login_credential_dto.dart';
|
||||
part 'model/login_response_dto.dart';
|
||||
part 'model/logout_response_dto.dart';
|
||||
part 'model/manual_job_name.dart';
|
||||
part 'model/map_marker_response_dto.dart';
|
||||
part 'model/map_reverse_geocode_response_dto.dart';
|
||||
part 'model/map_theme.dart';
|
||||
part 'model/memories_response.dart';
|
||||
part 'model/memories_update.dart';
|
||||
part 'model/memory_create_dto.dart';
|
||||
@@ -191,6 +191,7 @@ part 'model/places_response_dto.dart';
|
||||
part 'model/purchase_response.dart';
|
||||
part 'model/purchase_update.dart';
|
||||
part 'model/queue_status_dto.dart';
|
||||
part 'model/random_search_dto.dart';
|
||||
part 'model/ratings_response.dart';
|
||||
part 'model/ratings_update.dart';
|
||||
part 'model/reaction_level.dart';
|
||||
@@ -264,6 +265,7 @@ part 'model/time_bucket_size.dart';
|
||||
part 'model/tone_mapping.dart';
|
||||
part 'model/transcode_hw_accel.dart';
|
||||
part 'model/transcode_policy.dart';
|
||||
part 'model/trash_response_dto.dart';
|
||||
part 'model/update_album_dto.dart';
|
||||
part 'model/update_album_user_dto.dart';
|
||||
part 'model/update_asset_dto.dart';
|
||||
|
||||
13
mobile/openapi/lib/api_client.dart
generated
13
mobile/openapi/lib/api_client.dart
generated
@@ -166,7 +166,6 @@ class ApiClient {
|
||||
|
||||
/// Returns a native instance of an OpenAPI class matching the [specified type][targetType].
|
||||
static dynamic fromJson(dynamic value, String targetType, {bool growable = false,}) {
|
||||
upgradeDto(value, targetType);
|
||||
try {
|
||||
switch (targetType) {
|
||||
case 'String':
|
||||
@@ -343,6 +342,8 @@ class ApiClient {
|
||||
return JobCommandDto.fromJson(value);
|
||||
case 'JobCountsDto':
|
||||
return JobCountsDto.fromJson(value);
|
||||
case 'JobCreateDto':
|
||||
return JobCreateDto.fromJson(value);
|
||||
case 'JobName':
|
||||
return JobNameTypeTransformer().decode(value);
|
||||
case 'JobSettingsDto':
|
||||
@@ -357,8 +358,6 @@ class ApiClient {
|
||||
return LicenseKeyDto.fromJson(value);
|
||||
case 'LicenseResponseDto':
|
||||
return LicenseResponseDto.fromJson(value);
|
||||
case 'LoadTextualModelOnConnection':
|
||||
return LoadTextualModelOnConnection.fromJson(value);
|
||||
case 'LogLevel':
|
||||
return LogLevelTypeTransformer().decode(value);
|
||||
case 'LoginCredentialDto':
|
||||
@@ -367,12 +366,12 @@ class ApiClient {
|
||||
return LoginResponseDto.fromJson(value);
|
||||
case 'LogoutResponseDto':
|
||||
return LogoutResponseDto.fromJson(value);
|
||||
case 'ManualJobName':
|
||||
return ManualJobNameTypeTransformer().decode(value);
|
||||
case 'MapMarkerResponseDto':
|
||||
return MapMarkerResponseDto.fromJson(value);
|
||||
case 'MapReverseGeocodeResponseDto':
|
||||
return MapReverseGeocodeResponseDto.fromJson(value);
|
||||
case 'MapTheme':
|
||||
return MapThemeTypeTransformer().decode(value);
|
||||
case 'MemoriesResponse':
|
||||
return MemoriesResponse.fromJson(value);
|
||||
case 'MemoriesUpdate':
|
||||
@@ -437,6 +436,8 @@ class ApiClient {
|
||||
return PurchaseUpdate.fromJson(value);
|
||||
case 'QueueStatusDto':
|
||||
return QueueStatusDto.fromJson(value);
|
||||
case 'RandomSearchDto':
|
||||
return RandomSearchDto.fromJson(value);
|
||||
case 'RatingsResponse':
|
||||
return RatingsResponse.fromJson(value);
|
||||
case 'RatingsUpdate':
|
||||
@@ -583,6 +584,8 @@ class ApiClient {
|
||||
return TranscodeHWAccelTypeTransformer().decode(value);
|
||||
case 'TranscodePolicy':
|
||||
return TranscodePolicyTypeTransformer().decode(value);
|
||||
case 'TrashResponseDto':
|
||||
return TrashResponseDto.fromJson(value);
|
||||
case 'UpdateAlbumDto':
|
||||
return UpdateAlbumDto.fromJson(value);
|
||||
case 'UpdateAlbumUserDto':
|
||||
|
||||
11
mobile/openapi/lib/model/clip_config.dart
generated
11
mobile/openapi/lib/model/clip_config.dart
generated
@@ -14,36 +14,30 @@ class CLIPConfig {
|
||||
/// Returns a new [CLIPConfig] instance.
|
||||
CLIPConfig({
|
||||
required this.enabled,
|
||||
required this.loadTextualModelOnConnection,
|
||||
required this.modelName,
|
||||
});
|
||||
|
||||
bool enabled;
|
||||
|
||||
LoadTextualModelOnConnection loadTextualModelOnConnection;
|
||||
|
||||
String modelName;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is CLIPConfig &&
|
||||
other.enabled == enabled &&
|
||||
other.loadTextualModelOnConnection == loadTextualModelOnConnection &&
|
||||
other.modelName == modelName;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(enabled.hashCode) +
|
||||
(loadTextualModelOnConnection.hashCode) +
|
||||
(modelName.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'CLIPConfig[enabled=$enabled, loadTextualModelOnConnection=$loadTextualModelOnConnection, modelName=$modelName]';
|
||||
String toString() => 'CLIPConfig[enabled=$enabled, modelName=$modelName]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'enabled'] = this.enabled;
|
||||
json[r'loadTextualModelOnConnection'] = this.loadTextualModelOnConnection;
|
||||
json[r'modelName'] = this.modelName;
|
||||
return json;
|
||||
}
|
||||
@@ -52,12 +46,12 @@ class CLIPConfig {
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static CLIPConfig? fromJson(dynamic value) {
|
||||
upgradeDto(value, "CLIPConfig");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return CLIPConfig(
|
||||
enabled: mapValueOfType<bool>(json, r'enabled')!,
|
||||
loadTextualModelOnConnection: LoadTextualModelOnConnection.fromJson(json[r'loadTextualModelOnConnection'])!,
|
||||
modelName: mapValueOfType<String>(json, r'modelName')!,
|
||||
);
|
||||
}
|
||||
@@ -107,7 +101,6 @@ class CLIPConfig {
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'enabled',
|
||||
'loadTextualModelOnConnection',
|
||||
'modelName',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,14 +22,14 @@ class FacialRecognitionConfig {
|
||||
|
||||
bool enabled;
|
||||
|
||||
/// Minimum value: 0.1
|
||||
/// Minimum value: 0
|
||||
/// Maximum value: 2
|
||||
double maxDistance;
|
||||
|
||||
/// Minimum value: 1
|
||||
int minFaces;
|
||||
|
||||
/// Minimum value: 0.1
|
||||
/// Minimum value: 0
|
||||
/// Maximum value: 1
|
||||
double minScore;
|
||||
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class LoadTextualModelOnConnection {
|
||||
/// Returns a new [LoadTextualModelOnConnection] instance.
|
||||
LoadTextualModelOnConnection({
|
||||
required this.enabled,
|
||||
required this.ttl,
|
||||
});
|
||||
|
||||
bool enabled;
|
||||
|
||||
/// Minimum value: 0
|
||||
num ttl;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is LoadTextualModelOnConnection &&
|
||||
other.enabled == enabled &&
|
||||
other.ttl == ttl;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(enabled.hashCode) +
|
||||
(ttl.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'LoadTextualModelOnConnection[enabled=$enabled, ttl=$ttl]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'enabled'] = this.enabled;
|
||||
json[r'ttl'] = this.ttl;
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [LoadTextualModelOnConnection] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static LoadTextualModelOnConnection? fromJson(dynamic value) {
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return LoadTextualModelOnConnection(
|
||||
enabled: mapValueOfType<bool>(json, r'enabled')!,
|
||||
ttl: num.parse('${json[r'ttl']}'),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<LoadTextualModelOnConnection> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <LoadTextualModelOnConnection>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = LoadTextualModelOnConnection.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, LoadTextualModelOnConnection> mapFromJson(dynamic json) {
|
||||
final map = <String, LoadTextualModelOnConnection>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = LoadTextualModelOnConnection.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of LoadTextualModelOnConnection-objects as value to a dart map
|
||||
static Map<String, List<LoadTextualModelOnConnection>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<LoadTextualModelOnConnection>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = LoadTextualModelOnConnection.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'enabled',
|
||||
'ttl',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/models/activities/activity.model.dart';
|
||||
import 'package:immich_mobile/providers/activity_service.provider.dart';
|
||||
import 'package:immich_mobile/providers/activity_statistics.provider.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
@@ -26,7 +25,7 @@ void main() {
|
||||
test('Returns the proper count family', () async {
|
||||
when(
|
||||
() => activityMock.getStatistics('test-album', assetId: 'test-asset'),
|
||||
).thenAnswer((_) async => const ActivityStats(comments: 5));
|
||||
).thenAnswer((_) async => 5);
|
||||
|
||||
// Read here to make the getStatistics call
|
||||
container.read(activityStatisticsProvider('test-album', 'test-asset'));
|
||||
@@ -51,7 +50,7 @@ void main() {
|
||||
test('Adds activity', () async {
|
||||
when(
|
||||
() => activityMock.getStatistics('test-album'),
|
||||
).thenAnswer((_) async => const ActivityStats(comments: 10));
|
||||
).thenAnswer((_) async => 10);
|
||||
|
||||
final provider = activityStatisticsProvider('test-album');
|
||||
container.listen(
|
||||
@@ -72,7 +71,7 @@ void main() {
|
||||
test('Removes activity', () async {
|
||||
when(
|
||||
() => activityMock.getStatistics('new-album', assetId: 'test-asset'),
|
||||
).thenAnswer((_) async => const ActivityStats(comments: 10));
|
||||
).thenAnswer((_) async => 10);
|
||||
|
||||
final provider = activityStatisticsProvider('new-album', 'test-asset');
|
||||
container.listen(
|
||||
|
||||
@@ -3167,6 +3167,123 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/map/style.json": {
|
||||
"get": {
|
||||
"operationId": "getMapStyle",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "key",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "theme",
|
||||
"required": true,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MapTheme"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Map"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/map/tiles.json": {
|
||||
"get": {
|
||||
"operationId": "getTilesJson",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Map"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/map/tiles/{z}/{x}/{y}.{format}": {
|
||||
"get": {
|
||||
"operationId": "getTiles",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "format",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "x",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "z",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Map"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/memories": {
|
||||
"get": {
|
||||
"operationId": "searchMemories",
|
||||
@@ -8656,16 +8773,12 @@
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"loadTextualModelOnConnection": {
|
||||
"$ref": "#/components/schemas/LoadTextualModelOnConnection"
|
||||
},
|
||||
"modelName": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"enabled",
|
||||
"loadTextualModelOnConnection",
|
||||
"modelName"
|
||||
],
|
||||
"type": "object"
|
||||
@@ -9123,7 +9236,7 @@
|
||||
"maxDistance": {
|
||||
"format": "double",
|
||||
"maximum": 2,
|
||||
"minimum": 0.1,
|
||||
"minimum": 0,
|
||||
"type": "number"
|
||||
},
|
||||
"minFaces": {
|
||||
@@ -9133,7 +9246,7 @@
|
||||
"minScore": {
|
||||
"format": "double",
|
||||
"maximum": 1,
|
||||
"minimum": 0.1,
|
||||
"minimum": 0,
|
||||
"type": "number"
|
||||
},
|
||||
"modelName": {
|
||||
@@ -9506,17 +9619,6 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"LoadTextualModelOnConnection": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"enabled"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"LogLevel": {
|
||||
"enum": [
|
||||
"verbose",
|
||||
@@ -9661,6 +9763,13 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"MapTheme": {
|
||||
"enum": [
|
||||
"light",
|
||||
"dark"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"MemoriesResponse": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
@@ -10876,12 +10985,6 @@
|
||||
"loginPageMessage": {
|
||||
"type": "string"
|
||||
},
|
||||
"mapDarkStyleUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"mapLightStyleUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"oauthButtonText": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -10897,8 +11000,6 @@
|
||||
"isInitialized",
|
||||
"isOnboarded",
|
||||
"loginPageMessage",
|
||||
"mapDarkStyleUrl",
|
||||
"mapLightStyleUrl",
|
||||
"oauthButtonText",
|
||||
"trashDays",
|
||||
"userDeleteDelay"
|
||||
|
||||
@@ -1142,13 +1142,8 @@ export type SystemConfigLoggingDto = {
|
||||
enabled: boolean;
|
||||
level: LogLevel;
|
||||
};
|
||||
export type LoadTextualModelOnConnection = {
|
||||
enabled: boolean;
|
||||
ttl: number;
|
||||
};
|
||||
export type ClipConfig = {
|
||||
enabled: boolean;
|
||||
loadTextualModelOnConnection: LoadTextualModelOnConnection;
|
||||
modelName: string;
|
||||
};
|
||||
export type DuplicateDetectionConfig = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# dev build
|
||||
FROM ghcr.io/immich-app/base-server-dev:20240924@sha256:fff4358d435065a626c64a4c015cbfce6ee714b05fabe39aa0d83d8cff3951f2 AS dev
|
||||
FROM ghcr.io/immich-app/base-server-dev:20240917@sha256:3d92952d37cd68f5bf641aa80e5cc034e0d11f3774147f5db8db93138cfa5b3b AS dev
|
||||
|
||||
RUN apt-get install --no-install-recommends -yqq tini
|
||||
WORKDIR /usr/src/app
|
||||
@@ -41,7 +41,7 @@ RUN npm run build
|
||||
|
||||
|
||||
# prod build
|
||||
FROM ghcr.io/immich-app/base-server-prod:20240924@sha256:af3089fe48d7ff162594bd7edfffa56ba4e7014ad10ad69c4ebfd428e39b06ff
|
||||
FROM ghcr.io/immich-app/base-server-prod:20240917@sha256:67a40250f03812fe1e6f6b6345a3c7b71b3a9f24c65ed4862e82be8b3e53d23a
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
ENV NODE_ENV=production \
|
||||
|
||||
655
server/package-lock.json
generated
655
server/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -78,6 +78,7 @@
|
||||
"openid-client": "^5.4.3",
|
||||
"pg": "^8.11.3",
|
||||
"picomatch": "^4.0.2",
|
||||
"pmtiles": "^3.0.7",
|
||||
"react": "^18.3.1",
|
||||
"react-email": "^3.0.0",
|
||||
"reflect-metadata": "^0.2.0",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"sources": {
|
||||
"protomaps": {
|
||||
"type": "vector",
|
||||
"url": "https://tiles.immich.cloud/v1.json"
|
||||
"url": "http://localhost:2283/api/map/tiles.json"
|
||||
}
|
||||
},
|
||||
"layers": [
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"sources": {
|
||||
"protomaps": {
|
||||
"type": "vector",
|
||||
"url": "https://tiles.immich.cloud/v1.json"
|
||||
"url": "http://localhost:2283/api/map/tiles.json"
|
||||
}
|
||||
},
|
||||
"layers": [
|
||||
|
||||
6367
server/resources/tiles.json
Normal file
6367
server/resources/tiles.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -120,9 +120,6 @@ export interface SystemConfig {
|
||||
clip: {
|
||||
enabled: boolean;
|
||||
modelName: string;
|
||||
loadTextualModelOnConnection: {
|
||||
enabled: boolean;
|
||||
};
|
||||
};
|
||||
duplicateDetection: {
|
||||
enabled: boolean;
|
||||
@@ -273,9 +270,6 @@ export const defaults = Object.freeze<SystemConfig>({
|
||||
clip: {
|
||||
enabled: true,
|
||||
modelName: 'ViT-B-32__openai',
|
||||
loadTextualModelOnConnection: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
duplicateDetection: {
|
||||
enabled: true,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Controller, Get, HttpCode, HttpStatus, Query } from '@nestjs/common';
|
||||
import { Controller, Get, HttpCode, HttpStatus, Query, Res } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { Response } from 'express';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import {
|
||||
MapMarkerDto,
|
||||
@@ -27,4 +28,12 @@ export class MapController {
|
||||
reverseGeocode(@Query() dto: MapReverseGeocodeDto): Promise<MapReverseGeocodeResponseDto[]> {
|
||||
return this.service.reverseGeocode(dto);
|
||||
}
|
||||
|
||||
@Authenticated()
|
||||
@Get('pmtiles')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async getPMTiles(@Res() res: Response) {
|
||||
const tileStream = await this.service.getLocalTilesStream();
|
||||
return tileStream.pipe(res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsNotEmpty, IsNumber, IsObject, IsString, Max, Min, ValidateNested } from 'class-validator';
|
||||
import { IsNotEmpty, IsNumber, IsString, Max, Min } from 'class-validator';
|
||||
import { ValidateBoolean } from 'src/validation';
|
||||
|
||||
export class TaskConfig {
|
||||
@@ -14,17 +14,7 @@ export class ModelConfig extends TaskConfig {
|
||||
modelName!: string;
|
||||
}
|
||||
|
||||
export class LoadTextualModelOnConnection {
|
||||
@ValidateBoolean()
|
||||
enabled!: boolean;
|
||||
}
|
||||
|
||||
export class CLIPConfig extends ModelConfig {
|
||||
@Type(() => LoadTextualModelOnConnection)
|
||||
@ValidateNested()
|
||||
@IsObject()
|
||||
loadTextualModelOnConnection!: LoadTextualModelOnConnection;
|
||||
}
|
||||
export class CLIPConfig extends ModelConfig {}
|
||||
|
||||
export class DuplicateDetectionConfig extends TaskConfig {
|
||||
@IsNumber()
|
||||
@@ -37,14 +27,14 @@ export class DuplicateDetectionConfig extends TaskConfig {
|
||||
|
||||
export class FacialRecognitionConfig extends ModelConfig {
|
||||
@IsNumber()
|
||||
@Min(0.1)
|
||||
@Min(0)
|
||||
@Max(1)
|
||||
@Type(() => Number)
|
||||
@ApiProperty({ type: 'number', format: 'double' })
|
||||
minScore!: number;
|
||||
|
||||
@IsNumber()
|
||||
@Min(0.1)
|
||||
@Min(0)
|
||||
@Max(2)
|
||||
@Type(() => Number)
|
||||
@ApiProperty({ type: 'number', format: 'double' })
|
||||
|
||||
@@ -46,11 +46,6 @@ export interface Face {
|
||||
score: number;
|
||||
}
|
||||
|
||||
export enum LoadTextModelActions {
|
||||
LOAD,
|
||||
UNLOAD,
|
||||
}
|
||||
|
||||
export type FacialRecognitionResponse = { [ModelTask.FACIAL_RECOGNITION]: Face[] } & VisualResponse;
|
||||
export type DetectedFaces = { faces: Face[] } & VisualResponse;
|
||||
export type MachineLearningRequest = ClipVisualRequest | ClipTextualRequest | FacialRecognitionRequest;
|
||||
@@ -59,5 +54,4 @@ export interface IMachineLearningRepository {
|
||||
encodeImage(url: string, imagePath: string, config: ModelOptions): Promise<number[]>;
|
||||
encodeText(url: string, text: string, config: ModelOptions): Promise<number[]>;
|
||||
detectFaces(url: string, imagePath: string, config: FaceDetectionOptions): Promise<DetectedFaces>;
|
||||
prepareTextModel(url: string, config: ModelOptions, action: LoadTextModelActions): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -483,13 +483,16 @@ UPDATE "albums"
|
||||
SET
|
||||
"albumThumbnailAssetId" = (
|
||||
SELECT
|
||||
"album_assets"."assetsId"
|
||||
"albums_assets2"."assetsId"
|
||||
FROM
|
||||
"albums_assets_assets" "album_assets"
|
||||
INNER JOIN "assets" "assets" ON "album_assets"."assetsId" = "assets"."id"
|
||||
AND "assets"."deletedAt" IS NULL
|
||||
"assets" "assets",
|
||||
"albums_assets_assets" "albums_assets2"
|
||||
WHERE
|
||||
"album_assets"."albumsId" = "albums"."id"
|
||||
(
|
||||
"albums_assets2"."assetsId" = "assets"."id"
|
||||
AND "albums_assets2"."albumsId" = "albums"."id"
|
||||
)
|
||||
AND ("assets"."deletedAt" IS NULL)
|
||||
ORDER BY
|
||||
"assets"."fileCreatedAt" DESC
|
||||
LIMIT
|
||||
@@ -502,21 +505,17 @@ WHERE
|
||||
SELECT
|
||||
1
|
||||
FROM
|
||||
"albums_assets_assets" "album_assets"
|
||||
INNER JOIN "assets" "assets" ON "album_assets"."assetsId" = "assets"."id"
|
||||
AND "assets"."deletedAt" IS NULL
|
||||
"albums_assets_assets" "albums_assets"
|
||||
WHERE
|
||||
"album_assets"."albumsId" = "albums"."id"
|
||||
"albums"."id" = "albums_assets"."albumsId"
|
||||
)
|
||||
OR "albums"."albumThumbnailAssetId" IS NOT NULL
|
||||
AND NOT EXISTS (
|
||||
SELECT
|
||||
1
|
||||
FROM
|
||||
"albums_assets_assets" "album_assets"
|
||||
INNER JOIN "assets" "assets" ON "album_assets"."assetsId" = "assets"."id"
|
||||
AND "assets"."deletedAt" IS NULL
|
||||
"albums_assets_assets" "albums_assets"
|
||||
WHERE
|
||||
"album_assets"."albumsId" = "albums"."id"
|
||||
AND "albums"."albumThumbnailAssetId" = "album_assets"."assetsId"
|
||||
"albums"."id" = "albums_assets"."albumsId"
|
||||
AND "albums"."albumThumbnailAssetId" = "albums_assets"."assetsId"
|
||||
)
|
||||
|
||||
@@ -277,26 +277,32 @@ export class AlbumRepository implements IAlbumRepository {
|
||||
@GenerateSql()
|
||||
async updateThumbnails(): Promise<number | undefined> {
|
||||
// Subquery for getting a new thumbnail.
|
||||
|
||||
const builder = this.dataSource
|
||||
.createQueryBuilder('albums_assets_assets', 'album_assets')
|
||||
.innerJoin('assets', 'assets', '"album_assets"."assetsId" = "assets"."id"')
|
||||
.where('"album_assets"."albumsId" = "albums"."id"');
|
||||
|
||||
const newThumbnail = builder
|
||||
.clone()
|
||||
.select('"album_assets"."assetsId"')
|
||||
.orderBy('"assets"."fileCreatedAt"', 'DESC')
|
||||
const newThumbnail = this.assetRepository
|
||||
.createQueryBuilder('assets')
|
||||
.select('albums_assets2.assetsId')
|
||||
.addFrom('albums_assets_assets', 'albums_assets2')
|
||||
.where('albums_assets2.assetsId = assets.id')
|
||||
.andWhere('albums_assets2.albumsId = "albums"."id"') // Reference to albums.id outside this query
|
||||
.orderBy('assets.fileCreatedAt', 'DESC')
|
||||
.limit(1);
|
||||
const hasAssets = builder.clone().select('1');
|
||||
const hasInvalidAsset = hasAssets.clone().andWhere('"albums"."albumThumbnailAssetId" = "album_assets"."assetsId"');
|
||||
|
||||
// Using dataSource, because there is no direct access to albums_assets_assets.
|
||||
const albumHasAssets = this.dataSource
|
||||
.createQueryBuilder()
|
||||
.select('1')
|
||||
.from('albums_assets_assets', 'albums_assets')
|
||||
.where('"albums"."id" = "albums_assets"."albumsId"');
|
||||
|
||||
const albumContainsThumbnail = albumHasAssets
|
||||
.clone()
|
||||
.andWhere('"albums"."albumThumbnailAssetId" = "albums_assets"."assetsId"');
|
||||
|
||||
const updateAlbums = this.repository
|
||||
.createQueryBuilder('albums')
|
||||
.update(AlbumEntity)
|
||||
.set({ albumThumbnailAssetId: () => `(${newThumbnail.getQuery()})` })
|
||||
.where(`"albums"."albumThumbnailAssetId" IS NULL AND EXISTS (${hasAssets.getQuery()})`)
|
||||
.orWhere(`"albums"."albumThumbnailAssetId" IS NOT NULL AND NOT EXISTS (${hasInvalidAsset.getQuery()})`);
|
||||
.where(`"albums"."albumThumbnailAssetId" IS NULL AND EXISTS (${albumHasAssets.getQuery()})`)
|
||||
.orWhere(`"albums"."albumThumbnailAssetId" IS NOT NULL AND NOT EXISTS (${albumContainsThumbnail.getQuery()})`);
|
||||
|
||||
const result = await updateAlbums.execute();
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
WebSocketServer,
|
||||
} from '@nestjs/websockets';
|
||||
import { Server, Socket } from 'socket.io';
|
||||
import { SystemConfigCore } from 'src/cores/system-config.core';
|
||||
import {
|
||||
ArgsOf,
|
||||
ClientEventMap,
|
||||
@@ -20,8 +19,6 @@ import {
|
||||
ServerEventMap,
|
||||
} from 'src/interfaces/event.interface';
|
||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||
import { IMachineLearningRepository, LoadTextModelActions } from 'src/interfaces/machine-learning.interface';
|
||||
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
|
||||
import { AuthService } from 'src/services/auth.service';
|
||||
import { Instrumentation } from 'src/utils/instrumentation';
|
||||
|
||||
@@ -36,7 +33,6 @@ type EmitHandlers = Partial<{ [T in EmitEvent]: EmitHandler<T>[] }>;
|
||||
@Injectable()
|
||||
export class EventRepository implements OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit, IEventRepository {
|
||||
private emitHandlers: EmitHandlers = {};
|
||||
private configCore: SystemConfigCore;
|
||||
|
||||
@WebSocketServer()
|
||||
private server?: Server;
|
||||
@@ -45,11 +41,8 @@ export class EventRepository implements OnGatewayConnection, OnGatewayDisconnect
|
||||
private moduleRef: ModuleRef,
|
||||
private eventEmitter: EventEmitter2,
|
||||
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
||||
@Inject(IMachineLearningRepository) private machineLearningRepository: IMachineLearningRepository,
|
||||
@Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository,
|
||||
) {
|
||||
this.logger.setContext(EventRepository.name);
|
||||
this.configCore = SystemConfigCore.create(systemMetadataRepository, this.logger);
|
||||
}
|
||||
|
||||
afterInit(server: Server) {
|
||||
@@ -75,21 +68,6 @@ export class EventRepository implements OnGatewayConnection, OnGatewayDisconnect
|
||||
queryParams: {},
|
||||
metadata: { adminRoute: false, sharedLinkRoute: false, uri: '/api/socket.io' },
|
||||
});
|
||||
if ('background' in client.handshake.query && client.handshake.query.background === 'false') {
|
||||
const { machineLearning } = await this.configCore.getConfig({ withCache: true });
|
||||
if (machineLearning.clip.loadTextualModelOnConnection.enabled) {
|
||||
try {
|
||||
console.log(this.server);
|
||||
this.machineLearningRepository.prepareTextModel(
|
||||
machineLearning.url,
|
||||
machineLearning.clip,
|
||||
LoadTextModelActions.LOAD,
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.warn(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
await client.join(auth.user.id);
|
||||
if (auth.session) {
|
||||
await client.join(auth.session.id);
|
||||
@@ -105,21 +83,6 @@ export class EventRepository implements OnGatewayConnection, OnGatewayDisconnect
|
||||
async handleDisconnect(client: Socket) {
|
||||
this.logger.log(`Websocket Disconnect: ${client.id}`);
|
||||
await client.leave(client.nsp.name);
|
||||
if ('background' in client.handshake.query && client.handshake.query.background === 'false') {
|
||||
const { machineLearning } = await this.configCore.getConfig({ withCache: true });
|
||||
if (machineLearning.clip.loadTextualModelOnConnection.enabled && this.server?.engine.clientsCount == 0) {
|
||||
try {
|
||||
this.machineLearningRepository.prepareTextModel(
|
||||
machineLearning.url,
|
||||
machineLearning.clip,
|
||||
LoadTextModelActions.UNLOAD,
|
||||
);
|
||||
this.logger.debug('sent request to unload text model');
|
||||
} catch (error) {
|
||||
this.logger.warn(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
on<T extends EmitEvent>(event: T, handler: EmitHandler<T>): void {
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
FaceDetectionOptions,
|
||||
FacialRecognitionResponse,
|
||||
IMachineLearningRepository,
|
||||
LoadTextModelActions,
|
||||
MachineLearningRequest,
|
||||
ModelPayload,
|
||||
ModelTask,
|
||||
@@ -21,9 +20,13 @@ const errorPrefix = 'Machine learning request';
|
||||
@Injectable()
|
||||
export class MachineLearningRepository implements IMachineLearningRepository {
|
||||
private async predict<T>(url: string, payload: ModelPayload, config: MachineLearningRequest): Promise<T> {
|
||||
const formData = await this.getFormData(config, payload);
|
||||
const formData = await this.getFormData(payload, config);
|
||||
|
||||
const res = await this.fetchData(url, '/predict', formData);
|
||||
const res = await fetch(new URL('/predict', url), { method: 'POST', body: formData }).catch(
|
||||
(error: Error | any) => {
|
||||
throw new Error(`${errorPrefix} to "${url}" failed with ${error?.cause || error}`);
|
||||
},
|
||||
);
|
||||
|
||||
if (res.status >= 400) {
|
||||
throw new Error(`${errorPrefix} '${JSON.stringify(config)}' failed with status ${res.status}: ${res.statusText}`);
|
||||
@@ -31,30 +34,6 @@ export class MachineLearningRepository implements IMachineLearningRepository {
|
||||
return res.json();
|
||||
}
|
||||
|
||||
private async fetchData(url: string, path: string, formData?: FormData): Promise<Response> {
|
||||
const res = await fetch(new URL(path, url), { method: 'POST', body: formData }).catch((error: Error | any) => {
|
||||
throw new Error(`${errorPrefix} to "${url}" failed with ${error?.cause || error}`);
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private prepareTextModelUrl: Record<LoadTextModelActions, string> = {
|
||||
[LoadTextModelActions.LOAD]: '/load',
|
||||
[LoadTextModelActions.UNLOAD]: '/unload',
|
||||
};
|
||||
|
||||
async prepareTextModel(url: string, { modelName }: CLIPConfig, actions: LoadTextModelActions) {
|
||||
try {
|
||||
const request = { [ModelTask.SEARCH]: { [ModelType.TEXTUAL]: { modelName } } };
|
||||
const formData = await this.getFormData(request);
|
||||
const res = await this.fetchData(url, this.prepareTextModelUrl[actions], formData);
|
||||
if (res.status >= 400) {
|
||||
throw new Error(`${errorPrefix} Loadings textual model failed with status ${res.status}: ${res.statusText}`);
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
async detectFaces(url: string, imagePath: string, { modelName, minScore }: FaceDetectionOptions) {
|
||||
const request = {
|
||||
[ModelTask.FACIAL_RECOGNITION]: {
|
||||
@@ -82,17 +61,16 @@ export class MachineLearningRepository implements IMachineLearningRepository {
|
||||
return response[ModelTask.SEARCH];
|
||||
}
|
||||
|
||||
private async getFormData(config: MachineLearningRequest, payload?: ModelPayload): Promise<FormData> {
|
||||
private async getFormData(payload: ModelPayload, config: MachineLearningRequest): Promise<FormData> {
|
||||
const formData = new FormData();
|
||||
formData.append('entries', JSON.stringify(config));
|
||||
if (payload) {
|
||||
if ('imagePath' in payload) {
|
||||
formData.append('image', new Blob([await readFile(payload.imagePath)]));
|
||||
} else if ('text' in payload) {
|
||||
formData.append('text', payload.text);
|
||||
} else {
|
||||
throw new Error('Invalid input');
|
||||
}
|
||||
|
||||
if ('imagePath' in payload) {
|
||||
formData.append('image', new Blob([await readFile(payload.imagePath)]));
|
||||
} else if ('text' in payload) {
|
||||
formData.append('text', payload.text);
|
||||
} else {
|
||||
throw new Error('Invalid input');
|
||||
}
|
||||
|
||||
return formData;
|
||||
|
||||
@@ -6,6 +6,7 @@ import { IAlbumRepository } from 'src/interfaces/album.interface';
|
||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||
import { IMapRepository } from 'src/interfaces/map.interface';
|
||||
import { IPartnerRepository } from 'src/interfaces/partner.interface';
|
||||
import { IStorageRepository } from 'src/interfaces/storage.interface';
|
||||
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
|
||||
import { getMyPartnerIds } from 'src/utils/asset.util';
|
||||
|
||||
@@ -18,6 +19,7 @@ export class MapService {
|
||||
@Inject(IPartnerRepository) private partnerRepository: IPartnerRepository,
|
||||
@Inject(IMapRepository) private mapRepository: IMapRepository,
|
||||
@Inject(ISystemMetadataRepository) private systemMetadataRepository: ISystemMetadataRepository,
|
||||
@Inject(IStorageRepository) private storageRepository: IStorageRepository,
|
||||
) {
|
||||
this.logger.setContext(MapService.name);
|
||||
this.configCore = SystemConfigCore.create(systemMetadataRepository, logger);
|
||||
@@ -43,6 +45,11 @@ export class MapService {
|
||||
return this.mapRepository.getMapMarkers(userIds, albumIds, options);
|
||||
}
|
||||
|
||||
async getLocalTilesStream() {
|
||||
const readStream = await this.storageRepository.createReadStream('/usr/src/app/uploads/tiles/tiles.pmtiles');
|
||||
return readStream.stream;
|
||||
}
|
||||
|
||||
async reverseGeocode(dto: MapReverseGeocodeDto) {
|
||||
const { lat: latitude, lon: longitude } = dto;
|
||||
// eventually this should probably return an array of results
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
This project uses the [SvelteKit](https://kit.svelte.dev/) web framework. Please refer to [the SvelteKit docs](https://kit.svelte.dev/docs) for information on getting started as a contributor to this project. In particular, it will help you navigate the project's code if you understand the basics of [SvelteKit routing](https://kit.svelte.dev/docs/routing).
|
||||
|
||||
When developing locally, you will run a SvelteKit Node.js server. When this project is deployed to production, it is built as a SPA and deployed as part of [the server project](../server).
|
||||
When developing locally, you will run a SvelteKit Node.js server. When this project is deployed to production, it is built as a SPA and deployed as part of [../server](the server project).
|
||||
|
||||
258
web/package-lock.json
generated
258
web/package-lock.json
generated
@@ -759,9 +759,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@faker-js/faker": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.0.1.tgz",
|
||||
"integrity": "sha512-4mDeYIgM3By7X6t5E6eYwLAa+2h4DeZDF7thhzIg6XB76jeEvMwadYAMCFJL/R4AnEBcAUO9+gL0vhy3s+qvZA==",
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.0.0.tgz",
|
||||
"integrity": "sha512-dTDHJSmz6c1OJ6HO7jiUiIb4sB20Dlkb3pxYsKm0qTXm2Bmj97rlXIhlvaFsW2rvCi+OLlwKLVSS6ZxFUVZvjQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -1875,9 +1875,9 @@
|
||||
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="
|
||||
},
|
||||
"node_modules/@sveltejs/adapter-static": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.5.tgz",
|
||||
"integrity": "sha512-kFJR7RxeB6FBvrKZWAEzIALatgy11ISaaZbcPup8JdWUdrmmfUHHTJ738YHJTEfnCiiXi6aX8Q6ePY7tnSMD6Q==",
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.4.tgz",
|
||||
"integrity": "sha512-Qm4GAHCnRXwfWG9/AtnQ7mqjyjTs7i0Opyb8H2KH9rMR7fLxqiPx/tXeoE6HHo66+72CjyOb4nFH3lrejY4vzA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
@@ -1885,9 +1885,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sveltejs/enhanced-img": {
|
||||
"version": "0.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/enhanced-img/-/enhanced-img-0.3.8.tgz",
|
||||
"integrity": "sha512-n66u46ZeqHltiTm0BEjWptYmCrCY0EltEEvakmC7d5o5ZejDbOvOWm914mebbRKaP2Bezv65TNCod/wqvw/0KA==",
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/enhanced-img/-/enhanced-img-0.3.4.tgz",
|
||||
"integrity": "sha512-eX+ob5uWr0bTLMKeG9nhhM84aR88hqiLiyEfWZPX7ijhk/wlmYSUX9nOiaVHh2ct1U+Ju9Hhb90Copw+ZNOB8w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1901,9 +1901,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sveltejs/kit": {
|
||||
"version": "2.5.28",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.28.tgz",
|
||||
"integrity": "sha512-/O7pvFGBsQPcFa9UrW8eUC5uHTOXLsUp3SN0dY6YmRAL9nfPSrJsSJk//j5vMpinSshzUjteAFcfQTU+04Ka1w==",
|
||||
"version": "2.5.26",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.26.tgz",
|
||||
"integrity": "sha512-8l1JTIM2L+bS8ebq1E+nGjv/YSKSnD9Q19bYIUkc41vaEG2JjVUx6ikvPIJv2hkQAuqJLzoPrXlKk4KcyWOv3Q==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
@@ -2318,17 +2318,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.6.0.tgz",
|
||||
"integrity": "sha512-UOaz/wFowmoh2G6Mr9gw60B1mm0MzUtm6Ic8G2yM1Le6gyj5Loi/N+O5mocugRGY+8OeeKmkMmbxNqUCq3B4Sg==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.5.0.tgz",
|
||||
"integrity": "sha512-lHS5hvz33iUFQKuPFGheAB84LwcJ60G8vKnEhnfcK1l8kGVLro2SFYW6K0/tj8FUhRJ0VHyg1oAfg50QGbPPHw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.6.0",
|
||||
"@typescript-eslint/type-utils": "8.6.0",
|
||||
"@typescript-eslint/utils": "8.6.0",
|
||||
"@typescript-eslint/visitor-keys": "8.6.0",
|
||||
"@typescript-eslint/scope-manager": "8.5.0",
|
||||
"@typescript-eslint/type-utils": "8.5.0",
|
||||
"@typescript-eslint/utils": "8.5.0",
|
||||
"@typescript-eslint/visitor-keys": "8.5.0",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.3.1",
|
||||
"natural-compare": "^1.4.0",
|
||||
@@ -2352,16 +2352,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.6.0.tgz",
|
||||
"integrity": "sha512-eQcbCuA2Vmw45iGfcyG4y6rS7BhWfz9MQuk409WD47qMM+bKCGQWXxvoOs1DUp+T7UBMTtRTVT+kXr7Sh4O9Ow==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.5.0.tgz",
|
||||
"integrity": "sha512-gF77eNv0Xz2UJg/NbpWJ0kqAm35UMsvZf1GHj8D9MRFTj/V3tAciIWXfmPLsAAF/vUlpWPvUDyH1jjsr0cMVWw==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.6.0",
|
||||
"@typescript-eslint/types": "8.6.0",
|
||||
"@typescript-eslint/typescript-estree": "8.6.0",
|
||||
"@typescript-eslint/visitor-keys": "8.6.0",
|
||||
"@typescript-eslint/scope-manager": "8.5.0",
|
||||
"@typescript-eslint/types": "8.5.0",
|
||||
"@typescript-eslint/typescript-estree": "8.5.0",
|
||||
"@typescript-eslint/visitor-keys": "8.5.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2381,14 +2381,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.6.0.tgz",
|
||||
"integrity": "sha512-ZuoutoS5y9UOxKvpc/GkvF4cuEmpokda4wRg64JEia27wX+PysIE9q+lzDtlHHgblwUWwo5/Qn+/WyTUvDwBHw==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.5.0.tgz",
|
||||
"integrity": "sha512-06JOQ9Qgj33yvBEx6tpC8ecP9o860rsR22hWMEd12WcTRrfaFgHr2RB/CA/B+7BMhHkXT4chg2MyboGdFGawYg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.6.0",
|
||||
"@typescript-eslint/visitor-keys": "8.6.0"
|
||||
"@typescript-eslint/types": "8.5.0",
|
||||
"@typescript-eslint/visitor-keys": "8.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -2399,14 +2399,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.6.0.tgz",
|
||||
"integrity": "sha512-dtePl4gsuenXVwC7dVNlb4mGDcKjDT/Ropsk4za/ouMBPplCLyznIaR+W65mvCvsyS97dymoBRrioEXI7k0XIg==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.5.0.tgz",
|
||||
"integrity": "sha512-N1K8Ix+lUM+cIDhL2uekVn/ZD7TZW+9/rwz8DclQpcQ9rk4sIL5CAlBC0CugWKREmDjBzI/kQqU4wkg46jWLYA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "8.6.0",
|
||||
"@typescript-eslint/utils": "8.6.0",
|
||||
"@typescript-eslint/typescript-estree": "8.5.0",
|
||||
"@typescript-eslint/utils": "8.5.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^1.3.0"
|
||||
},
|
||||
@@ -2424,9 +2424,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.6.0.tgz",
|
||||
"integrity": "sha512-rojqFZGd4MQxw33SrOy09qIDS8WEldM8JWtKQLAjf/X5mGSeEFh5ixQlxssMNyPslVIk9yzWqXCsV2eFhYrYUw==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.5.0.tgz",
|
||||
"integrity": "sha512-qjkormnQS5wF9pjSi6q60bKUHH44j2APxfh9TQRXK8wbYVeDYYdYJGIROL87LGZZ2gz3Rbmjc736qyL8deVtdw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -2438,14 +2438,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.6.0.tgz",
|
||||
"integrity": "sha512-MOVAzsKJIPIlLK239l5s06YXjNqpKTVhBVDnqUumQJja5+Y94V3+4VUFRA0G60y2jNnTVwRCkhyGQpavfsbq/g==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.5.0.tgz",
|
||||
"integrity": "sha512-vEG2Sf9P8BPQ+d0pxdfndw3xIXaoSjliG0/Ejk7UggByZPKXmJmw3GW5jV2gHNQNawBUyfahoSiCFVov0Ruf7Q==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.6.0",
|
||||
"@typescript-eslint/visitor-keys": "8.6.0",
|
||||
"@typescript-eslint/types": "8.5.0",
|
||||
"@typescript-eslint/visitor-keys": "8.5.0",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
@@ -2493,16 +2493,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.6.0.tgz",
|
||||
"integrity": "sha512-eNp9cWnYf36NaOVjkEUznf6fEgVy1TWpE0o52e4wtojjBx7D1UV2WAWGzR+8Y5lVFtpMLPwNbC67T83DWSph4A==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.5.0.tgz",
|
||||
"integrity": "sha512-6yyGYVL0e+VzGYp60wvkBHiqDWOpT63pdMV2CVG4LVDd5uR6q1qQN/7LafBZtAtNIn/mqXjsSeS5ggv/P0iECw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@typescript-eslint/scope-manager": "8.6.0",
|
||||
"@typescript-eslint/types": "8.6.0",
|
||||
"@typescript-eslint/typescript-estree": "8.6.0"
|
||||
"@typescript-eslint/scope-manager": "8.5.0",
|
||||
"@typescript-eslint/types": "8.5.0",
|
||||
"@typescript-eslint/typescript-estree": "8.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -2516,13 +2516,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.6.0.tgz",
|
||||
"integrity": "sha512-wapVFfZg9H0qOYh4grNVQiMklJGluQrOUiOhYRrQWhx7BY/+I1IYb8BczWNbbUpO+pqy0rDciv3lQH5E1bCLrg==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.5.0.tgz",
|
||||
"integrity": "sha512-yTPqMnbAZJNy2Xq2XU8AdtOW9tJIr+UQb64aXB9f3B1498Zx9JorVgFJcZpEc9UBuCCrdzKID2RGAMkYcDtZOw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.6.0",
|
||||
"@typescript-eslint/types": "8.5.0",
|
||||
"eslint-visitor-keys": "^3.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2534,9 +2534,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/coverage-v8": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.1.tgz",
|
||||
"integrity": "sha512-md/A7A3c42oTT8JUHSqjP5uKTWJejzUW4jalpvs+rZ27gsURsMU8DEb+8Jf8C6Kj2gwfSHJqobDNBuoqlm0cFw==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.0.tgz",
|
||||
"integrity": "sha512-yqCkr2nrV4o58VcVMxTVkS6Ggxzy7pmSD8JbTbhbH5PsQfUIES1QT716VUzo33wf2lX9EcWYdT3Vl2MMmjR59g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2557,8 +2557,8 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vitest/browser": "2.1.1",
|
||||
"vitest": "2.1.1"
|
||||
"@vitest/browser": "2.1.0",
|
||||
"vitest": "2.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vitest/browser": {
|
||||
@@ -2567,14 +2567,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.1.tgz",
|
||||
"integrity": "sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.0.tgz",
|
||||
"integrity": "sha512-N3/xR4fSu0+6sVZETEtPT1orUs2+Y477JOXTcU3xKuu3uBlsgbD7/7Mz2LZ1Jr1XjwilEWlrIgSCj4N1+5ZmsQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "2.1.1",
|
||||
"@vitest/utils": "2.1.1",
|
||||
"@vitest/spy": "2.1.0",
|
||||
"@vitest/utils": "2.1.0",
|
||||
"chai": "^5.1.1",
|
||||
"tinyrainbow": "^1.2.0"
|
||||
},
|
||||
@@ -2583,9 +2583,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/mocker": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.1.tgz",
|
||||
"integrity": "sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.0.tgz",
|
||||
"integrity": "sha512-ZxENovUqhzl+QiOFpagiHUNUuZ1qPd5yYTCYHomGIZOFArzn4mgX2oxZmiAItJWAaXHG6bbpb/DpSPhlk5DgtA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2597,7 +2597,7 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vitest/spy": "2.1.1",
|
||||
"@vitest/spy": "2.1.0",
|
||||
"msw": "^2.3.5",
|
||||
"vite": "^5.0.0"
|
||||
},
|
||||
@@ -2624,13 +2624,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/runner": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.1.tgz",
|
||||
"integrity": "sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.0.tgz",
|
||||
"integrity": "sha512-D9+ZiB8MbMt7qWDRJc4CRNNUlne/8E1X7dcKhZVAbcOKG58MGGYVDqAq19xlhNfMFZsW0bpVKgztBwks38Ko0w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/utils": "2.1.1",
|
||||
"@vitest/utils": "2.1.0",
|
||||
"pathe": "^1.1.2"
|
||||
},
|
||||
"funding": {
|
||||
@@ -2638,13 +2638,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/snapshot": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.1.tgz",
|
||||
"integrity": "sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.0.tgz",
|
||||
"integrity": "sha512-x69CygGMzt9VCO283K2/FYQ+nBrOj66OTKpsPykjCR4Ac3lLV+m85hj9reaIGmjBSsKzVvbxWmjWE3kF5ha3uQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "2.1.1",
|
||||
"@vitest/pretty-format": "2.1.0",
|
||||
"magic-string": "^0.30.11",
|
||||
"pathe": "^1.1.2"
|
||||
},
|
||||
@@ -2652,10 +2652,23 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/snapshot/node_modules/@vitest/pretty-format": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.0.tgz",
|
||||
"integrity": "sha512-7sxf2F3DNYatgmzXXcTh6cq+/fxwB47RIQqZJFoSH883wnVAoccSRT6g+dTKemUBo8Q5N4OYYj1EBXLuRKvp3Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tinyrainbow": "^1.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/spy": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.1.tgz",
|
||||
"integrity": "sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.0.tgz",
|
||||
"integrity": "sha512-IXX5NkbdgTYTog3F14i2LgnBc+20YmkXMx0IWai84mcxySUDRgm0ihbOfR4L0EVRBDFG85GjmQQEZNNKVVpkZw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2666,13 +2679,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/utils": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.1.tgz",
|
||||
"integrity": "sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.0.tgz",
|
||||
"integrity": "sha512-rreyfVe0PuNqJfKYUwfPDfi6rrp0VSu0Wgvp5WBqJonP+4NvXHk48X6oBam1Lj47Hy6jbJtnMj3OcRdrkTP0tA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "2.1.1",
|
||||
"@vitest/pretty-format": "2.1.0",
|
||||
"loupe": "^3.1.1",
|
||||
"tinyrainbow": "^1.2.0"
|
||||
},
|
||||
@@ -2680,10 +2693,23 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/utils/node_modules/@vitest/pretty-format": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.0.tgz",
|
||||
"integrity": "sha512-7sxf2F3DNYatgmzXXcTh6cq+/fxwB47RIQqZJFoSH883wnVAoccSRT6g+dTKemUBo8Q5N4OYYj1EBXLuRKvp3Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tinyrainbow": "^1.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@zoom-image/core": {
|
||||
"version": "0.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@zoom-image/core/-/core-0.38.0.tgz",
|
||||
"integrity": "sha512-rA6/qTGfsRtWRs+WfMF0dIs+Ft9GBFusxXzEqqFsQa/0iYtN0MmOiuKzXGYPcIFKTbmQW/qqk0afIBtWd9163g==",
|
||||
"version": "0.37.1",
|
||||
"resolved": "https://registry.npmjs.org/@zoom-image/core/-/core-0.37.1.tgz",
|
||||
"integrity": "sha512-mIJaZJBi3jvOD2gtzoSe4yhnxfvx7GcYlVTLoJE6VPawb3Ei5dvHuRRXa8/dNHtCf1Xf2RNSEm1Za2+TqkAiBQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@namnode/store": "^0.1.0"
|
||||
@@ -2694,12 +2720,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@zoom-image/svelte": {
|
||||
"version": "0.2.22",
|
||||
"resolved": "https://registry.npmjs.org/@zoom-image/svelte/-/svelte-0.2.22.tgz",
|
||||
"integrity": "sha512-lExo4M511/HtkmCsBzV5f8ABs8bEMZGtIrwl1pJro77iJ+5j9Yt7KUlPs6o+Yp028T6fqGJUsOCxCNWNZn9BIg==",
|
||||
"version": "0.2.21",
|
||||
"resolved": "https://registry.npmjs.org/@zoom-image/svelte/-/svelte-0.2.21.tgz",
|
||||
"integrity": "sha512-242xKpIaVZC/cymvNF4+JlcKwAaM9l3W2QS4DHSsnqT8xvPBgBgns+1lqOuYYKSAa85DB1UL0NMBhTg8Gk4RpA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@zoom-image/core": "0.38.0"
|
||||
"@zoom-image/core": "0.37.1"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -3838,9 +3864,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-svelte": {
|
||||
"version": "2.44.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.44.0.tgz",
|
||||
"integrity": "sha512-wav4MOs02vBb1WjvTCYItwJCxMkuk2Z4p+K/eyjL0N/z7ahXLP+0LtQQjiKc2ezuif7GnZLbD1F3o1VHzSvdVg==",
|
||||
"version": "2.43.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.43.0.tgz",
|
||||
"integrity": "sha512-REkxQWvg2pp7QVLxQNa+dJ97xUqRe7Y2JJbSWkHSuszu0VcblZtXkPBPckkivk99y5CdLw4slqfPylL2d/X4jQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -3854,7 +3880,7 @@
|
||||
"postcss-safe-parser": "^6.0.0",
|
||||
"postcss-selector-parser": "^6.1.0",
|
||||
"semver": "^7.6.2",
|
||||
"svelte-eslint-parser": "^0.41.1"
|
||||
"svelte-eslint-parser": "^0.41.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.17.0 || >=16.0.0"
|
||||
@@ -7134,9 +7160,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-eslint-parser": {
|
||||
"version": "0.41.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.41.1.tgz",
|
||||
"integrity": "sha512-08ndI6zTghzI8SuJAFpvMbA/haPSGn3xz19pjre19yYMw8Nw/wQJ2PrZBI/L8ijGTgtkWCQQiLLy+Z1tfaCwNA==",
|
||||
"version": "0.41.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.41.0.tgz",
|
||||
"integrity": "sha512-L6f4hOL+AbgfBIB52Z310pg1d2QjRqm7wy3kI1W6hhdhX5bvu7+f0R6w4ykp5HoDdzq+vGhIJmsisaiJDGmVfA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -7652,9 +7678,9 @@
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "3.4.12",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.12.tgz",
|
||||
"integrity": "sha512-Htf/gHj2+soPb9UayUNci/Ja3d8pTmu9ONTfh4QY8r3MATTZOzmv6UYWF7ZwikEIC8okpfqmGqrmDehua8mF8w==",
|
||||
"version": "3.4.11",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.11.tgz",
|
||||
"integrity": "sha512-qhEuBcLemjSJk5ajccN9xJFtM/h0AVCPaA6C92jNP+M2J8kX+eMJHI7R2HFKUvvAsMpcfLILMCFYSeDwpMmlUg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -8220,9 +8246,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite-node": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.1.tgz",
|
||||
"integrity": "sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.0.tgz",
|
||||
"integrity": "sha512-+ybYqBVUjYyIscoLzMWodus2enQDZOpGhcU6HdOVD6n8WZdk12w1GFL3mbnxLs7hPtRtqs1Wo5YF6/Tsr6fmhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -8256,19 +8282,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.1.tgz",
|
||||
"integrity": "sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.0.tgz",
|
||||
"integrity": "sha512-XuuEeyNkqbfr0FtAvd9vFbInSSNY1ykCQTYQ0sj9wPy4hx+1gR7gqVNdW0AX2wrrM1wWlN5fnJDjF9xG6mYRSQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/expect": "2.1.1",
|
||||
"@vitest/mocker": "2.1.1",
|
||||
"@vitest/pretty-format": "^2.1.1",
|
||||
"@vitest/runner": "2.1.1",
|
||||
"@vitest/snapshot": "2.1.1",
|
||||
"@vitest/spy": "2.1.1",
|
||||
"@vitest/utils": "2.1.1",
|
||||
"@vitest/expect": "2.1.0",
|
||||
"@vitest/mocker": "2.1.0",
|
||||
"@vitest/pretty-format": "^2.1.0",
|
||||
"@vitest/runner": "2.1.0",
|
||||
"@vitest/snapshot": "2.1.0",
|
||||
"@vitest/spy": "2.1.0",
|
||||
"@vitest/utils": "2.1.0",
|
||||
"chai": "^5.1.1",
|
||||
"debug": "^4.3.6",
|
||||
"magic-string": "^0.30.11",
|
||||
@@ -8279,7 +8305,7 @@
|
||||
"tinypool": "^1.0.0",
|
||||
"tinyrainbow": "^1.2.0",
|
||||
"vite": "^5.0.0",
|
||||
"vite-node": "2.1.1",
|
||||
"vite-node": "2.1.0",
|
||||
"why-is-node-running": "^2.3.0"
|
||||
},
|
||||
"bin": {
|
||||
@@ -8294,8 +8320,8 @@
|
||||
"peerDependencies": {
|
||||
"@edge-runtime/vm": "*",
|
||||
"@types/node": "^18.0.0 || >=20.0.0",
|
||||
"@vitest/browser": "2.1.1",
|
||||
"@vitest/ui": "2.1.1",
|
||||
"@vitest/browser": "2.1.0",
|
||||
"@vitest/ui": "2.1.0",
|
||||
"happy-dom": "*",
|
||||
"jsdom": "*"
|
||||
},
|
||||
|
||||
@@ -75,21 +75,6 @@
|
||||
</FormatMessage>
|
||||
</p>
|
||||
</SettingInputField>
|
||||
|
||||
<SettingAccordion
|
||||
key="Preload clip model"
|
||||
title={$t('admin.machine_learning_preload_model')}
|
||||
subtitle={$t('admin.machine_learning_preload_model_setting_description')}
|
||||
>
|
||||
<div class="ml-4 mt-4 flex flex-col gap-4">
|
||||
<SettingSwitch
|
||||
title={$t('admin.machine_learning_preload_model_enabled')}
|
||||
subtitle={$t('admin.machine_learning_preload_model_enabled_description')}
|
||||
bind:checked={config.machineLearning.clip.loadTextualModelOnConnection.enabled}
|
||||
disabled={disabled || !config.machineLearning.enabled || !config.machineLearning.clip.enabled}
|
||||
/>
|
||||
</div>
|
||||
</SettingAccordion>
|
||||
</div>
|
||||
</SettingAccordion>
|
||||
|
||||
@@ -160,7 +145,7 @@
|
||||
desc={$t('admin.machine_learning_min_detection_score_description')}
|
||||
bind:value={config.machineLearning.facialRecognition.minScore}
|
||||
step="0.1"
|
||||
min={0.1}
|
||||
min={0}
|
||||
max={1}
|
||||
disabled={disabled || !config.machineLearning.enabled || !config.machineLearning.facialRecognition.enabled}
|
||||
isEdited={config.machineLearning.facialRecognition.minScore !==
|
||||
@@ -173,7 +158,7 @@
|
||||
desc={$t('admin.machine_learning_max_recognition_distance_description')}
|
||||
bind:value={config.machineLearning.facialRecognition.maxDistance}
|
||||
step="0.1"
|
||||
min={0.1}
|
||||
min={0}
|
||||
max={2}
|
||||
disabled={disabled || !config.machineLearning.enabled || !config.machineLearning.facialRecognition.enabled}
|
||||
isEdited={config.machineLearning.facialRecognition.maxDistance !==
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
export let id: string;
|
||||
export let albumName: string;
|
||||
export let isOwned: boolean;
|
||||
export let onUpdate: (albumName: string) => void;
|
||||
|
||||
$: newAlbumName = albumName;
|
||||
|
||||
@@ -17,17 +16,17 @@
|
||||
}
|
||||
|
||||
try {
|
||||
({ albumName } = await updateAlbumInfo({
|
||||
await updateAlbumInfo({
|
||||
id,
|
||||
updateAlbumDto: {
|
||||
albumName: newAlbumName,
|
||||
},
|
||||
}));
|
||||
onUpdate(albumName);
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.unable_to_save_album'));
|
||||
return;
|
||||
}
|
||||
albumName = newAlbumName;
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -116,12 +116,6 @@
|
||||
"machine_learning_min_detection_score_description": "Minimum confidence score for a face to be detected from 0-1. Lower values will detect more faces but may result in false positives.",
|
||||
"machine_learning_min_recognized_faces": "Minimum recognized faces",
|
||||
"machine_learning_min_recognized_faces_description": "The minimum number of recognized faces for a person to be created. Increasing this makes Facial Recognition more precise at the cost of increasing the chance that a face is not assigned to a person.",
|
||||
"machine_learning_preload_model": "Preload model",
|
||||
"machine_learning_preload_model_enabled": "Enable preload model",
|
||||
"machine_learning_preload_model_enabled_description": "Preload the textual model during the connexion instead of during the first search",
|
||||
"machine_learning_preload_model_setting_description": "Preload the textual model during the connexion",
|
||||
"machine_learning_preload_model_ttl": "Inactivity time before a model in unloaded",
|
||||
"machine_learning_preload_model_ttl_description": "Preload the textual model during the connexion",
|
||||
"machine_learning_settings": "Machine Learning Settings",
|
||||
"machine_learning_settings_description": "Manage machine learning features and settings",
|
||||
"machine_learning_smart_search": "Smart Search",
|
||||
|
||||
@@ -35,9 +35,6 @@ const websocket: Socket<Events> = io({
|
||||
reconnection: true,
|
||||
forceNew: true,
|
||||
autoConnect: false,
|
||||
query: {
|
||||
background: false,
|
||||
},
|
||||
});
|
||||
|
||||
export const websocketStore = {
|
||||
|
||||
@@ -589,12 +589,7 @@
|
||||
{#if viewMode !== ViewMode.SELECT_THUMBNAIL}
|
||||
<!-- ALBUM TITLE -->
|
||||
<section class="pt-8 md:pt-24">
|
||||
<AlbumTitle
|
||||
id={album.id}
|
||||
albumName={album.albumName}
|
||||
{isOwned}
|
||||
onUpdate={(albumName) => (album.albumName = albumName)}
|
||||
/>
|
||||
<AlbumTitle id={album.id} bind:albumName={album.albumName} {isOwned} />
|
||||
|
||||
{#if album.assetCount > 0}
|
||||
<AlbumSummary {album} />
|
||||
|
||||
Reference in New Issue
Block a user