From 1b583e071e80b68352b1b366d60e010d8f8f9535 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Thu, 9 Dec 2021 02:33:41 +0100 Subject: [PATCH] People: Add mutex so changes don't get processed concurrently --- assets/locales/messages.pot | 176 ++++++++-------- frontend/package-lock.json | 288 +++++++++++++-------------- frontend/src/dialog/photo/people.vue | 12 ++ frontend/src/pages/people/faces.vue | 14 +- internal/api/api.go | 4 + internal/api/marker.go | 15 ++ internal/api/subject.go | 8 + internal/config/config.go | 2 + internal/config/test.go | 32 +-- internal/entity/counts.go | 12 +- internal/i18n/messages.go | 2 + internal/mutex/mutex.go | 3 +- internal/query/albums.go | 8 +- internal/query/covers.go | 20 +- internal/query/faces.go | 4 +- internal/query/folders.go | 4 +- internal/query/photo.go | 8 +- internal/query/purge.go | 20 +- 18 files changed, 340 insertions(+), 292 deletions(-) diff --git a/assets/locales/messages.pot b/assets/locales/messages.pot index aadd04cdf..02101b42f 100644 --- a/assets/locales/messages.pot +++ b/assets/locales/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-11-22 17:01+0000\n" +"POT-Creation-Date: 2021-12-09 00:51+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,309 +17,313 @@ msgstr "" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: messages.go:82 +#: messages.go:83 msgid "Unexpected error, please try again" msgstr "" -#: messages.go:83 +#: messages.go:84 msgid "Invalid request" msgstr "" -#: messages.go:84 +#: messages.go:85 msgid "Changes could not be saved" msgstr "" -#: messages.go:85 +#: messages.go:86 msgid "Could not be deleted" msgstr "" -#: messages.go:86 +#: messages.go:87 #, c-format msgid "%s already exists" msgstr "" -#: messages.go:87 +#: messages.go:88 msgid "Not found" msgstr "" -#: messages.go:88 +#: messages.go:89 msgid "File not found" msgstr "" -#: messages.go:89 +#: messages.go:90 msgid "Selection not found" msgstr "" -#: messages.go:90 +#: messages.go:91 msgid "Entity not found" msgstr "" -#: messages.go:91 +#: messages.go:92 msgid "Account not found" msgstr "" -#: messages.go:92 +#: messages.go:93 msgid "User not found" msgstr "" -#: messages.go:93 +#: messages.go:94 msgid "Label not found" msgstr "" -#: messages.go:94 +#: messages.go:95 msgid "Album not found" msgstr "" -#: messages.go:95 +#: messages.go:96 msgid "Subject not found" msgstr "" -#: messages.go:96 +#: messages.go:97 msgid "Person not found" msgstr "" -#: messages.go:97 +#: messages.go:98 msgid "Face not found" msgstr "" -#: messages.go:98 +#: messages.go:99 msgid "Not available in public mode" msgstr "" -#: messages.go:99 +#: messages.go:100 msgid "not available in read-only mode" msgstr "" -#: messages.go:100 +#: messages.go:101 msgid "Please log in and try again" msgstr "" -#: messages.go:101 +#: messages.go:102 msgid "Upload might be offensive" msgstr "" -#: messages.go:102 +#: messages.go:103 msgid "No items selected" msgstr "" -#: messages.go:103 +#: messages.go:104 msgid "Failed creating file, please check permissions" msgstr "" -#: messages.go:104 +#: messages.go:105 msgid "Failed creating folder, please check permissions" msgstr "" -#: messages.go:105 +#: messages.go:106 msgid "Could not connect, please try again" msgstr "" -#: messages.go:106 +#: messages.go:107 msgid "Invalid password, please try again" msgstr "" -#: messages.go:107 +#: messages.go:108 msgid "Feature disabled" msgstr "" -#: messages.go:108 +#: messages.go:109 msgid "No labels selected" msgstr "" -#: messages.go:109 +#: messages.go:110 msgid "No albums selected" msgstr "" -#: messages.go:110 +#: messages.go:111 msgid "No files available for download" msgstr "" -#: messages.go:111 +#: messages.go:112 msgid "Failed to create zip file" msgstr "" -#: messages.go:112 +#: messages.go:113 msgid "Invalid credentials" msgstr "" -#: messages.go:113 +#: messages.go:114 msgid "Invalid link" msgstr "" -#: messages.go:114 +#: messages.go:115 msgid "Invalid name" msgstr "" -#: messages.go:117 -msgid "Changes successfully saved" -msgstr "" - -#: messages.go:118 -msgid "Album created" +#: messages.go:116 +msgid "Busy, please try again later" msgstr "" #: messages.go:119 -msgid "Album saved" +msgid "Changes successfully saved" msgstr "" #: messages.go:120 +msgid "Album created" +msgstr "" + +#: messages.go:121 +msgid "Album saved" +msgstr "" + +#: messages.go:122 #, c-format msgid "Album %s deleted" msgstr "" -#: messages.go:121 +#: messages.go:123 msgid "Album contents cloned" msgstr "" -#: messages.go:122 +#: messages.go:124 msgid "File removed from stack" msgstr "" -#: messages.go:123 -msgid "File deleted" -msgstr "" - -#: messages.go:124 -#, c-format -msgid "Selection added to %s" -msgstr "" - #: messages.go:125 -#, c-format -msgid "One entry added to %s" +msgid "File deleted" msgstr "" #: messages.go:126 #, c-format -msgid "%d entries added to %s" +msgid "Selection added to %s" msgstr "" #: messages.go:127 #, c-format -msgid "One entry removed from %s" +msgid "One entry added to %s" msgstr "" #: messages.go:128 #, c-format -msgid "%d entries removed from %s" +msgid "%d entries added to %s" msgstr "" #: messages.go:129 -msgid "Account created" +#, c-format +msgid "One entry removed from %s" msgstr "" #: messages.go:130 -msgid "Account saved" +#, c-format +msgid "%d entries removed from %s" msgstr "" #: messages.go:131 -msgid "Account deleted" +msgid "Account created" msgstr "" #: messages.go:132 -msgid "Settings saved" +msgid "Account saved" msgstr "" #: messages.go:133 -msgid "Password changed" +msgid "Account deleted" msgstr "" #: messages.go:134 -#, c-format -msgid "Import completed in %d s" +msgid "Settings saved" msgstr "" #: messages.go:135 -msgid "Import canceled" +msgid "Password changed" msgstr "" #: messages.go:136 #, c-format -msgid "Indexing completed in %d s" +msgid "Import completed in %d s" msgstr "" #: messages.go:137 -msgid "Indexing originals..." +msgid "Import canceled" msgstr "" #: messages.go:138 #, c-format -msgid "Indexing files in %s" +msgid "Indexing completed in %d s" msgstr "" #: messages.go:139 -msgid "Indexing canceled" +msgid "Indexing originals..." msgstr "" #: messages.go:140 #, c-format -msgid "Removed %d files and %d photos" +msgid "Indexing files in %s" msgstr "" #: messages.go:141 -#, c-format -msgid "Moving files from %s" +msgid "Indexing canceled" msgstr "" #: messages.go:142 #, c-format -msgid "Copying files from %s" +msgid "Removed %d files and %d photos" msgstr "" #: messages.go:143 -msgid "Labels deleted" +#, c-format +msgid "Moving files from %s" msgstr "" #: messages.go:144 -msgid "Label saved" +#, c-format +msgid "Copying files from %s" msgstr "" #: messages.go:145 -msgid "Subject saved" +msgid "Labels deleted" msgstr "" #: messages.go:146 -msgid "Subject deleted" +msgid "Label saved" msgstr "" #: messages.go:147 -msgid "Person saved" +msgid "Subject saved" msgstr "" #: messages.go:148 -msgid "Person deleted" +msgid "Subject deleted" msgstr "" #: messages.go:149 +msgid "Person saved" +msgstr "" + +#: messages.go:150 +msgid "Person deleted" +msgstr "" + +#: messages.go:151 #, c-format msgid "%d files uploaded in %d s" msgstr "" -#: messages.go:150 +#: messages.go:152 msgid "Selection approved" msgstr "" -#: messages.go:151 +#: messages.go:153 msgid "Selection archived" msgstr "" -#: messages.go:152 +#: messages.go:154 msgid "Selection restored" msgstr "" -#: messages.go:153 +#: messages.go:155 msgid "Selection marked as private" msgstr "" -#: messages.go:154 +#: messages.go:156 msgid "Albums deleted" msgstr "" -#: messages.go:155 +#: messages.go:157 #, c-format msgid "Zip created in %d s" msgstr "" -#: messages.go:156 +#: messages.go:158 msgid "Permanently deleted" msgstr "" diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a7a9fd308..f797224f5 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1902,9 +1902,9 @@ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=" }, "node_modules/@types/node": { - "version": "16.11.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.11.tgz", - "integrity": "sha512-KB0sixD67CeecHC33MYn+eYARkqTheIRNuu97y2XMjR7Wu3XibO1vaY6VBV6O/a89SPI81cEUIYT87UqUWlZNw==" + "version": "16.11.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz", + "integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==" }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -1917,12 +1917,12 @@ "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==" }, "node_modules/@vue/compiler-core": { - "version": "3.2.23", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.23.tgz", - "integrity": "sha512-4ZhiI/orx+7EJ1B+0zjgvXMV2uRN+XBfG06UN2sJfND9rH5gtEQT3QmO4erum1o6Irl7y754W8/KSaDJh4EUQg==", + "version": "3.2.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.24.tgz", + "integrity": "sha512-A0SxB2HAggKzP57LDin5gfgWOTwFyGCtQ5MTMNBADnfQYALWnYuC8kMI0DhRSplGTWRvn9Z2DAnG8f35BnojuA==", "dependencies": { "@babel/parser": "^7.15.0", - "@vue/shared": "3.2.23", + "@vue/shared": "3.2.24", "estree-walker": "^2.0.2", "source-map": "^0.6.1" } @@ -1936,25 +1936,25 @@ } }, "node_modules/@vue/compiler-dom": { - "version": "3.2.23", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.23.tgz", - "integrity": "sha512-X2Nw8QFc5lgoK3kio5ktM95nqmLUH+q+N/PbV4kCHzF1avqv/EGLnAhaaF0Iu4bewNvHJAAhhwPZFeoV/22nbw==", + "version": "3.2.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.24.tgz", + "integrity": "sha512-KQEm8r0JFsrNNIfbD28pcwMvHpcJcwjVR1XWFcD0yyQ8eREd7IXhT7J6j7iNCSE/TIo78NOvkwbyX+lnIm836w==", "dependencies": { - "@vue/compiler-core": "3.2.23", - "@vue/shared": "3.2.23" + "@vue/compiler-core": "3.2.24", + "@vue/shared": "3.2.24" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.2.23", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.23.tgz", - "integrity": "sha512-Aw+pb50Q5zTjyvWod8mNKmYZDRGHJBptmNNWE+84ZxrzEztPgMz8cNYIzWGbwcFVkmJlhvioAMvKnB+LM/sjSA==", + "version": "3.2.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.24.tgz", + "integrity": "sha512-YGPcIvVJp2qTPkuT6kT43Eo1xjstyY4bmuiSV31my4bQMBFVR26ANmifUSt759Blok71gK0WzfIZHbcOKYOeKA==", "dependencies": { "@babel/parser": "^7.15.0", - "@vue/compiler-core": "3.2.23", - "@vue/compiler-dom": "3.2.23", - "@vue/compiler-ssr": "3.2.23", - "@vue/ref-transform": "3.2.23", - "@vue/shared": "3.2.23", + "@vue/compiler-core": "3.2.24", + "@vue/compiler-dom": "3.2.24", + "@vue/compiler-ssr": "3.2.24", + "@vue/ref-transform": "3.2.24", + "@vue/shared": "3.2.24", "estree-walker": "^2.0.2", "magic-string": "^0.25.7", "postcss": "^8.1.10", @@ -1970,12 +1970,12 @@ } }, "node_modules/@vue/compiler-ssr": { - "version": "3.2.23", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.23.tgz", - "integrity": "sha512-Bqzn4jFyXPK1Ehqiq7e/czS8n62gtYF1Zfeu0DrR5uv+SBllh7LIvZjZU6+c8qbocAd3/T3I3gn2cZGmnDb6zg==", + "version": "3.2.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.24.tgz", + "integrity": "sha512-E1HHShNsGVWXxs68LDOUuI+Bzak9W/Ier/366aKDBFuwvfwgruwq6abhMfj6pSDZpwZ/PXnfliyl/m7qBSq6gw==", "dependencies": { - "@vue/compiler-dom": "3.2.23", - "@vue/shared": "3.2.23" + "@vue/compiler-dom": "3.2.24", + "@vue/shared": "3.2.24" } }, "node_modules/@vue/component-compiler-utils": { @@ -2026,26 +2026,26 @@ } }, "node_modules/@vue/ref-transform": { - "version": "3.2.23", - "resolved": "https://registry.npmjs.org/@vue/ref-transform/-/ref-transform-3.2.23.tgz", - "integrity": "sha512-gW0GD2PSAs/th7mC7tPB/UwpIQxclbApVtsDtscDmOJXb2+cdu60ny+SuHNgfrlUT/JqWKQHq7jFKO4woxLNaA==", + "version": "3.2.24", + "resolved": "https://registry.npmjs.org/@vue/ref-transform/-/ref-transform-3.2.24.tgz", + "integrity": "sha512-j6oNbsGLvea2rF8GQB9w6q7UFL1So7J+t6ducaMeWPSyjYZ+slWpwPVK6mmyghg5oGqC41R+HC5BV036Y0KhXQ==", "dependencies": { "@babel/parser": "^7.15.0", - "@vue/compiler-core": "3.2.23", - "@vue/shared": "3.2.23", + "@vue/compiler-core": "3.2.24", + "@vue/shared": "3.2.24", "estree-walker": "^2.0.2", "magic-string": "^0.25.7" } }, "node_modules/@vue/shared": { - "version": "3.2.23", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.23.tgz", - "integrity": "sha512-U+/Jefa0QfXUF2qVy9Dqlrb6HKJSr9/wJcM66wXmWcTOoqg7hOWzF4qruDle51pyF4x3wMn6TSH54UdjKjCKMA==" + "version": "3.2.24", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.24.tgz", + "integrity": "sha512-BUgRiZCkCrqDps5aQ9av05xcge3rn092ztKIh17tHkeEFgP4zfXMQWBA2zfdoCdCEdBL26xtOv+FZYiOp9RUDA==" }, "node_modules/@vvo/tzdb": { - "version": "6.35.0", - "resolved": "https://registry.npmjs.org/@vvo/tzdb/-/tzdb-6.35.0.tgz", - "integrity": "sha512-leN0YmA9+N7ZotdAGp/MzLcAippUpC6Occ/P4hG6BAn4rDXJ4NMf/yiD2K8RyV41I1zK0EMVxo3ca1WrCq4J5g==" + "version": "6.36.0", + "resolved": "https://registry.npmjs.org/@vvo/tzdb/-/tzdb-6.36.0.tgz", + "integrity": "sha512-t+Hs4PJJLiWYZcf5+19VDEHnSO1KVJ6qp7SmRfYWpuHLCWcSvCeaZGSeEjprEYIW4+2vOqGTmSUZaRzySIVq+A==" }, "node_modules/@webassemblyjs/ast": { "version": "1.11.1", @@ -3068,9 +3068,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001284", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001284.tgz", - "integrity": "sha512-t28SKa7g6kiIQi6NHeOcKrOrGMzCRrXvlasPwWC26TH2QNdglgzQIRUuJ0cR3NeQPH+5jpuveeeSFDLm2zbkEw==", + "version": "1.0.30001285", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001285.tgz", + "integrity": "sha512-KAOkuUtcQ901MtmvxfKD+ODHH9YVDYnBt+TGYSz2KIfnq22CiArbUxXPN9067gNbgMlnNYRSwho8OPXZPALB9Q==", "funding": { "type": "opencollective", "url": "https://opencollective.com/browserslist" @@ -3477,9 +3477,9 @@ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, "node_modules/core-js": { - "version": "3.19.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.19.2.tgz", - "integrity": "sha512-ciYCResnLIATSsXuXnIOH4CbdfgV+H1Ltg16hJFN7/v6OxqnFr/IFGeLacaZ+fHLAm0TBbXwNK9/DNBzBUrO/g==", + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.19.3.tgz", + "integrity": "sha512-LeLBMgEGSsG7giquSzvgBrTS7V5UL6ks3eQlUSbN8dJStlLFiRzUm5iqsRyzUB8carhfKjkJ2vzKqE6z1Vga9g==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -3487,9 +3487,9 @@ } }, "node_modules/core-js-compat": { - "version": "3.19.2", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.19.2.tgz", - "integrity": "sha512-ObBY1W5vx/LFFMaL1P5Udo4Npib6fu+cMokeziWkA8Tns4FcDemKF5j9JvaI5JhdkW8EQJQGJN1EcrzmEwuAqQ==", + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.19.3.tgz", + "integrity": "sha512-59tYzuWgEEVU9r+SRgceIGXSSUn47JknoiXW6Oq7RW8QHjXWz3/vp8pa7dbtuVu40sewz3OP3JmQEcDdztrLhA==", "dependencies": { "browserslist": "^4.18.1", "semver": "7.0.0" @@ -4345,9 +4345,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.10.tgz", - "integrity": "sha512-tFgA40Iq2oy4k2PnZrLJowbgpij+lD6ZLxkw8Ht1NKTYyN8dvSvC5xlo8X0WW2jqhKSzITrbr5mpB4/AZ/8OUA==" + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.14.tgz", + "integrity": "sha512-RsGkAN9JEAYMObS72kzUsPPcPGMqX1rBqGuXi9aa4TBKLzICoLf+DAAtd0fVFzrniJqYzpby47gthCUoObfs0Q==" }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -5988,9 +5988,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.14.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", - "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==", + "version": "1.14.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz", + "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==", "funding": [ { "type": "individual", @@ -8406,9 +8406,9 @@ } }, "node_modules/object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz", + "integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8706,12 +8706,9 @@ } }, "node_modules/pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", - "dependencies": { - "node-modules-regexp": "^1.0.0" - }, + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.4.tgz", + "integrity": "sha512-ZIrVPH+A52Dw84R0L3/VS9Op04PuQ2SEoJL6bkshmiTic/HldyW9Tf7oH5mhJZBK7NmDx27vSMrYEXPXclpDKw==", "engines": { "node": ">= 6" } @@ -10790,9 +10787,9 @@ } }, "node_modules/prettier": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.0.tgz", - "integrity": "sha512-FM/zAKgWTxj40rH03VxzIPdXmj39SwSjwG0heUcNFwI+EMZJnY93yAiKXM3dObIKAM5TA88werc8T/EwhB45eg==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", + "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", "bin": { "prettier": "bin-prettier.js" }, @@ -11455,9 +11452,9 @@ } }, "node_modules/sass-loader": { - "version": "12.3.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.3.0.tgz", - "integrity": "sha512-6l9qwhdOb7qSrtOu96QQ81LVl8v6Dp9j1w3akOm0aWHyrTYtagDt5+kS32N4yq4hHk3M+rdqoRMH+lIdqvW6HA==", + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.4.0.tgz", + "integrity": "sha512-7xN+8khDIzym1oL9XyS6zP6Ges+Bo2B2xbPrjdMHEYyV3AQYhd/wXeru++3ODHF0zMjYmVadblSKrPrjEkL8mg==", "dependencies": { "klona": "^2.0.4", "neo-async": "^2.6.2" @@ -11471,7 +11468,7 @@ }, "peerDependencies": { "fibers": ">= 3.1.0", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", "sass": "^1.3.0", "webpack": "^5.0.0" }, @@ -13188,9 +13185,9 @@ } }, "node_modules/watchpack": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.0.tgz", - "integrity": "sha512-MnN0Q1OsvB/GGHETrFeZPQaOelWh/7O+EiFlj8sM9GPjtQkis7k01aAxrg/18kTfoIVcLL+haEVFlXDaSRwKRw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", + "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -13200,9 +13197,9 @@ } }, "node_modules/webpack": { - "version": "5.64.4", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.64.4.tgz", - "integrity": "sha512-LWhqfKjCLoYJLKJY8wk2C3h77i8VyHowG3qYNZiIqD6D0ZS40439S/KVuc/PY48jp2yQmy0mhMknq8cys4jFMw==", + "version": "5.65.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.65.0.tgz", + "integrity": "sha512-Q5or2o6EKs7+oKmJo7LaqZaMOlDWQse9Tm5l1WAfU/ujLGN5Pb0SqGeVkN/4bpPmEqEP5RnVhiqsOtWtUVwGRw==", "dependencies": { "@types/eslint-scope": "^3.7.0", "@types/estree": "^0.0.50", @@ -13226,7 +13223,7 @@ "schema-utils": "^3.1.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.3.0", + "watchpack": "^2.3.1", "webpack-sources": "^3.2.2" }, "bin": { @@ -15017,9 +15014,9 @@ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=" }, "@types/node": { - "version": "16.11.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.11.tgz", - "integrity": "sha512-KB0sixD67CeecHC33MYn+eYARkqTheIRNuu97y2XMjR7Wu3XibO1vaY6VBV6O/a89SPI81cEUIYT87UqUWlZNw==" + "version": "16.11.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz", + "integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==" }, "@types/parse-json": { "version": "4.0.0", @@ -15032,12 +15029,12 @@ "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==" }, "@vue/compiler-core": { - "version": "3.2.23", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.23.tgz", - "integrity": "sha512-4ZhiI/orx+7EJ1B+0zjgvXMV2uRN+XBfG06UN2sJfND9rH5gtEQT3QmO4erum1o6Irl7y754W8/KSaDJh4EUQg==", + "version": "3.2.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.24.tgz", + "integrity": "sha512-A0SxB2HAggKzP57LDin5gfgWOTwFyGCtQ5MTMNBADnfQYALWnYuC8kMI0DhRSplGTWRvn9Z2DAnG8f35BnojuA==", "requires": { "@babel/parser": "^7.15.0", - "@vue/shared": "3.2.23", + "@vue/shared": "3.2.24", "estree-walker": "^2.0.2", "source-map": "^0.6.1" }, @@ -15050,25 +15047,25 @@ } }, "@vue/compiler-dom": { - "version": "3.2.23", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.23.tgz", - "integrity": "sha512-X2Nw8QFc5lgoK3kio5ktM95nqmLUH+q+N/PbV4kCHzF1avqv/EGLnAhaaF0Iu4bewNvHJAAhhwPZFeoV/22nbw==", + "version": "3.2.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.24.tgz", + "integrity": "sha512-KQEm8r0JFsrNNIfbD28pcwMvHpcJcwjVR1XWFcD0yyQ8eREd7IXhT7J6j7iNCSE/TIo78NOvkwbyX+lnIm836w==", "requires": { - "@vue/compiler-core": "3.2.23", - "@vue/shared": "3.2.23" + "@vue/compiler-core": "3.2.24", + "@vue/shared": "3.2.24" } }, "@vue/compiler-sfc": { - "version": "3.2.23", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.23.tgz", - "integrity": "sha512-Aw+pb50Q5zTjyvWod8mNKmYZDRGHJBptmNNWE+84ZxrzEztPgMz8cNYIzWGbwcFVkmJlhvioAMvKnB+LM/sjSA==", + "version": "3.2.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.24.tgz", + "integrity": "sha512-YGPcIvVJp2qTPkuT6kT43Eo1xjstyY4bmuiSV31my4bQMBFVR26ANmifUSt759Blok71gK0WzfIZHbcOKYOeKA==", "requires": { "@babel/parser": "^7.15.0", - "@vue/compiler-core": "3.2.23", - "@vue/compiler-dom": "3.2.23", - "@vue/compiler-ssr": "3.2.23", - "@vue/ref-transform": "3.2.23", - "@vue/shared": "3.2.23", + "@vue/compiler-core": "3.2.24", + "@vue/compiler-dom": "3.2.24", + "@vue/compiler-ssr": "3.2.24", + "@vue/ref-transform": "3.2.24", + "@vue/shared": "3.2.24", "estree-walker": "^2.0.2", "magic-string": "^0.25.7", "postcss": "^8.1.10", @@ -15083,12 +15080,12 @@ } }, "@vue/compiler-ssr": { - "version": "3.2.23", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.23.tgz", - "integrity": "sha512-Bqzn4jFyXPK1Ehqiq7e/czS8n62gtYF1Zfeu0DrR5uv+SBllh7LIvZjZU6+c8qbocAd3/T3I3gn2cZGmnDb6zg==", + "version": "3.2.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.24.tgz", + "integrity": "sha512-E1HHShNsGVWXxs68LDOUuI+Bzak9W/Ier/366aKDBFuwvfwgruwq6abhMfj6pSDZpwZ/PXnfliyl/m7qBSq6gw==", "requires": { - "@vue/compiler-dom": "3.2.23", - "@vue/shared": "3.2.23" + "@vue/compiler-dom": "3.2.24", + "@vue/shared": "3.2.24" } }, "@vue/component-compiler-utils": { @@ -15129,26 +15126,26 @@ } }, "@vue/ref-transform": { - "version": "3.2.23", - "resolved": "https://registry.npmjs.org/@vue/ref-transform/-/ref-transform-3.2.23.tgz", - "integrity": "sha512-gW0GD2PSAs/th7mC7tPB/UwpIQxclbApVtsDtscDmOJXb2+cdu60ny+SuHNgfrlUT/JqWKQHq7jFKO4woxLNaA==", + "version": "3.2.24", + "resolved": "https://registry.npmjs.org/@vue/ref-transform/-/ref-transform-3.2.24.tgz", + "integrity": "sha512-j6oNbsGLvea2rF8GQB9w6q7UFL1So7J+t6ducaMeWPSyjYZ+slWpwPVK6mmyghg5oGqC41R+HC5BV036Y0KhXQ==", "requires": { "@babel/parser": "^7.15.0", - "@vue/compiler-core": "3.2.23", - "@vue/shared": "3.2.23", + "@vue/compiler-core": "3.2.24", + "@vue/shared": "3.2.24", "estree-walker": "^2.0.2", "magic-string": "^0.25.7" } }, "@vue/shared": { - "version": "3.2.23", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.23.tgz", - "integrity": "sha512-U+/Jefa0QfXUF2qVy9Dqlrb6HKJSr9/wJcM66wXmWcTOoqg7hOWzF4qruDle51pyF4x3wMn6TSH54UdjKjCKMA==" + "version": "3.2.24", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.24.tgz", + "integrity": "sha512-BUgRiZCkCrqDps5aQ9av05xcge3rn092ztKIh17tHkeEFgP4zfXMQWBA2zfdoCdCEdBL26xtOv+FZYiOp9RUDA==" }, "@vvo/tzdb": { - "version": "6.35.0", - "resolved": "https://registry.npmjs.org/@vvo/tzdb/-/tzdb-6.35.0.tgz", - "integrity": "sha512-leN0YmA9+N7ZotdAGp/MzLcAippUpC6Occ/P4hG6BAn4rDXJ4NMf/yiD2K8RyV41I1zK0EMVxo3ca1WrCq4J5g==" + "version": "6.36.0", + "resolved": "https://registry.npmjs.org/@vvo/tzdb/-/tzdb-6.36.0.tgz", + "integrity": "sha512-t+Hs4PJJLiWYZcf5+19VDEHnSO1KVJ6qp7SmRfYWpuHLCWcSvCeaZGSeEjprEYIW4+2vOqGTmSUZaRzySIVq+A==" }, "@webassemblyjs/ast": { "version": "1.11.1", @@ -15932,9 +15929,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001284", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001284.tgz", - "integrity": "sha512-t28SKa7g6kiIQi6NHeOcKrOrGMzCRrXvlasPwWC26TH2QNdglgzQIRUuJ0cR3NeQPH+5jpuveeeSFDLm2zbkEw==" + "version": "1.0.30001285", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001285.tgz", + "integrity": "sha512-KAOkuUtcQ901MtmvxfKD+ODHH9YVDYnBt+TGYSz2KIfnq22CiArbUxXPN9067gNbgMlnNYRSwho8OPXZPALB9Q==" }, "chai": { "version": "4.3.4", @@ -16264,14 +16261,14 @@ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, "core-js": { - "version": "3.19.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.19.2.tgz", - "integrity": "sha512-ciYCResnLIATSsXuXnIOH4CbdfgV+H1Ltg16hJFN7/v6OxqnFr/IFGeLacaZ+fHLAm0TBbXwNK9/DNBzBUrO/g==" + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.19.3.tgz", + "integrity": "sha512-LeLBMgEGSsG7giquSzvgBrTS7V5UL6ks3eQlUSbN8dJStlLFiRzUm5iqsRyzUB8carhfKjkJ2vzKqE6z1Vga9g==" }, "core-js-compat": { - "version": "3.19.2", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.19.2.tgz", - "integrity": "sha512-ObBY1W5vx/LFFMaL1P5Udo4Npib6fu+cMokeziWkA8Tns4FcDemKF5j9JvaI5JhdkW8EQJQGJN1EcrzmEwuAqQ==", + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.19.3.tgz", + "integrity": "sha512-59tYzuWgEEVU9r+SRgceIGXSSUn47JknoiXW6Oq7RW8QHjXWz3/vp8pa7dbtuVu40sewz3OP3JmQEcDdztrLhA==", "requires": { "browserslist": "^4.18.1", "semver": "7.0.0" @@ -16881,9 +16878,9 @@ "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==" }, "electron-to-chromium": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.10.tgz", - "integrity": "sha512-tFgA40Iq2oy4k2PnZrLJowbgpij+lD6ZLxkw8Ht1NKTYyN8dvSvC5xlo8X0WW2jqhKSzITrbr5mpB4/AZ/8OUA==" + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.14.tgz", + "integrity": "sha512-RsGkAN9JEAYMObS72kzUsPPcPGMqX1rBqGuXi9aa4TBKLzICoLf+DAAtd0fVFzrniJqYzpby47gthCUoObfs0Q==" }, "emoji-regex": { "version": "8.0.0", @@ -18115,9 +18112,9 @@ } }, "follow-redirects": { - "version": "1.14.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", - "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==" + "version": "1.14.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz", + "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==" }, "foreach": { "version": "2.0.5", @@ -19877,9 +19874,9 @@ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==" + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz", + "integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==" }, "object-keys": { "version": "1.1.1", @@ -20087,12 +20084,9 @@ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" }, "pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", - "requires": { - "node-modules-regexp": "^1.0.0" - } + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.4.tgz", + "integrity": "sha512-ZIrVPH+A52Dw84R0L3/VS9Op04PuQ2SEoJL6bkshmiTic/HldyW9Tf7oH5mhJZBK7NmDx27vSMrYEXPXclpDKw==" }, "pkg-dir": { "version": "2.0.0", @@ -21552,9 +21546,9 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" }, "prettier": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.0.tgz", - "integrity": "sha512-FM/zAKgWTxj40rH03VxzIPdXmj39SwSjwG0heUcNFwI+EMZJnY93yAiKXM3dObIKAM5TA88werc8T/EwhB45eg==" + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", + "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==" }, "prettier-linter-helpers": { "version": "1.0.0", @@ -22095,9 +22089,9 @@ } }, "sass-loader": { - "version": "12.3.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.3.0.tgz", - "integrity": "sha512-6l9qwhdOb7qSrtOu96QQ81LVl8v6Dp9j1w3akOm0aWHyrTYtagDt5+kS32N4yq4hHk3M+rdqoRMH+lIdqvW6HA==", + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.4.0.tgz", + "integrity": "sha512-7xN+8khDIzym1oL9XyS6zP6Ges+Bo2B2xbPrjdMHEYyV3AQYhd/wXeru++3ODHF0zMjYmVadblSKrPrjEkL8mg==", "requires": { "klona": "^2.0.4", "neo-async": "^2.6.2" @@ -23394,18 +23388,18 @@ } }, "watchpack": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.0.tgz", - "integrity": "sha512-MnN0Q1OsvB/GGHETrFeZPQaOelWh/7O+EiFlj8sM9GPjtQkis7k01aAxrg/18kTfoIVcLL+haEVFlXDaSRwKRw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", + "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", "requires": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" } }, "webpack": { - "version": "5.64.4", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.64.4.tgz", - "integrity": "sha512-LWhqfKjCLoYJLKJY8wk2C3h77i8VyHowG3qYNZiIqD6D0ZS40439S/KVuc/PY48jp2yQmy0mhMknq8cys4jFMw==", + "version": "5.65.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.65.0.tgz", + "integrity": "sha512-Q5or2o6EKs7+oKmJo7LaqZaMOlDWQse9Tm5l1WAfU/ujLGN5Pb0SqGeVkN/4bpPmEqEP5RnVhiqsOtWtUVwGRw==", "requires": { "@types/eslint-scope": "^3.7.0", "@types/estree": "^0.0.50", @@ -23429,7 +23423,7 @@ "schema-utils": "^3.1.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.3.0", + "watchpack": "^2.3.1", "webpack-sources": "^3.2.2" }, "dependencies": { diff --git a/frontend/src/dialog/photo/people.vue b/frontend/src/dialog/photo/people.vue index 0a31f5b00..75aaf021e 100644 --- a/frontend/src/dialog/photo/people.vue +++ b/frontend/src/dialog/photo/people.vue @@ -133,28 +133,40 @@ export default { refresh() { }, onReject(marker) { + if (this.busy || !marker) return; + this.busy = true; this.$notify.blockUI(); + marker.reject().finally(() => { this.$notify.unblockUI(); this.busy = false; }); }, onApprove(marker) { + if (this.busy || !marker) return; + this.busy = true; + marker.approve().finally(() => this.busy = false); }, onClearSubject(marker) { + if (this.busy || !marker) return; + this.busy = true; this.$notify.blockUI(); + marker.clearSubject(marker).finally(() => { this.$notify.unblockUI(); this.busy = false; }); }, onRename(marker) { + if (this.busy || !marker) return; + this.busy = true; this.$notify.blockUI(); + marker.rename().finally(() => { this.$notify.unblockUI(); this.busy = false; diff --git a/frontend/src/pages/people/faces.vue b/frontend/src/pages/people/faces.vue index ddd9b7375..a1760ae3b 100644 --- a/frontend/src/pages/people/faces.vue +++ b/frontend/src/pages/people/faces.vue @@ -452,7 +452,7 @@ export default { return params; }, refresh() { - if (this.loading || !this.active) { + if (this.loading || !this.active || this.busy) { return; } @@ -501,6 +501,8 @@ export default { }); }, onShow(model) { + if (this.busy || !model) return; + this.busy = true; model.show().finally(() => { this.busy = false; @@ -508,6 +510,8 @@ export default { }); }, onHide(model) { + if (this.busy || !model) return; + this.busy = true; model.hide().finally(() => { this.busy = false; @@ -515,9 +519,7 @@ export default { }); }, toggleHidden(model) { - if (!model) { - return; - } + if (this.busy || !model) return; this.busy = true; @@ -532,8 +534,8 @@ export default { }); }, onRename(model) { - if (!model.Name || model.Name.trim() === "") { - // Refuse to save empty name. + if (this.busy || !model || !model.Name || model.Name.trim() === "") { + // Ignore if busy, refuse to save empty name. return; } diff --git a/internal/api/api.go b/internal/api/api.go index 6d31d8999..52d1c2972 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -111,3 +111,7 @@ func AbortAlreadyExists(c *gin.Context, s string) { func AbortFeatureDisabled(c *gin.Context) { Abort(c, http.StatusForbidden, i18n.ErrFeatureDisabled) } + +func AbortBusy(c *gin.Context) { + Abort(c, http.StatusTooManyRequests, i18n.ErrBusy) +} diff --git a/internal/api/marker.go b/internal/api/marker.go index b346a2a08..5314a7711 100644 --- a/internal/api/marker.go +++ b/internal/api/marker.go @@ -12,6 +12,7 @@ import ( "github.com/photoprism/photoprism/internal/event" "github.com/photoprism/photoprism/internal/form" "github.com/photoprism/photoprism/internal/i18n" + "github.com/photoprism/photoprism/internal/mutex" "github.com/photoprism/photoprism/internal/query" "github.com/photoprism/photoprism/internal/service" ) @@ -66,6 +67,13 @@ func findFileMarker(c *gin.Context) (file *entity.File, marker *entity.Marker, e // id: int Marker ID as returned by the API func UpdateMarker(router *gin.RouterGroup) { router.PUT("/markers/:marker_uid", func(c *gin.Context) { + if err := mutex.People.Start(); err != nil { + AbortBusy(c) + return + } + + defer mutex.People.Stop() + file, marker, err := findFileMarker(c) if err != nil { @@ -137,6 +145,13 @@ func UpdateMarker(router *gin.RouterGroup) { // id: int Marker ID as returned by the API func ClearMarkerSubject(router *gin.RouterGroup) { router.DELETE("/markers/:marker_uid/subject", func(c *gin.Context) { + if err := mutex.People.Start(); err != nil { + AbortBusy(c) + return + } + + defer mutex.People.Stop() + file, marker, err := findFileMarker(c) if err != nil { diff --git a/internal/api/subject.go b/internal/api/subject.go index 77123de48..e832d5581 100644 --- a/internal/api/subject.go +++ b/internal/api/subject.go @@ -10,6 +10,7 @@ import ( "github.com/photoprism/photoprism/internal/event" "github.com/photoprism/photoprism/internal/form" "github.com/photoprism/photoprism/internal/i18n" + "github.com/photoprism/photoprism/internal/mutex" "github.com/photoprism/photoprism/pkg/txt" ) @@ -39,6 +40,13 @@ func GetSubject(router *gin.RouterGroup) { // PUT /api/v1/subjects/:uid func UpdateSubject(router *gin.RouterGroup) { router.PUT("/subjects/:uid", func(c *gin.Context) { + if err := mutex.People.Start(); err != nil { + AbortBusy(c) + return + } + + defer mutex.People.Stop() + s := Auth(SessionID(c), acl.ResourceSubjects, acl.ActionUpdate) if s.Invalid() { diff --git a/internal/config/config.go b/internal/config/config.go index b081d136e..5b3a90868 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -446,10 +446,12 @@ func (c *Config) LogLevel() logrus.Level { // Shutdown services and workers. func (c *Config) Shutdown() { + mutex.People.Cancel() mutex.MainWorker.Cancel() mutex.ShareWorker.Cancel() mutex.SyncWorker.Cancel() mutex.MetaWorker.Cancel() + mutex.FacesWorker.Cancel() if err := c.CloseDb(); err != nil { log.Errorf("could not close database connection: %s", err) diff --git a/internal/config/test.go b/internal/config/test.go index 07a973eb3..f6a2c345e 100644 --- a/internal/config/test.go +++ b/internal/config/test.go @@ -18,7 +18,7 @@ import ( "github.com/urfave/cli" ) -// define constants used for testing the config package +// Download URL and ZIP hash for test files. const ( TestDataZip = "/tmp/photoprism/testdata.zip" TestDataURL = "https://dl.photoprism.org/qa/testdata.zip" @@ -28,12 +28,13 @@ const ( var testConfig *Config var testConfigOnce sync.Once var testConfigMutex sync.Mutex +var testDataMutex sync.Mutex func testDataPath(assetsPath string) string { return assetsPath + "/testdata" } -// NewTestOptions inits valid options used for testing +// NewTestOptions returns valid config options for tests. func NewTestOptions() *Options { assetsPath := fs.Abs("../../assets") storagePath := fs.Abs("../../storage") @@ -80,7 +81,7 @@ func NewTestOptions() *Options { return c } -// NewTestOptionsError inits invalid options used for testing +// NewTestOptionsError returns invalid config options for tests. func NewTestOptionsError() *Options { assetsPath := fs.Abs("../..") testDataPath := fs.Abs("../../storage/testdata") @@ -104,14 +105,14 @@ func SetNewTestConfig() { testConfig = NewTestConfig() } -// TestConfig inits the global testConfig if it was not already initialised +// TestConfig returns the existing test config instance or creates a new instance and returns it. func TestConfig() *Config { testConfigOnce.Do(SetNewTestConfig) return testConfig } -// NewTestConfig inits valid config used for testing +// NewTestConfig returns a valid test config. func NewTestConfig() *Config { defer log.Debug(capture.Time(time.Now(), "config: new test config created")) @@ -147,14 +148,14 @@ func NewTestConfig() *Config { return c } -// NewTestErrorConfig inits invalid config used for testing +// NewTestErrorConfig returns an invalid test config. func NewTestErrorConfig() *Config { c := &Config{options: NewTestOptionsError()} return c } -// CliTestContext returns example cli config for testing +// CliTestContext returns a CLI context for testing. func CliTestContext() *cli.Context { config := NewTestOptions() @@ -200,7 +201,7 @@ func CliTestContext() *cli.Context { return c } -// RemoveTestData deletes files in import, export, originals and cache folders +// RemoveTestData deletes files in import, export, originals, and cache folders. func (c *Config) RemoveTestData(t *testing.T) { if err := os.RemoveAll(c.ImportPath()); err != nil { t.Fatal(err) @@ -219,7 +220,7 @@ func (c *Config) RemoveTestData(t *testing.T) { } } -// DownloadTestData downloads test data from photoprism.org server +// DownloadTestData downloads the test files from the file server. func (c *Config) DownloadTestData(t *testing.T) { if fs.FileExists(TestDataZip) { hash := fs.Hash(TestDataZip) @@ -242,20 +243,23 @@ func (c *Config) DownloadTestData(t *testing.T) { } } -// UnzipTestData in default test folder +// UnzipTestData extracts tests files from the zip archive. func (c *Config) UnzipTestData(t *testing.T) { if _, err := fs.Unzip(TestDataZip, c.StoragePath()); err != nil { t.Fatalf("config: could not unzip test data: %s", err.Error()) } } -// InitializeTestData using testing constant +// InitializeTestData resets the test file directory. func (c *Config) InitializeTestData(t *testing.T) { - defer t.Logf(capture.Time(time.Now(), "config: initialized test data")) + testDataMutex.Lock() + defer testDataMutex.Unlock() + + start := time.Now() c.RemoveTestData(t) - c.DownloadTestData(t) - c.UnzipTestData(t) + + t.Logf("config: initialized test data [%s]", time.Since(start)) } diff --git a/internal/entity/counts.go b/internal/entity/counts.go index 0378d6c48..6b7608cf2 100644 --- a/internal/entity/counts.go +++ b/internal/entity/counts.go @@ -49,8 +49,8 @@ func LabelCounts() LabelPhotoCounts { // UpdatePlacesCounts updates the places photo counts. func UpdatePlacesCounts() (err error) { - mutex.IndexUpdate.Lock() - defer mutex.IndexUpdate.Unlock() + mutex.Index.Lock() + defer mutex.Index.Unlock() start := time.Now() @@ -73,8 +73,8 @@ func UpdatePlacesCounts() (err error) { // UpdateSubjectCounts updates the subject file counts. func UpdateSubjectCounts() (err error) { - mutex.IndexUpdate.Lock() - defer mutex.IndexUpdate.Unlock() + mutex.Index.Lock() + defer mutex.Index.Unlock() start := time.Now() @@ -128,8 +128,8 @@ func UpdateSubjectCounts() (err error) { // UpdateLabelCounts updates the label photo counts. func UpdateLabelCounts() (err error) { - mutex.IndexUpdate.Lock() - defer mutex.IndexUpdate.Unlock() + mutex.Index.Lock() + defer mutex.Index.Unlock() start := time.Now() var res *gorm.DB diff --git a/internal/i18n/messages.go b/internal/i18n/messages.go index 782cb5964..0263a0711 100644 --- a/internal/i18n/messages.go +++ b/internal/i18n/messages.go @@ -34,6 +34,7 @@ const ( ErrInvalidCredentials ErrInvalidLink ErrInvalidName + ErrBusy MsgChangesSaved MsgAlbumCreated @@ -112,6 +113,7 @@ var Messages = MessageMap{ ErrInvalidCredentials: gettext("Invalid credentials"), ErrInvalidLink: gettext("Invalid link"), ErrInvalidName: gettext("Invalid name"), + ErrBusy: gettext("Busy, please try again later"), // Info and confirmation messages: MsgChangesSaved: gettext("Changes successfully saved"), diff --git a/internal/mutex/mutex.go b/internal/mutex/mutex.go index 22262bc78..c8303af94 100644 --- a/internal/mutex/mutex.go +++ b/internal/mutex/mutex.go @@ -6,7 +6,8 @@ import ( var ( Db = sync.Mutex{} - IndexUpdate = sync.Mutex{} + Index = sync.Mutex{} + People = Busy{} MainWorker = Busy{} SyncWorker = Busy{} ShareWorker = Busy{} diff --git a/internal/query/albums.go b/internal/query/albums.go index 6c5b30225..d0a199a3f 100644 --- a/internal/query/albums.go +++ b/internal/query/albums.go @@ -74,8 +74,8 @@ func AlbumCoverByUID(uid string) (file entity.File, err error) { // UpdateAlbumDates updates album year, month and day based on indexed photo metadata. func UpdateAlbumDates() error { - mutex.IndexUpdate.Lock() - defer mutex.IndexUpdate.Unlock() + mutex.Index.Lock() + defer mutex.Index.Unlock() switch DbDialect() { case MySQL: @@ -93,8 +93,8 @@ func UpdateAlbumDates() error { // UpdateMissingAlbumEntries sets a flag for missing photo album entries. func UpdateMissingAlbumEntries() error { - mutex.IndexUpdate.Lock() - defer mutex.IndexUpdate.Unlock() + mutex.Index.Lock() + defer mutex.Index.Unlock() switch DbDialect() { default: diff --git a/internal/query/covers.go b/internal/query/covers.go index 7a1e45cf3..ae49c0995 100644 --- a/internal/query/covers.go +++ b/internal/query/covers.go @@ -14,8 +14,8 @@ import ( // UpdateAlbumDefaultCovers updates default album cover thumbs. func UpdateAlbumDefaultCovers() (err error) { - mutex.IndexUpdate.Lock() - defer mutex.IndexUpdate.Unlock() + mutex.Index.Lock() + defer mutex.Index.Unlock() start := time.Now() @@ -62,8 +62,8 @@ func UpdateAlbumDefaultCovers() (err error) { // UpdateAlbumFolderCovers updates folder album cover thumbs. func UpdateAlbumFolderCovers() (err error) { - mutex.IndexUpdate.Lock() - defer mutex.IndexUpdate.Unlock() + mutex.Index.Lock() + defer mutex.Index.Unlock() start := time.Now() @@ -110,8 +110,8 @@ func UpdateAlbumFolderCovers() (err error) { // UpdateAlbumMonthCovers updates month album cover thumbs. func UpdateAlbumMonthCovers() (err error) { - mutex.IndexUpdate.Lock() - defer mutex.IndexUpdate.Unlock() + mutex.Index.Lock() + defer mutex.Index.Unlock() start := time.Now() @@ -178,8 +178,8 @@ func UpdateAlbumCovers() (err error) { // UpdateLabelCovers updates label cover thumbs. func UpdateLabelCovers() (err error) { - mutex.IndexUpdate.Lock() - defer mutex.IndexUpdate.Unlock() + mutex.Index.Lock() + defer mutex.Index.Unlock() start := time.Now() @@ -245,8 +245,8 @@ func UpdateLabelCovers() (err error) { // UpdateSubjectCovers updates subject cover thumbs. func UpdateSubjectCovers() (err error) { - mutex.IndexUpdate.Lock() - defer mutex.IndexUpdate.Unlock() + mutex.Index.Lock() + defer mutex.Index.Unlock() start := time.Now() diff --git a/internal/query/faces.go b/internal/query/faces.go index 4ba8dbbc5..8e78c98df 100644 --- a/internal/query/faces.go +++ b/internal/query/faces.go @@ -213,8 +213,8 @@ func ResolveFaceCollisions() (conflicts, resolved int, err error) { // RemovePeopleAndFaces permanently removes all people, faces, and face markers. func RemovePeopleAndFaces() (err error) { - mutex.IndexUpdate.Lock() - defer mutex.IndexUpdate.Unlock() + mutex.Index.Lock() + defer mutex.Index.Unlock() // Delete people. if err = UnscopedDb().Delete(entity.Subject{}, "subj_type = ?", entity.SubjPerson).Error; err != nil { diff --git a/internal/query/folders.go b/internal/query/folders.go index 6d7ce52ef..fdceb9d4e 100644 --- a/internal/query/folders.go +++ b/internal/query/folders.go @@ -64,8 +64,8 @@ func AlbumFolders(threshold int) (folders entity.Folders, err error) { // UpdateFolderDates updates folder year, month and day based on indexed photo metadata. func UpdateFolderDates() error { - mutex.IndexUpdate.Lock() - defer mutex.IndexUpdate.Unlock() + mutex.Index.Lock() + defer mutex.Index.Unlock() switch DbDialect() { case MySQL: diff --git a/internal/query/photo.go b/internal/query/photo.go index c5094c26c..ebeb734fc 100644 --- a/internal/query/photo.go +++ b/internal/query/photo.go @@ -118,8 +118,8 @@ func OrphanPhotos() (photos entity.Photos, err error) { // FixPrimaries tries to set a primary file for photos that have none. func FixPrimaries() error { - mutex.IndexUpdate.Lock() - defer mutex.IndexUpdate.Unlock() + mutex.Index.Lock() + defer mutex.Index.Unlock() start := time.Now() @@ -162,8 +162,8 @@ func FixPrimaries() error { // FlagHiddenPhotos sets the quality score of photos without valid primary file to -1. func FlagHiddenPhotos() error { - mutex.IndexUpdate.Lock() - defer mutex.IndexUpdate.Unlock() + mutex.Index.Lock() + defer mutex.Index.Unlock() start := time.Now() diff --git a/internal/query/purge.go b/internal/query/purge.go index 0b22b327f..56d088b76 100644 --- a/internal/query/purge.go +++ b/internal/query/purge.go @@ -44,8 +44,8 @@ func PurgeOrphans() error { // PurgeOrphanFiles removes files without a photo from the index. func PurgeOrphanFiles() (count int, err error) { - mutex.IndexUpdate.Lock() - defer mutex.IndexUpdate.Unlock() + mutex.Index.Lock() + defer mutex.Index.Unlock() files, err := OrphanFiles() @@ -66,8 +66,8 @@ func PurgeOrphanFiles() (count int, err error) { // PurgeOrphanDuplicates deletes all files from the duplicates table that don't exist in the files table. func PurgeOrphanDuplicates() error { - mutex.IndexUpdate.Lock() - defer mutex.IndexUpdate.Unlock() + mutex.Index.Lock() + defer mutex.Index.Unlock() return UnscopedDb().Delete( entity.Duplicate{}, @@ -76,8 +76,8 @@ func PurgeOrphanDuplicates() error { // PurgeOrphanCountries removes countries without any photos. func PurgeOrphanCountries() error { - mutex.IndexUpdate.Lock() - defer mutex.IndexUpdate.Unlock() + mutex.Index.Lock() + defer mutex.Index.Unlock() entity.FlushCountryCache() switch DbDialect() { @@ -88,8 +88,8 @@ func PurgeOrphanCountries() error { // PurgeOrphanCameras removes cameras without any photos. func PurgeOrphanCameras() error { - mutex.IndexUpdate.Lock() - defer mutex.IndexUpdate.Unlock() + mutex.Index.Lock() + defer mutex.Index.Unlock() entity.FlushCameraCache() switch DbDialect() { @@ -100,8 +100,8 @@ func PurgeOrphanCameras() error { // PurgeOrphanLenses removes cameras without any photos. func PurgeOrphanLenses() error { - mutex.IndexUpdate.Lock() - defer mutex.IndexUpdate.Unlock() + mutex.Index.Lock() + defer mutex.Index.Unlock() entity.FlushLensCache() switch DbDialect() {