new version with admin
This commit is contained in:
parent
0f983eb34d
commit
d08a02b6e7
|
|
@ -16,7 +16,6 @@
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
"jssha": "^3.3.1",
|
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hook-form": "^7.51.0",
|
"react-hook-form": "^7.51.0",
|
||||||
|
|
@ -25,6 +24,7 @@
|
||||||
"react-router-dom": "^6.22.2",
|
"react-router-dom": "^6.22.2",
|
||||||
"react-tag-input": "^6.10.3",
|
"react-tag-input": "^6.10.3",
|
||||||
"tailwind-merge": "^2.2.1",
|
"tailwind-merge": "^2.2.1",
|
||||||
|
"tus-js-client": "^4.3.1",
|
||||||
"zod": "^3.22.4",
|
"zod": "^3.22.4",
|
||||||
"zustand": "^4.5.2"
|
"zustand": "^4.5.2"
|
||||||
},
|
},
|
||||||
|
|
@ -2298,6 +2298,12 @@
|
||||||
"ieee754": "^1.2.1"
|
"ieee754": "^1.2.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/buffer-from": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/call-bind": {
|
"node_modules/call-bind": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
|
||||||
|
|
@ -2434,6 +2440,15 @@
|
||||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/combine-errors": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/combine-errors/-/combine-errors-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-C8ikRNRMygCwaTx+Ek3Yr+OuZzgZjduCOfSQBjbM8V3MfgcjSTeto/GXP6PAwKvJz/v15b7GHZvx5rOlczFw/Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"custom-error-instance": "2.1.1",
|
||||||
|
"lodash.uniqby": "4.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/combined-stream": {
|
"node_modules/combined-stream": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
|
|
@ -2496,6 +2511,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/custom-error-instance": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/custom-error-instance/-/custom-error-instance-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-p6JFxJc3M4OTD2li2qaHkDCw9SfMw82Ldr6OC9Je1aXiGfhx2W8p3GaoeaGrPJTUN9NirTM/KTxHWMUdR1rsUg==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
|
|
@ -3683,6 +3704,12 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/graceful-fs": {
|
||||||
|
"version": "4.2.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||||
|
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/graphemer": {
|
"node_modules/graphemer": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
|
||||||
|
|
@ -4062,6 +4089,18 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-stream": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-string": {
|
"node_modules/is-string": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
|
||||||
|
|
@ -4158,6 +4197,12 @@
|
||||||
"jiti": "bin/jiti.js"
|
"jiti": "bin/jiti.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/js-base64": {
|
||||||
|
"version": "3.7.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.8.tgz",
|
||||||
|
"integrity": "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/js-sha3": {
|
"node_modules/js-sha3": {
|
||||||
"version": "0.8.0",
|
"version": "0.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
|
||||||
|
|
@ -4222,15 +4267,6 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jssha": {
|
|
||||||
"version": "3.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/jssha/-/jssha-3.3.1.tgz",
|
|
||||||
"integrity": "sha512-VCMZj12FCFMQYcFLPRm/0lOBbLi8uM2BhXPTqw3U4YAfs4AZfiApOoBLoN8cQE60Z50m1MYMTQVCfgF/KaCVhQ==",
|
|
||||||
"license": "BSD-3-Clause",
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/keyv": {
|
"node_modules/keyv": {
|
||||||
"version": "4.5.4",
|
"version": "4.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||||
|
|
@ -4294,12 +4330,74 @@
|
||||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
|
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash._baseiteratee": {
|
||||||
|
"version": "4.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash._baseiteratee/-/lodash._baseiteratee-4.7.0.tgz",
|
||||||
|
"integrity": "sha512-nqB9M+wITz0BX/Q2xg6fQ8mLkyfF7MU7eE+MNBNjTHFKeKaZAPEzEg+E8LWxKWf1DQVflNEn9N49yAuqKh2mWQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"lodash._stringtopath": "~4.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lodash._basetostring": {
|
||||||
|
"version": "4.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-4.12.0.tgz",
|
||||||
|
"integrity": "sha512-SwcRIbyxnN6CFEEK4K1y+zuApvWdpQdBHM/swxP962s8HIxPO3alBH5t3m/dl+f4CMUug6sJb7Pww8d13/9WSw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/lodash._baseuniq": {
|
||||||
|
"version": "4.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz",
|
||||||
|
"integrity": "sha512-Ja1YevpHZctlI5beLA7oc5KNDhGcPixFhcqSiORHNsp/1QTv7amAXzw+gu4YOvErqVlMVyIJGgtzeepCnnur0A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"lodash._createset": "~4.0.0",
|
||||||
|
"lodash._root": "~3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lodash._createset": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash._createset/-/lodash._createset-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-GTkC6YMprrJZCYU3zcqZj+jkXkrXzq3IPBcF/fIPpNEAB4hZEtXU8zp/RwKOvZl43NUmwDbyRk3+ZTbeRdEBXA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/lodash._root": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-O0pWuFSK6x4EXhM1dhZ8gchNtG7JMqBtrHdoUFUWXD7dJnNSUze1GuyQr5sOs0aCvgGeI3o/OJW8f4ca7FDxmQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/lodash._stringtopath": {
|
||||||
|
"version": "4.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash._stringtopath/-/lodash._stringtopath-4.8.0.tgz",
|
||||||
|
"integrity": "sha512-SXL66C731p0xPDC5LZg4wI5H+dJo/EO4KTqOMwLYCH3+FmmfAKJEZCm6ohGpI+T1xwsDsJCfL4OnhorllvlTPQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"lodash._basetostring": "~4.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lodash.merge": {
|
"node_modules/lodash.merge": {
|
||||||
"version": "4.6.2",
|
"version": "4.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash.throttle": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/lodash.uniqby": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.5.0.tgz",
|
||||||
|
"integrity": "sha512-IRt7cfTtHy6f1aRVA5n7kT8rgN3N1nH6MOWLcHfpWG2SH19E3JksLK38MktLxZDhlAjCP9jpIXkOnRXlu6oByQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"lodash._baseiteratee": "~4.7.0",
|
||||||
|
"lodash._baseuniq": "~4.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/loose-envify": {
|
"node_modules/loose-envify": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||||
|
|
@ -5053,6 +5151,23 @@
|
||||||
"react-is": "^16.13.1"
|
"react-is": "^16.13.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/proper-lockfile": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"graceful-fs": "^4.2.4",
|
||||||
|
"retry": "^0.12.0",
|
||||||
|
"signal-exit": "^3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/proper-lockfile/node_modules/signal-exit": {
|
||||||
|
"version": "3.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
|
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/proxy-from-env": {
|
"node_modules/proxy-from-env": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
|
|
@ -5067,6 +5182,12 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/querystringify": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/queue-microtask": {
|
"node_modules/queue-microtask": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||||
|
|
@ -5341,6 +5462,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz",
|
||||||
"integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A=="
|
"integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A=="
|
||||||
},
|
},
|
||||||
|
"node_modules/requires-port": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/resolve": {
|
"node_modules/resolve": {
|
||||||
"version": "1.22.8",
|
"version": "1.22.8",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
|
||||||
|
|
@ -5367,6 +5494,15 @@
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/retry": {
|
||||||
|
"version": "0.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
|
||||||
|
"integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/reusify": {
|
"node_modules/reusify": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
||||||
|
|
@ -6040,6 +6176,24 @@
|
||||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
|
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/tus-js-client": {
|
||||||
|
"version": "4.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tus-js-client/-/tus-js-client-4.3.1.tgz",
|
||||||
|
"integrity": "sha512-ZLeYmjrkaU1fUsKbIi8JML52uAocjEZtBx4DKjRrqzrZa0O4MYwT6db+oqePlspV+FxXJAyFBc/L5gwUi2OFsg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"buffer-from": "^1.1.2",
|
||||||
|
"combine-errors": "^3.0.3",
|
||||||
|
"is-stream": "^2.0.0",
|
||||||
|
"js-base64": "^3.7.2",
|
||||||
|
"lodash.throttle": "^4.1.1",
|
||||||
|
"proper-lockfile": "^4.1.2",
|
||||||
|
"url-parse": "^1.5.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tweetnacl": {
|
"node_modules/tweetnacl": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
|
||||||
|
|
@ -6257,6 +6411,16 @@
|
||||||
"punycode": "^2.1.0"
|
"punycode": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/url-parse": {
|
||||||
|
"version": "1.5.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||||
|
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"querystringify": "^2.1.1",
|
||||||
|
"requires-port": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/use-sync-external-store": {
|
"node_modules/use-sync-external-store": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
"react-router-dom": "^6.22.2",
|
"react-router-dom": "^6.22.2",
|
||||||
"react-tag-input": "^6.10.3",
|
"react-tag-input": "^6.10.3",
|
||||||
"tailwind-merge": "^2.2.1",
|
"tailwind-merge": "^2.2.1",
|
||||||
|
"tus-js-client": "^4.3.1",
|
||||||
"zod": "^3.22.4",
|
"zod": "^3.22.4",
|
||||||
"zustand": "^4.5.2"
|
"zustand": "^4.5.2"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,5 @@ export const Routes = {
|
||||||
Root: "/uploadContent",
|
Root: "/uploadContent",
|
||||||
ViewContent: "/viewContent",
|
ViewContent: "/viewContent",
|
||||||
SentryCheck: "/sentryCheck",
|
SentryCheck: "/sentryCheck",
|
||||||
|
Admin: "/admin",
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
||||||
import { Routes } from "~/app/router/constants";
|
import { Routes } from "~/app/router/constants";
|
||||||
import { RootPage } from "~/pages/root";
|
import { RootPage } from "~/pages/root";
|
||||||
import { ViewContentPage } from "~/pages/view-content";
|
import { ViewContentPage } from "~/pages/view-content";
|
||||||
|
import { AdminPage } from "~/pages/admin";
|
||||||
import { ProtectedLayout } from "./protected-layout";
|
import { ProtectedLayout } from "./protected-layout";
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
|
|
@ -29,6 +30,7 @@ const router = createBrowserRouter([
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{ path: Routes.Admin, element: <AdminPage /> },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const AppRouter = () => {
|
export const AppRouter = () => {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -8,7 +8,7 @@ import ReactPlayer from "react-player/lazy";
|
||||||
import { Button } from "~/shared/ui/button";
|
import { Button } from "~/shared/ui/button";
|
||||||
import { useRootStore } from "~/shared/stores/root";
|
import { useRootStore } from "~/shared/stores/root";
|
||||||
import { FormLabel } from "~/shared/ui/form-label";
|
import { FormLabel } from "~/shared/ui/form-label";
|
||||||
import { useUploadFile } from "~/shared/services/file";
|
import { useLegacyUploadFile, useTusUpload } from "~/shared/services/file";
|
||||||
import { Progress } from "~/shared/ui/progress";
|
import { Progress } from "~/shared/ui/progress";
|
||||||
import { useCreateNewContent } from "~/shared/services/content";
|
import { useCreateNewContent } from "~/shared/services/content";
|
||||||
import { BackButton } from "~/shared/ui/back-button";
|
import { BackButton } from "~/shared/ui/back-button";
|
||||||
|
|
@ -31,8 +31,8 @@ export const PresubmitStep = ({ prevStep }: PresubmitStepProps) => {
|
||||||
|
|
||||||
const [isCoverExpanded, setCoverExpanded] = useState(false);
|
const [isCoverExpanded, setCoverExpanded] = useState(false);
|
||||||
|
|
||||||
const uploadCover = useUploadFile();
|
const uploadCover = useLegacyUploadFile();
|
||||||
const uploadFile = useUploadFile();
|
const uploadFile = useTusUpload();
|
||||||
|
|
||||||
const createContent = useCreateNewContent();
|
const createContent = useCreateNewContent();
|
||||||
|
|
||||||
|
|
@ -54,9 +54,21 @@ export const PresubmitStep = ({ prevStep }: PresubmitStepProps) => {
|
||||||
try {
|
try {
|
||||||
let coverUploadResult = { content_id_v1: "" };
|
let coverUploadResult = { content_id_v1: "" };
|
||||||
|
|
||||||
const fileUploadResult = await uploadFile.mutateAsync(
|
const fileUploadResult = await uploadFile.mutateAsync({
|
||||||
rootStore.file as File,
|
file: rootStore.file as File,
|
||||||
);
|
metadata: {
|
||||||
|
title: rootStore.name,
|
||||||
|
description: "",
|
||||||
|
content_type: rootStore.file?.type || "application/octet-stream",
|
||||||
|
preview_start_ms: 0,
|
||||||
|
preview_duration_ms: 30000,
|
||||||
|
downloadable: rootStore.allowDwnld ? "1" : "0",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!fileUploadResult.encryptedCid) {
|
||||||
|
throw new Error("Tus upload did not return encrypted cid");
|
||||||
|
}
|
||||||
|
|
||||||
if (rootStore.allowCover && rootStore.cover) {
|
if (rootStore.allowCover && rootStore.cover) {
|
||||||
coverUploadResult = await uploadCover.mutateAsync(
|
coverUploadResult = await uploadCover.mutateAsync(
|
||||||
|
|
@ -76,7 +88,7 @@ export const PresubmitStep = ({ prevStep }: PresubmitStepProps) => {
|
||||||
// следует отметить что вы должны еще откомментровать AuthorsStep в RootPage
|
// следует отметить что вы должны еще откомментровать AuthorsStep в RootPage
|
||||||
// authors: rootStore.authors,
|
// authors: rootStore.authors,
|
||||||
downloadable: rootStore.allowDwnld,
|
downloadable: rootStore.allowDwnld,
|
||||||
content: fileUploadResult.content_id_v1,
|
content: fileUploadResult.encryptedCid,
|
||||||
image: coverUploadResult.content_id_v1,
|
image: coverUploadResult.content_id_v1,
|
||||||
price: String(rootStore.price * 10 ** 9),
|
price: String(rootStore.price * 10 ** 9),
|
||||||
hashtags: rootStore.hashtags,
|
hashtags: rootStore.hashtags,
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,102 @@
|
||||||
import axios from 'axios';
|
import axios, { AxiosError, InternalAxiosRequestConfig } from 'axios';
|
||||||
|
|
||||||
export const APP_API_BASE_URL = import.meta.env.VITE_API_BASE_URL;
|
import { getAdminAuthSnapshot } from '~/shared/libs/admin-auth';
|
||||||
|
|
||||||
export const request = axios.create({
|
const API_BASE_PATH = '/api/v1';
|
||||||
baseURL: APP_API_BASE_URL,
|
const API_ENDPOINTS = [
|
||||||
|
'https://my-public-node-8.projscale.dev',
|
||||||
|
'http://localhost:3000',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const API_BASE_URLS = API_ENDPOINTS.map((endpoint) => {
|
||||||
|
return `${endpoint.replace(/\/$/, '')}${API_BASE_PATH}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
request.interceptors.request.use((config) => {
|
const ENDPOINT_INDEX_KEY = '__apiEndpointIndex__';
|
||||||
const auth_v1_token = localStorage.getItem('auth_v1_token');
|
|
||||||
|
|
||||||
if (auth_v1_token) {
|
const isBrowser = typeof window !== 'undefined';
|
||||||
config.headers.Authorization = auth_v1_token;
|
|
||||||
|
const readStorage = (key: string) => {
|
||||||
|
if (!isBrowser) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
return window.localStorage.getItem(key);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to access localStorage', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
type RequestConfig = InternalAxiosRequestConfig & {
|
||||||
|
[ENDPOINT_INDEX_KEY]?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const request = axios.create({ withCredentials: true });
|
||||||
|
|
||||||
|
request.interceptors.request.use((config: RequestConfig) => {
|
||||||
|
config.headers = config.headers ?? {};
|
||||||
|
const headers = config.headers as Record<string, any>;
|
||||||
|
const authToken = readStorage('auth_v1_token');
|
||||||
|
|
||||||
|
if (authToken && !headers.Authorization) {
|
||||||
|
headers.Authorization = authToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlPath = config.url ?? '';
|
||||||
|
if (urlPath.startsWith('/admin')) {
|
||||||
|
const { token, headerName } = getAdminAuthSnapshot();
|
||||||
|
if (token) {
|
||||||
|
const headerKey = headerName || 'X-Admin-Token';
|
||||||
|
if (!headers[headerKey]) {
|
||||||
|
headers[headerKey] = token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasCustomBaseUrl = Boolean(config.baseURL && !API_BASE_URLS.includes(config.baseURL));
|
||||||
|
if (hasCustomBaseUrl) {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
const attempt = typeof config[ENDPOINT_INDEX_KEY] === 'number' ? (config[ENDPOINT_INDEX_KEY] as number) : 0;
|
||||||
|
config[ENDPOINT_INDEX_KEY] = attempt;
|
||||||
|
config.baseURL = API_BASE_URLS[attempt] ?? API_BASE_URLS[0];
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
request.interceptors.response.use(
|
||||||
|
(response) => response,
|
||||||
|
async (error: AxiosError) => {
|
||||||
|
const config = error.config as RequestConfig | undefined;
|
||||||
|
|
||||||
|
if (!config) {
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasCustomBaseUrl = Boolean(config.baseURL && !API_BASE_URLS.includes(config.baseURL));
|
||||||
|
if (hasCustomBaseUrl) {
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAbortError = error.code === 'ERR_CANCELED' || error.message === 'canceled';
|
||||||
|
const isNetworkError = !error.response && !isAbortError;
|
||||||
|
|
||||||
|
if (!isNetworkError) {
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentIndex = typeof config[ENDPOINT_INDEX_KEY] === 'number' ? (config[ENDPOINT_INDEX_KEY] as number) : 0;
|
||||||
|
const nextIndex = currentIndex + 1;
|
||||||
|
|
||||||
|
if (nextIndex >= API_BASE_URLS.length) {
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
config[ENDPOINT_INDEX_KEY] = nextIndex;
|
||||||
|
config.baseURL = API_BASE_URLS[nextIndex];
|
||||||
|
|
||||||
|
return request(config);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,421 @@
|
||||||
|
import axios, { AxiosError } from 'axios';
|
||||||
|
import {
|
||||||
|
useMutation,
|
||||||
|
useQuery,
|
||||||
|
UseMutationOptions,
|
||||||
|
UseQueryOptions,
|
||||||
|
} from 'react-query';
|
||||||
|
|
||||||
|
import { request } from "~/shared/libs";
|
||||||
|
import { clearAdminAuth, persistAdminAuth } from "~/shared/libs/admin-auth";
|
||||||
|
|
||||||
|
export type AdminServiceState = {
|
||||||
|
name: string;
|
||||||
|
status?: string;
|
||||||
|
last_reported_seconds: number | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AdminOverviewResponse = {
|
||||||
|
project: {
|
||||||
|
host: string | null;
|
||||||
|
name: string;
|
||||||
|
privacy: string;
|
||||||
|
};
|
||||||
|
codebase: {
|
||||||
|
branch: string | null;
|
||||||
|
commit: string | null;
|
||||||
|
};
|
||||||
|
node: {
|
||||||
|
id: string;
|
||||||
|
service_wallet: string;
|
||||||
|
ton_master: string;
|
||||||
|
};
|
||||||
|
runtime: {
|
||||||
|
python: string;
|
||||||
|
implementation: string;
|
||||||
|
platform: string;
|
||||||
|
utc_now: string;
|
||||||
|
};
|
||||||
|
ipfs: Record<string, unknown> & {
|
||||||
|
identity?: Record<string, unknown> | { error: string };
|
||||||
|
bitswap?: Record<string, unknown> | { error: string };
|
||||||
|
repo?: Record<string, unknown> | { error: string };
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
encrypted_total: number;
|
||||||
|
upload_sessions_total: number;
|
||||||
|
derivatives_ready: number;
|
||||||
|
};
|
||||||
|
ton: {
|
||||||
|
host: string | null;
|
||||||
|
api_key_configured: boolean;
|
||||||
|
testnet: boolean;
|
||||||
|
};
|
||||||
|
services: AdminServiceState[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AdminStorageResponse = {
|
||||||
|
directories: Array<{
|
||||||
|
label: string;
|
||||||
|
path: string;
|
||||||
|
exists: boolean;
|
||||||
|
file_count: number;
|
||||||
|
size_bytes: number;
|
||||||
|
}>;
|
||||||
|
disk: null | {
|
||||||
|
path: string;
|
||||||
|
total_bytes: number;
|
||||||
|
used_bytes: number;
|
||||||
|
free_bytes: number;
|
||||||
|
percent_used: number | null;
|
||||||
|
};
|
||||||
|
derivatives: {
|
||||||
|
ready: number;
|
||||||
|
processing: number;
|
||||||
|
pending: number;
|
||||||
|
failed: number;
|
||||||
|
total_bytes: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AdminUploadsResponse = {
|
||||||
|
total: number;
|
||||||
|
states: Record<string, number>;
|
||||||
|
recent: Array<{
|
||||||
|
id: string;
|
||||||
|
filename: string | null;
|
||||||
|
size_bytes: number | null;
|
||||||
|
state: string;
|
||||||
|
encrypted_cid: string | null;
|
||||||
|
error: string | null;
|
||||||
|
updated_at: string;
|
||||||
|
created_at: string;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AdminSystemResponse = {
|
||||||
|
env: Record<string, string | null | undefined>;
|
||||||
|
service_config: Array<{
|
||||||
|
key: string;
|
||||||
|
value: string | null;
|
||||||
|
raw: string | null;
|
||||||
|
}>;
|
||||||
|
services: AdminServiceState[];
|
||||||
|
blockchain_tasks: Record<string, number>;
|
||||||
|
latest_index_items: Array<{
|
||||||
|
encrypted_cid: string | null;
|
||||||
|
updated_at: string;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AdminBlockchainResponse = {
|
||||||
|
counts: Record<string, number>;
|
||||||
|
recent: Array<{
|
||||||
|
id: string;
|
||||||
|
destination: string | null;
|
||||||
|
amount: string | null;
|
||||||
|
status: string;
|
||||||
|
epoch: number | null;
|
||||||
|
seqno: number | null;
|
||||||
|
transaction_hash: string | null;
|
||||||
|
updated: string;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AdminNodesResponse = {
|
||||||
|
items: Array<{
|
||||||
|
ip: string | null;
|
||||||
|
port: number | null;
|
||||||
|
public_key: string | null;
|
||||||
|
role: 'trusted' | 'read-only' | 'deny';
|
||||||
|
version?: string | null;
|
||||||
|
last_seen?: string | null;
|
||||||
|
notes?: string | null;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AdminStatusResponse = {
|
||||||
|
ipfs: {
|
||||||
|
bitswap: Record<string, unknown>;
|
||||||
|
repo: Record<string, unknown>;
|
||||||
|
};
|
||||||
|
pin_counts: Record<string, number>;
|
||||||
|
derivatives: {
|
||||||
|
ready: number;
|
||||||
|
processing: number;
|
||||||
|
pending: number;
|
||||||
|
failed: number;
|
||||||
|
total_bytes: number;
|
||||||
|
};
|
||||||
|
convert_backlog: number;
|
||||||
|
limits: {
|
||||||
|
DERIVATIVE_CACHE_MAX_GB: number;
|
||||||
|
DERIVATIVE_CACHE_TTL_DAYS: number;
|
||||||
|
SYNC_MAX_CONCURRENT_PINS: number;
|
||||||
|
SYNC_DISK_LOW_WATERMARK_PCT: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AdminCacheSetLimitsPayload = {
|
||||||
|
max_gb: number;
|
||||||
|
ttl_days: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AdminCacheCleanupPayload =
|
||||||
|
| {
|
||||||
|
mode: 'ttl';
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
mode?: 'fit';
|
||||||
|
max_gb: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AdminSyncSetLimitsPayload = {
|
||||||
|
max_concurrent_pins: number;
|
||||||
|
disk_low_watermark_pct: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AdminNodeSetRolePayload = {
|
||||||
|
public_key?: string;
|
||||||
|
host?: string;
|
||||||
|
role: 'trusted' | 'read-only' | 'deny';
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultQueryOptions = {
|
||||||
|
staleTime: 30_000,
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
type QueryOptions<TData, TQueryKey extends readonly unknown[]> = UseQueryOptions<
|
||||||
|
TData,
|
||||||
|
AxiosError,
|
||||||
|
TData,
|
||||||
|
TQueryKey
|
||||||
|
>;
|
||||||
|
|
||||||
|
type MutationOptions<TData, TVariables> = UseMutationOptions<
|
||||||
|
TData,
|
||||||
|
AxiosError,
|
||||||
|
TVariables
|
||||||
|
>;
|
||||||
|
|
||||||
|
export const useAdminOverview = (
|
||||||
|
options?: QueryOptions<AdminOverviewResponse, ['admin', 'overview']>,
|
||||||
|
) => {
|
||||||
|
return useQuery<AdminOverviewResponse, AxiosError, AdminOverviewResponse, ['admin', 'overview']>(
|
||||||
|
['admin', 'overview'],
|
||||||
|
async () => {
|
||||||
|
const { data } = await request.get<AdminOverviewResponse>('/admin.overview');
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...defaultQueryOptions,
|
||||||
|
...options,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAdminStorage = (
|
||||||
|
options?: QueryOptions<AdminStorageResponse, ['admin', 'storage']>,
|
||||||
|
) => {
|
||||||
|
return useQuery<AdminStorageResponse, AxiosError, AdminStorageResponse, ['admin', 'storage']>(
|
||||||
|
['admin', 'storage'],
|
||||||
|
async () => {
|
||||||
|
const { data } = await request.get<AdminStorageResponse>('/admin.storage');
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...defaultQueryOptions,
|
||||||
|
...options,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAdminUploads = (
|
||||||
|
options?: QueryOptions<AdminUploadsResponse, ['admin', 'uploads']>,
|
||||||
|
) => {
|
||||||
|
return useQuery<AdminUploadsResponse, AxiosError, AdminUploadsResponse, ['admin', 'uploads']>(
|
||||||
|
['admin', 'uploads'],
|
||||||
|
async () => {
|
||||||
|
const { data } = await request.get<AdminUploadsResponse>('/admin.uploads');
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...defaultQueryOptions,
|
||||||
|
...options,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAdminSystem = (
|
||||||
|
options?: QueryOptions<AdminSystemResponse, ['admin', 'system']>,
|
||||||
|
) => {
|
||||||
|
return useQuery<AdminSystemResponse, AxiosError, AdminSystemResponse, ['admin', 'system']>(
|
||||||
|
['admin', 'system'],
|
||||||
|
async () => {
|
||||||
|
const { data } = await request.get<AdminSystemResponse>('/admin.system');
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...defaultQueryOptions,
|
||||||
|
...options,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAdminBlockchain = (
|
||||||
|
options?: QueryOptions<AdminBlockchainResponse, ['admin', 'blockchain']>,
|
||||||
|
) => {
|
||||||
|
return useQuery<AdminBlockchainResponse, AxiosError, AdminBlockchainResponse, ['admin', 'blockchain']>(
|
||||||
|
['admin', 'blockchain'],
|
||||||
|
async () => {
|
||||||
|
const { data } = await request.get<AdminBlockchainResponse>('/admin.blockchain');
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...defaultQueryOptions,
|
||||||
|
...options,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAdminNodes = (
|
||||||
|
options?: QueryOptions<AdminNodesResponse, ['admin', 'nodes']>,
|
||||||
|
) => {
|
||||||
|
return useQuery<AdminNodesResponse, AxiosError, AdminNodesResponse, ['admin', 'nodes']>(
|
||||||
|
['admin', 'nodes'],
|
||||||
|
async () => {
|
||||||
|
const { data } = await request.get<AdminNodesResponse>('/admin.nodes');
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...defaultQueryOptions,
|
||||||
|
...options,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAdminStatus = (
|
||||||
|
options?: QueryOptions<AdminStatusResponse, ['admin', 'status']>,
|
||||||
|
) => {
|
||||||
|
return useQuery<AdminStatusResponse, AxiosError, AdminStatusResponse, ['admin', 'status']>(
|
||||||
|
['admin', 'status'],
|
||||||
|
async () => {
|
||||||
|
const { data } = await request.get<AdminStatusResponse>('/admin.status');
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...defaultQueryOptions,
|
||||||
|
...options,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AdminLoginResponse = {
|
||||||
|
ok: true;
|
||||||
|
cookie_name?: string;
|
||||||
|
header_name?: string;
|
||||||
|
max_age?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAdminLogin = (
|
||||||
|
options?: MutationOptions<AdminLoginResponse, { secret: string }>,
|
||||||
|
) => {
|
||||||
|
return useMutation<AdminLoginResponse, AxiosError, { secret: string }>(
|
||||||
|
async ({ secret }) => {
|
||||||
|
const { data } = await request.post<AdminLoginResponse>('/admin.login', { secret });
|
||||||
|
persistAdminAuth({
|
||||||
|
token: secret,
|
||||||
|
headerName: data.header_name,
|
||||||
|
cookieName: data.cookie_name,
|
||||||
|
maxAge: data.max_age,
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAdminLogout = (
|
||||||
|
options?: MutationOptions<{ ok: true }, void>,
|
||||||
|
) => {
|
||||||
|
return useMutation<{ ok: true }, AxiosError, void>(
|
||||||
|
async () => {
|
||||||
|
const { data } = await request.post<{ ok: true }>('/admin.logout');
|
||||||
|
clearAdminAuth();
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAdminCacheSetLimits = (
|
||||||
|
options?: MutationOptions<{ ok: true }, AdminCacheSetLimitsPayload>,
|
||||||
|
) => {
|
||||||
|
return useMutation<{ ok: true }, AxiosError, AdminCacheSetLimitsPayload>(
|
||||||
|
async (payload) => {
|
||||||
|
const { data } = await request.post<{ ok: true }>('/admin.cache.setLimits', payload);
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAdminCacheCleanup = (
|
||||||
|
options?: MutationOptions<{ ok: true; removed?: number }, AdminCacheCleanupPayload>,
|
||||||
|
) => {
|
||||||
|
return useMutation<{ ok: true; removed?: number }, AxiosError, AdminCacheCleanupPayload>(
|
||||||
|
async (payload) => {
|
||||||
|
const { data } = await request.post<{ ok: true; removed?: number }>('/admin.cache.cleanup', payload);
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAdminSyncSetLimits = (
|
||||||
|
options?: MutationOptions<{ ok: true }, AdminSyncSetLimitsPayload>,
|
||||||
|
) => {
|
||||||
|
return useMutation<{ ok: true }, AxiosError, AdminSyncSetLimitsPayload>(
|
||||||
|
async (payload) => {
|
||||||
|
const { data } = await request.post<{ ok: true }>('/admin.sync.setLimits', payload);
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAdminNodeSetRole = (
|
||||||
|
options?: MutationOptions<{ ok: true; node: { ip: string | null; public_key: string | null; role: string } }, AdminNodeSetRolePayload>,
|
||||||
|
) => {
|
||||||
|
return useMutation<
|
||||||
|
{ ok: true; node: { ip: string | null; public_key: string | null; role: string } },
|
||||||
|
AxiosError,
|
||||||
|
AdminNodeSetRolePayload
|
||||||
|
>(
|
||||||
|
async (payload) => {
|
||||||
|
const { data } = await request.post<{ ok: true; node: { ip: string | null; public_key: string | null; role: string } }>(
|
||||||
|
'/admin.node.setRole',
|
||||||
|
payload,
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAdminSyncLimits = () => {
|
||||||
|
const cacheSetLimits = useAdminCacheSetLimits();
|
||||||
|
const syncSetLimits = useAdminSyncSetLimits();
|
||||||
|
return { cacheSetLimits, syncSetLimits };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isUnauthorizedError = (error: unknown) => {
|
||||||
|
if (!error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (axios.isAxiosError(error)) {
|
||||||
|
return error.response?.status === 401;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
@ -1,118 +1,397 @@
|
||||||
import { useMutation } from "react-query";
|
import { useMutation } from "react-query";
|
||||||
import { useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
|
import { Upload } from "tus-js-client";
|
||||||
|
|
||||||
import { request } from "~/shared/libs";
|
import { request } from "~/shared/libs";
|
||||||
|
|
||||||
const STORAGE_API_URL = import.meta.env.VITE_API_BASE_STORAGE_URL;
|
const STORAGE_API_URL = import.meta.env.VITE_API_BASE_STORAGE_URL;
|
||||||
const MAX_CHUNK_SIZE = 80 * 1024 * 1024; // 80 MB
|
const MAX_CHUNK_SIZE = 80 * 1024 * 1024; // 80 MB
|
||||||
|
const TUS_ENDPOINT = import.meta.env.VITE_TUS_ENDPOINT?.trim();
|
||||||
|
|
||||||
export const useUploadFile = () => {
|
const TUS_STATUS_POLL_INTERVAL_MS = 2000;
|
||||||
|
const TUS_STATUS_POLL_TIMEOUT_MS = 5 * 60 * 1000;
|
||||||
|
|
||||||
|
const sleep = (ms: number) => new Promise<void>((resolve) => {
|
||||||
|
setTimeout(resolve, ms);
|
||||||
|
});
|
||||||
|
|
||||||
|
const normalizeError = (error: unknown, fallbackMessage: string) => {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof error === "string") {
|
||||||
|
return new Error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error && typeof error === "object" && "message" in error) {
|
||||||
|
const message = (error as { message?: unknown }).message;
|
||||||
|
if (typeof message === "string") {
|
||||||
|
return new Error(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Error(fallbackMessage);
|
||||||
|
};
|
||||||
|
|
||||||
|
const sanitizeMetadataKey = (key: string) => {
|
||||||
|
return key.replace(/[^A-Za-z0-9_.-]/g, "_");
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalizeMetadata = (
|
||||||
|
metadata?: Record<string, string | number | boolean | undefined>,
|
||||||
|
) => {
|
||||||
|
if (!metadata) {
|
||||||
|
return {} as Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.entries(metadata).reduce((acc, [rawKey, rawValue]) => {
|
||||||
|
if (rawValue === undefined || rawValue === null) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = sanitizeMetadataKey(rawKey);
|
||||||
|
if (!key) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[key] = String(rawValue);
|
||||||
|
return acc;
|
||||||
|
}, {} as Record<string, string>);
|
||||||
|
};
|
||||||
|
|
||||||
|
const extractUploadId = (uploadUrl: string) => {
|
||||||
|
try {
|
||||||
|
const url = new URL(uploadUrl);
|
||||||
|
const parts = url.pathname.split("/").filter(Boolean);
|
||||||
|
const uploadId = parts[parts.length - 1];
|
||||||
|
if (!uploadId) {
|
||||||
|
throw new Error("Empty upload id");
|
||||||
|
}
|
||||||
|
return uploadId;
|
||||||
|
} catch (error) {
|
||||||
|
const normalized = uploadUrl.split("/").filter(Boolean);
|
||||||
|
const uploadId = normalized[normalized.length - 1];
|
||||||
|
if (!uploadId) {
|
||||||
|
throw new Error("Unable to extract upload id from tus Location");
|
||||||
|
}
|
||||||
|
return uploadId;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
type UploadSessionState = "uploading" | "processing" | "pinned" | "failed";
|
||||||
|
|
||||||
|
type UploadStatusResponse = {
|
||||||
|
id: string;
|
||||||
|
state: UploadSessionState;
|
||||||
|
encrypted_cid?: string | null;
|
||||||
|
size_bytes?: number | null;
|
||||||
|
error?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const pollUploadStatus = async (
|
||||||
|
uploadId: string,
|
||||||
|
signal?: AbortSignal,
|
||||||
|
): Promise<UploadStatusResponse> => {
|
||||||
|
const startedAt = Date.now();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (signal?.aborted) {
|
||||||
|
throw new DOMException("Tus status polling aborted", "AbortError");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { data } = await request.get<UploadStatusResponse>(
|
||||||
|
`/upload.status/${uploadId}`,
|
||||||
|
{
|
||||||
|
headers: { "Cache-Control": "no-store" },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (data.state === "failed") {
|
||||||
|
throw new Error(data.error || "Tus upload failed on server");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.state === "pinned" && data.encrypted_cid) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const maybeAxios = error as { response?: { status?: number } };
|
||||||
|
if (maybeAxios?.response?.status === 404) {
|
||||||
|
// Hook has not recorded the upload session yet; continue polling.
|
||||||
|
} else if (error instanceof DOMException && error.name === "AbortError") {
|
||||||
|
throw error;
|
||||||
|
} else {
|
||||||
|
throw normalizeError(error, "Tus status polling failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Date.now() - startedAt > TUS_STATUS_POLL_TIMEOUT_MS) {
|
||||||
|
throw new Error("Timed out waiting for tus upload finalization");
|
||||||
|
}
|
||||||
|
|
||||||
|
await sleep(TUS_STATUS_POLL_INTERVAL_MS);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
type TusUploadArgs = {
|
||||||
|
file: File;
|
||||||
|
metadata?: Record<string, string | number | boolean | undefined>;
|
||||||
|
signal?: AbortSignal;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TusUploadResult = {
|
||||||
|
kind: "tus";
|
||||||
|
uploadId: string;
|
||||||
|
encryptedCid: string;
|
||||||
|
sizeBytes?: number;
|
||||||
|
state: UploadSessionState;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useTusUpload = () => {
|
||||||
|
const [uploadProgress, setUploadProgress] = useState(0);
|
||||||
|
const [isUploading, setIsUploading] = useState(false);
|
||||||
|
const [uploadError, setUploadError] = useState<Error | null>(null);
|
||||||
|
const activeUploadRef = useRef<Upload | null>(null);
|
||||||
|
|
||||||
|
const mutation = useMutation<TusUploadResult, Error, TusUploadArgs>(
|
||||||
|
["upload-file", "tus"],
|
||||||
|
async ({ file, metadata, signal }) => {
|
||||||
|
if (!file) {
|
||||||
|
throw new Error("File is required for tus upload");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TUS_ENDPOINT) {
|
||||||
|
throw new Error("Tus endpoint is not configured");
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsUploading(true);
|
||||||
|
setUploadProgress(0);
|
||||||
|
setUploadError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await new Promise<TusUploadResult>((resolve, reject) => {
|
||||||
|
const token = localStorage.getItem("auth_v1_token") ?? "";
|
||||||
|
const normalizedMeta = normalizeMetadata(metadata);
|
||||||
|
const headers: Record<string, string> = { "Cache-Control": "no-store" };
|
||||||
|
if (token) {
|
||||||
|
headers.Authorization = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
const upload = new Upload(file, {
|
||||||
|
endpoint: TUS_ENDPOINT,
|
||||||
|
metadata: {
|
||||||
|
filename: file.name,
|
||||||
|
content_type: file.type || "application/octet-stream",
|
||||||
|
...normalizedMeta,
|
||||||
|
},
|
||||||
|
headers,
|
||||||
|
retryDelays: [0, 1000, 3000, 5000],
|
||||||
|
removeFingerprintOnSuccess: true,
|
||||||
|
uploadDataDuringCreation: true,
|
||||||
|
onError: (err) => {
|
||||||
|
const normalized = normalizeError(err, "Tus upload failed");
|
||||||
|
setUploadError(normalized);
|
||||||
|
setIsUploading(false);
|
||||||
|
setUploadProgress(0);
|
||||||
|
activeUploadRef.current = null;
|
||||||
|
reject(normalized);
|
||||||
|
},
|
||||||
|
onProgress: (bytesUploaded, bytesTotal) => {
|
||||||
|
const percent = bytesTotal
|
||||||
|
? Math.floor((bytesUploaded / bytesTotal) * 100)
|
||||||
|
: 0;
|
||||||
|
setUploadProgress(Math.min(99, Math.max(0, percent)));
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
setUploadProgress(99);
|
||||||
|
const uploadUrl = upload.url;
|
||||||
|
if (!uploadUrl) {
|
||||||
|
throw new Error("Tus upload finished without Location header");
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadId = extractUploadId(uploadUrl);
|
||||||
|
const status = await pollUploadStatus(uploadId, signal);
|
||||||
|
const encryptedCid = status.encrypted_cid;
|
||||||
|
if (!encryptedCid) {
|
||||||
|
throw new Error("Tus upload finalized without encrypted CID");
|
||||||
|
}
|
||||||
|
|
||||||
|
setUploadProgress(100);
|
||||||
|
setIsUploading(false);
|
||||||
|
activeUploadRef.current = null;
|
||||||
|
|
||||||
|
resolve({
|
||||||
|
kind: "tus",
|
||||||
|
uploadId,
|
||||||
|
encryptedCid,
|
||||||
|
sizeBytes: status.size_bytes ?? undefined,
|
||||||
|
state: status.state,
|
||||||
|
});
|
||||||
|
} catch (statusError) {
|
||||||
|
const normalized = normalizeError(
|
||||||
|
statusError,
|
||||||
|
"Failed to finalize tus upload",
|
||||||
|
);
|
||||||
|
setUploadError(normalized);
|
||||||
|
setIsUploading(false);
|
||||||
|
activeUploadRef.current = null;
|
||||||
|
reject(normalized);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
activeUploadRef.current = upload;
|
||||||
|
|
||||||
|
if (signal) {
|
||||||
|
signal.addEventListener(
|
||||||
|
"abort",
|
||||||
|
() => {
|
||||||
|
upload.abort();
|
||||||
|
const abortError = new DOMException(
|
||||||
|
"Tus upload aborted",
|
||||||
|
"AbortError",
|
||||||
|
);
|
||||||
|
setUploadError(abortError);
|
||||||
|
setIsUploading(false);
|
||||||
|
activeUploadRef.current = null;
|
||||||
|
reject(abortError);
|
||||||
|
},
|
||||||
|
{ once: true },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
upload
|
||||||
|
.findPreviousUploads()
|
||||||
|
.then((previousUploads) => {
|
||||||
|
if (previousUploads.length > 0) {
|
||||||
|
upload.resumeFromPreviousUpload(previousUploads[0]);
|
||||||
|
}
|
||||||
|
upload.start();
|
||||||
|
})
|
||||||
|
.catch((resumeError) => {
|
||||||
|
const normalized = normalizeError(
|
||||||
|
resumeError,
|
||||||
|
"Failed to resume tus upload",
|
||||||
|
);
|
||||||
|
setUploadError(normalized);
|
||||||
|
setIsUploading(false);
|
||||||
|
activeUploadRef.current = null;
|
||||||
|
reject(normalized);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} finally {
|
||||||
|
activeUploadRef.current = null;
|
||||||
|
setIsUploading(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const resetUploadError = () => setUploadError(null);
|
||||||
|
|
||||||
|
const abortUpload = () => {
|
||||||
|
if (activeUploadRef.current) {
|
||||||
|
activeUploadRef.current.abort();
|
||||||
|
activeUploadRef.current = null;
|
||||||
|
setIsUploading(false);
|
||||||
|
setUploadProgress(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...mutation,
|
||||||
|
uploadProgress,
|
||||||
|
isUploading,
|
||||||
|
uploadError,
|
||||||
|
resetUploadError,
|
||||||
|
abortUpload,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useLegacyUploadFile = () => {
|
||||||
const [uploadProgress, setUploadProgress] = useState(0);
|
const [uploadProgress, setUploadProgress] = useState(0);
|
||||||
const [isUploading, setIsUploading] = useState(false);
|
const [isUploading, setIsUploading] = useState(false);
|
||||||
const [uploadError, setUploadError] = useState<Error | null>(null);
|
const [uploadError, setUploadError] = useState<Error | null>(null);
|
||||||
|
|
||||||
const mutation = useMutation(["upload-file"], async (file: File) => {
|
const mutation = useMutation(["upload-file", "legacy"], async (file: File) => {
|
||||||
console.log(`Начинаем загрузку файла: ${file.name} (${file.size} байт)`);
|
|
||||||
setIsUploading(true);
|
setIsUploading(true);
|
||||||
setUploadProgress(0);
|
setUploadProgress(0);
|
||||||
setUploadError(null); // Сбрасываем ошибку перед началом новой загрузки
|
setUploadError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Для маленьких файлов используем обычную загрузку, но с теми же заголовками
|
|
||||||
if (file.size <= MAX_CHUNK_SIZE) {
|
if (file.size <= MAX_CHUNK_SIZE) {
|
||||||
console.log("Используем обычную загрузку (файл <= MAX_CHUNK_SIZE)");
|
|
||||||
|
|
||||||
// Подготавливаем заголовки - такие же, как для чанковой загрузки
|
|
||||||
const headers: Record<string, string> = {
|
const headers: Record<string, string> = {
|
||||||
"X-File-Name": btoa(unescape(encodeURIComponent(file.name))), // Имя файла в base64
|
"X-File-Name": btoa(unescape(encodeURIComponent(file.name))),
|
||||||
"X-Chunk-Start": "0", // Начинаем с позиции 0
|
"X-Chunk-Start": "0",
|
||||||
"Content-Type": file.type || "application/octet-stream",
|
"Content-Type": file.type || "application/octet-stream",
|
||||||
"X-Last-Chunk": "1" // Это единственный и последний чанк
|
"X-Last-Chunk": "1",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Добавляем заголовок авторизации
|
const authToken = localStorage.getItem("auth_v1_token");
|
||||||
const authToken = localStorage.getItem('auth_v1_token');
|
|
||||||
if (authToken) {
|
if (authToken) {
|
||||||
headers["Authorization"] = authToken;
|
headers.Authorization = authToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Заголовки запроса:", headers);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await request.post<{
|
const response = await request.post<{
|
||||||
upload_id?: string;
|
upload_id?: string;
|
||||||
content_sha256?: string;
|
content_sha256?: string;
|
||||||
content_id?: string;
|
content_id?: string;
|
||||||
content_id_v1?: string;
|
content_id_v1?: string;
|
||||||
content_url?: string;
|
content_url?: string;
|
||||||
}>("", file, { // Отправляем файл напрямую вместо FormData
|
}>("", file, {
|
||||||
baseURL: STORAGE_API_URL,
|
baseURL: STORAGE_API_URL,
|
||||||
headers,
|
headers,
|
||||||
onUploadProgress: (progressEvent) => {
|
onUploadProgress: (progressEvent) => {
|
||||||
|
const total = progressEvent?.total ?? file.size;
|
||||||
const percentCompleted = Math.round(
|
const percentCompleted = Math.round(
|
||||||
(progressEvent.loaded * 100) / (progressEvent?.total as number || file.size)
|
(progressEvent.loaded * 100) / total,
|
||||||
);
|
);
|
||||||
setUploadProgress(Math.min(99, percentCompleted));
|
setUploadProgress(Math.min(99, percentCompleted));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("Ответ на обычную загрузку:", response.data);
|
|
||||||
setUploadProgress(100);
|
setUploadProgress(100);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content_sha256: response.data.content_sha256 || "",
|
content_sha256: response.data.content_sha256 || "",
|
||||||
content_id_v1: response.data.content_id_v1 || response.data.content_id || "",
|
content_id_v1:
|
||||||
content_url: response.data.content_url || ""
|
response.data.content_id_v1 || response.data.content_id || "",
|
||||||
|
content_url: response.data.content_url || "",
|
||||||
};
|
};
|
||||||
} catch (error) {
|
|
||||||
console.error("Ошибка при обычной загрузке:", error);
|
|
||||||
setUploadError(error instanceof Error ? error : new Error('Ошибка при загрузке файла'));
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Для больших файлов используем чанковую загрузку
|
|
||||||
console.log("Используем чанкированную загрузку (файл > MAX_CHUNK_SIZE)");
|
|
||||||
|
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
let uploadId: string | null = null;
|
let uploadId: string | null = null;
|
||||||
let chunkNumber = 0;
|
|
||||||
|
|
||||||
// Загружаем файл по чанкам
|
|
||||||
while (offset < file.size) {
|
while (offset < file.size) {
|
||||||
chunkNumber++;
|
|
||||||
const chunkEnd = Math.min(offset + MAX_CHUNK_SIZE, file.size);
|
const chunkEnd = Math.min(offset + MAX_CHUNK_SIZE, file.size);
|
||||||
const chunk = file.slice(offset, chunkEnd);
|
const chunk = file.slice(offset, chunkEnd);
|
||||||
console.log(`Загрузка чанка #${chunkNumber} начиная с байта ${offset}`);
|
|
||||||
|
|
||||||
// Определяем, является ли текущий чанк последним
|
|
||||||
const isLastChunk = chunkEnd === file.size;
|
const isLastChunk = chunkEnd === file.size;
|
||||||
|
|
||||||
// Подготавливаем заголовки
|
|
||||||
const headers: Record<string, string> = {
|
const headers: Record<string, string> = {
|
||||||
"X-File-Name": btoa(unescape(encodeURIComponent(file.name))), // Имя файла в base64
|
"X-File-Name": btoa(unescape(encodeURIComponent(file.name))),
|
||||||
"X-Chunk-Start": offset.toString(),
|
"X-Chunk-Start": offset.toString(),
|
||||||
"Content-Type": file.type || "application/octet-stream"
|
"Content-Type": file.type || "application/octet-stream",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Добавляем маркер последнего чанка, если это последний чанк
|
|
||||||
if (isLastChunk) {
|
if (isLastChunk) {
|
||||||
headers["X-Last-Chunk"] = "1";
|
headers["X-Last-Chunk"] = "1";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Добавляем заголовок авторизации
|
const authToken = localStorage.getItem("auth_v1_token");
|
||||||
const authToken = localStorage.getItem('auth_v1_token');
|
|
||||||
if (authToken) {
|
if (authToken) {
|
||||||
headers["Authorization"] = authToken;
|
headers.Authorization = authToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Если есть uploadId, добавляем его в заголовки
|
|
||||||
if (uploadId) {
|
if (uploadId) {
|
||||||
headers["X-Upload-ID"] = uploadId;
|
headers["X-Upload-ID"] = uploadId;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Заголовки запроса:", headers);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await request.post<{
|
const response = await request.post<{
|
||||||
upload_id?: string;
|
upload_id?: string;
|
||||||
current_size?: number;
|
current_size?: number;
|
||||||
|
|
@ -124,71 +403,56 @@ export const useUploadFile = () => {
|
||||||
baseURL: STORAGE_API_URL,
|
baseURL: STORAGE_API_URL,
|
||||||
headers,
|
headers,
|
||||||
onUploadProgress: (progressEvent) => {
|
onUploadProgress: (progressEvent) => {
|
||||||
// Прогресс загрузки текущего чанка
|
|
||||||
const overallProgress = offset + progressEvent.loaded;
|
const overallProgress = offset + progressEvent.loaded;
|
||||||
const percentCompleted = Math.round((overallProgress / file.size) * 100);
|
const percentCompleted = Math.round(
|
||||||
|
(overallProgress / file.size) * 100,
|
||||||
|
);
|
||||||
setUploadProgress(Math.min(99, percentCompleted));
|
setUploadProgress(Math.min(99, percentCompleted));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Ответ на чанк #${chunkNumber}:`, response.data);
|
|
||||||
|
|
||||||
// Сохраняем uploadId из первого ответа, если не установлен
|
|
||||||
if (!uploadId && response.data.upload_id) {
|
if (!uploadId && response.data.upload_id) {
|
||||||
uploadId = response.data.upload_id;
|
uploadId = response.data.upload_id;
|
||||||
console.log("Получен upload_id:", uploadId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверка на наличие content_id
|
|
||||||
if (response.data.content_id) {
|
if (response.data.content_id) {
|
||||||
console.log("Загрузка завершена. ID файла:", response.data.content_id);
|
|
||||||
setUploadProgress(100);
|
setUploadProgress(100);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content_sha256: response.data.content_sha256 || "",
|
content_sha256: response.data.content_sha256 || "",
|
||||||
content_id_v1: response.data.content_id_v1 || response.data.content_id || "",
|
content_id_v1:
|
||||||
content_url: response.data.content_url || ""
|
response.data.content_id_v1 || response.data.content_id || "",
|
||||||
|
content_url: response.data.content_url || "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обновляем смещение на основе ответа сервера
|
|
||||||
if (response.data.current_size !== undefined) {
|
if (response.data.current_size !== undefined) {
|
||||||
offset = response.data.current_size;
|
offset = response.data.current_size;
|
||||||
console.log(`Сервер сообщает current_size: ${offset}`);
|
|
||||||
} else {
|
} else {
|
||||||
console.warn("Неожиданный ответ от сервера, отсутствует current_size");
|
|
||||||
const error = new Error("Missing current_size in response");
|
const error = new Error("Missing current_size in response");
|
||||||
setUploadError(error);
|
setUploadError(error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
|
||||||
console.error(`Ошибка при загрузке чанка #${chunkNumber}:`, error);
|
|
||||||
if (error.response) {
|
|
||||||
console.error("Ответ сервера:", error.response.status, error.response.data);
|
|
||||||
}
|
|
||||||
setUploadError(error instanceof Error ? error : new Error(`Ошибка при загрузке чанка #${chunkNumber}`));
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const error = new Error("Ошибка загрузки файла: все чанки загружены, но content_id не получен");
|
const error = new Error(
|
||||||
console.error(error.message);
|
"All chunks uploaded but server did not return content_id",
|
||||||
|
);
|
||||||
setUploadError(error);
|
setUploadError(error);
|
||||||
throw error;
|
throw error;
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
console.error("Ошибка при загрузке:", error);
|
const normalized = normalizeError(
|
||||||
if (error.response) {
|
error,
|
||||||
console.error("Ответ сервера:", error.response.status, error.response.data);
|
"Unknown error during legacy upload",
|
||||||
}
|
);
|
||||||
setUploadError(error instanceof Error ? error : new Error("Неизвестная ошибка при загрузке"));
|
setUploadError(normalized);
|
||||||
setIsUploading(false);
|
setIsUploading(false);
|
||||||
throw error;
|
throw normalized;
|
||||||
} finally {
|
} finally {
|
||||||
setIsUploading(false);
|
setIsUploading(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Сбросить ошибку
|
|
||||||
const resetUploadError = () => setUploadError(null);
|
const resetUploadError = () => setUploadError(null);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -196,6 +460,9 @@ export const useUploadFile = () => {
|
||||||
uploadProgress,
|
uploadProgress,
|
||||||
isUploading,
|
isUploading,
|
||||||
uploadError,
|
uploadError,
|
||||||
resetUploadError
|
resetUploadError,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export { useTusUpload as useUploadFile };
|
||||||
|
export type { TusUploadArgs, TusUploadResult, UploadSessionState };
|
||||||
|
|
|
||||||
155
yarn.lock
155
yarn.lock
|
|
@ -225,10 +225,10 @@
|
||||||
"@babel/helper-validator-identifier" "^7.22.20"
|
"@babel/helper-validator-identifier" "^7.22.20"
|
||||||
to-fast-properties "^2.0.0"
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
"@esbuild/win32-x64@0.19.12":
|
"@esbuild/darwin-arm64@0.19.12":
|
||||||
version "0.19.12"
|
version "0.19.12"
|
||||||
resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz"
|
resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz"
|
||||||
integrity sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==
|
integrity sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==
|
||||||
|
|
||||||
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
|
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
|
||||||
version "4.4.0"
|
version "4.4.0"
|
||||||
|
|
@ -381,10 +381,10 @@
|
||||||
resolved "https://registry.npmjs.org/@remix-run/router/-/router-1.15.2.tgz"
|
resolved "https://registry.npmjs.org/@remix-run/router/-/router-1.15.2.tgz"
|
||||||
integrity sha512-+Rnav+CaoTE5QJc4Jcwh5toUpnVLKYbpU6Ys0zqbakqbaLQHeglLVHPfxOiQqdNmUy5C2lXz5dwC6tQNX2JW2Q==
|
integrity sha512-+Rnav+CaoTE5QJc4Jcwh5toUpnVLKYbpU6Ys0zqbakqbaLQHeglLVHPfxOiQqdNmUy5C2lXz5dwC6tQNX2JW2Q==
|
||||||
|
|
||||||
"@rollup/rollup-win32-x64-msvc@4.12.0":
|
"@rollup/rollup-darwin-arm64@4.12.0":
|
||||||
version "4.12.0"
|
version "4.12.0"
|
||||||
resolved "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.0.tgz"
|
resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz"
|
||||||
integrity sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==
|
integrity sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==
|
||||||
|
|
||||||
"@sentry-internal/browser-utils@9.1.0":
|
"@sentry-internal/browser-utils@9.1.0":
|
||||||
version "9.1.0"
|
version "9.1.0"
|
||||||
|
|
@ -976,6 +976,11 @@ browserslist@^4.22.2, browserslist@^4.23.0, "browserslist@>= 4.21.0":
|
||||||
node-releases "^2.0.14"
|
node-releases "^2.0.14"
|
||||||
update-browserslist-db "^1.0.13"
|
update-browserslist-db "^1.0.13"
|
||||||
|
|
||||||
|
buffer-from@^1.1.2:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz"
|
||||||
|
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
|
||||||
|
|
||||||
buffer@^6.0.3:
|
buffer@^6.0.3:
|
||||||
version "6.0.3"
|
version "6.0.3"
|
||||||
resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz"
|
resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz"
|
||||||
|
|
@ -1081,6 +1086,14 @@ color-name@1.1.3:
|
||||||
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
|
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
|
||||||
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
|
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
|
||||||
|
|
||||||
|
combine-errors@^3.0.3:
|
||||||
|
version "3.0.3"
|
||||||
|
resolved "https://registry.npmjs.org/combine-errors/-/combine-errors-3.0.3.tgz"
|
||||||
|
integrity sha512-C8ikRNRMygCwaTx+Ek3Yr+OuZzgZjduCOfSQBjbM8V3MfgcjSTeto/GXP6PAwKvJz/v15b7GHZvx5rOlczFw/Q==
|
||||||
|
dependencies:
|
||||||
|
custom-error-instance "2.1.1"
|
||||||
|
lodash.uniqby "4.5.0"
|
||||||
|
|
||||||
combined-stream@^1.0.8:
|
combined-stream@^1.0.8:
|
||||||
version "1.0.8"
|
version "1.0.8"
|
||||||
resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz"
|
resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz"
|
||||||
|
|
@ -1122,6 +1135,11 @@ csstype@^3.0.2, csstype@^3.1.1:
|
||||||
resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz"
|
resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz"
|
||||||
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
|
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
|
||||||
|
|
||||||
|
custom-error-instance@2.1.1:
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.npmjs.org/custom-error-instance/-/custom-error-instance-2.1.1.tgz"
|
||||||
|
integrity sha512-p6JFxJc3M4OTD2li2qaHkDCw9SfMw82Ldr6OC9Je1aXiGfhx2W8p3GaoeaGrPJTUN9NirTM/KTxHWMUdR1rsUg==
|
||||||
|
|
||||||
debug@^3.2.7:
|
debug@^3.2.7:
|
||||||
version "3.2.7"
|
version "3.2.7"
|
||||||
resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz"
|
resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz"
|
||||||
|
|
@ -1637,6 +1655,11 @@ fs.realpath@^1.0.0:
|
||||||
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
|
||||||
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
|
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
|
||||||
|
|
||||||
|
fsevents@~2.3.2, fsevents@~2.3.3:
|
||||||
|
version "2.3.3"
|
||||||
|
resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz"
|
||||||
|
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
|
||||||
|
|
||||||
function-bind@^1.1.2:
|
function-bind@^1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
|
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
|
||||||
|
|
@ -1769,6 +1792,11 @@ gopd@^1.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
get-intrinsic "^1.1.3"
|
get-intrinsic "^1.1.3"
|
||||||
|
|
||||||
|
graceful-fs@^4.2.4:
|
||||||
|
version "4.2.11"
|
||||||
|
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz"
|
||||||
|
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
|
||||||
|
|
||||||
graphemer@^1.4.0:
|
graphemer@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz"
|
resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz"
|
||||||
|
|
@ -1975,6 +2003,11 @@ is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind "^1.0.7"
|
call-bind "^1.0.7"
|
||||||
|
|
||||||
|
is-stream@^2.0.0:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz"
|
||||||
|
integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
|
||||||
|
|
||||||
is-string@^1.0.5, is-string@^1.0.7:
|
is-string@^1.0.5, is-string@^1.0.7:
|
||||||
version "1.0.7"
|
version "1.0.7"
|
||||||
resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz"
|
resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz"
|
||||||
|
|
@ -2027,6 +2060,11 @@ jiti@^1.19.1:
|
||||||
resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz"
|
resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz"
|
||||||
integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==
|
integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==
|
||||||
|
|
||||||
|
js-base64@^3.7.2:
|
||||||
|
version "3.7.8"
|
||||||
|
resolved "https://registry.npmjs.org/js-base64/-/js-base64-3.7.8.tgz"
|
||||||
|
integrity sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==
|
||||||
|
|
||||||
js-sha3@0.8.0:
|
js-sha3@0.8.0:
|
||||||
version "0.8.0"
|
version "0.8.0"
|
||||||
resolved "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz"
|
resolved "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz"
|
||||||
|
|
@ -2076,11 +2114,6 @@ json5@^2.2.3:
|
||||||
resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz"
|
resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz"
|
||||||
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
|
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
|
||||||
|
|
||||||
jssha@^3.3.1:
|
|
||||||
version "3.3.1"
|
|
||||||
resolved "https://registry.npmjs.org/jssha/-/jssha-3.3.1.tgz"
|
|
||||||
integrity sha512-VCMZj12FCFMQYcFLPRm/0lOBbLi8uM2BhXPTqw3U4YAfs4AZfiApOoBLoN8cQE60Z50m1MYMTQVCfgF/KaCVhQ==
|
|
||||||
|
|
||||||
jssha@3.2.0:
|
jssha@3.2.0:
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.npmjs.org/jssha/-/jssha-3.2.0.tgz"
|
resolved "https://registry.npmjs.org/jssha/-/jssha-3.2.0.tgz"
|
||||||
|
|
@ -2133,11 +2166,61 @@ lodash-es@^4.17.21:
|
||||||
resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz"
|
resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz"
|
||||||
integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
|
integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
|
||||||
|
|
||||||
|
lodash._baseiteratee@~4.7.0:
|
||||||
|
version "4.7.0"
|
||||||
|
resolved "https://registry.npmjs.org/lodash._baseiteratee/-/lodash._baseiteratee-4.7.0.tgz"
|
||||||
|
integrity sha512-nqB9M+wITz0BX/Q2xg6fQ8mLkyfF7MU7eE+MNBNjTHFKeKaZAPEzEg+E8LWxKWf1DQVflNEn9N49yAuqKh2mWQ==
|
||||||
|
dependencies:
|
||||||
|
lodash._stringtopath "~4.8.0"
|
||||||
|
|
||||||
|
lodash._basetostring@~4.12.0:
|
||||||
|
version "4.12.0"
|
||||||
|
resolved "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-4.12.0.tgz"
|
||||||
|
integrity sha512-SwcRIbyxnN6CFEEK4K1y+zuApvWdpQdBHM/swxP962s8HIxPO3alBH5t3m/dl+f4CMUug6sJb7Pww8d13/9WSw==
|
||||||
|
|
||||||
|
lodash._baseuniq@~4.6.0:
|
||||||
|
version "4.6.0"
|
||||||
|
resolved "https://registry.npmjs.org/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz"
|
||||||
|
integrity sha512-Ja1YevpHZctlI5beLA7oc5KNDhGcPixFhcqSiORHNsp/1QTv7amAXzw+gu4YOvErqVlMVyIJGgtzeepCnnur0A==
|
||||||
|
dependencies:
|
||||||
|
lodash._createset "~4.0.0"
|
||||||
|
lodash._root "~3.0.0"
|
||||||
|
|
||||||
|
lodash._createset@~4.0.0:
|
||||||
|
version "4.0.3"
|
||||||
|
resolved "https://registry.npmjs.org/lodash._createset/-/lodash._createset-4.0.3.tgz"
|
||||||
|
integrity sha512-GTkC6YMprrJZCYU3zcqZj+jkXkrXzq3IPBcF/fIPpNEAB4hZEtXU8zp/RwKOvZl43NUmwDbyRk3+ZTbeRdEBXA==
|
||||||
|
|
||||||
|
lodash._root@~3.0.0:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz"
|
||||||
|
integrity sha512-O0pWuFSK6x4EXhM1dhZ8gchNtG7JMqBtrHdoUFUWXD7dJnNSUze1GuyQr5sOs0aCvgGeI3o/OJW8f4ca7FDxmQ==
|
||||||
|
|
||||||
|
lodash._stringtopath@~4.8.0:
|
||||||
|
version "4.8.0"
|
||||||
|
resolved "https://registry.npmjs.org/lodash._stringtopath/-/lodash._stringtopath-4.8.0.tgz"
|
||||||
|
integrity sha512-SXL66C731p0xPDC5LZg4wI5H+dJo/EO4KTqOMwLYCH3+FmmfAKJEZCm6ohGpI+T1xwsDsJCfL4OnhorllvlTPQ==
|
||||||
|
dependencies:
|
||||||
|
lodash._basetostring "~4.12.0"
|
||||||
|
|
||||||
lodash.merge@^4.6.2:
|
lodash.merge@^4.6.2:
|
||||||
version "4.6.2"
|
version "4.6.2"
|
||||||
resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz"
|
resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz"
|
||||||
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
||||||
|
|
||||||
|
lodash.throttle@^4.1.1:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz"
|
||||||
|
integrity sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==
|
||||||
|
|
||||||
|
lodash.uniqby@4.5.0:
|
||||||
|
version "4.5.0"
|
||||||
|
resolved "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.5.0.tgz"
|
||||||
|
integrity sha512-IRt7cfTtHy6f1aRVA5n7kT8rgN3N1nH6MOWLcHfpWG2SH19E3JksLK38MktLxZDhlAjCP9jpIXkOnRXlu6oByQ==
|
||||||
|
dependencies:
|
||||||
|
lodash._baseiteratee "~4.7.0"
|
||||||
|
lodash._baseuniq "~4.6.0"
|
||||||
|
|
||||||
loose-envify@^1.1.0, loose-envify@^1.4.0:
|
loose-envify@^1.1.0, loose-envify@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz"
|
resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz"
|
||||||
|
|
@ -2544,6 +2627,15 @@ prop-types@^15.7.2:
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
react-is "^16.13.1"
|
react-is "^16.13.1"
|
||||||
|
|
||||||
|
proper-lockfile@^4.1.2:
|
||||||
|
version "4.1.2"
|
||||||
|
resolved "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz"
|
||||||
|
integrity sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==
|
||||||
|
dependencies:
|
||||||
|
graceful-fs "^4.2.4"
|
||||||
|
retry "^0.12.0"
|
||||||
|
signal-exit "^3.0.2"
|
||||||
|
|
||||||
proxy-from-env@^1.1.0:
|
proxy-from-env@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz"
|
resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz"
|
||||||
|
|
@ -2554,6 +2646,11 @@ punycode@^2.1.0:
|
||||||
resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz"
|
resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz"
|
||||||
integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
|
integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
|
||||||
|
|
||||||
|
querystringify@^2.1.1:
|
||||||
|
version "2.2.0"
|
||||||
|
resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz"
|
||||||
|
integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
|
||||||
|
|
||||||
queue-microtask@^1.2.2:
|
queue-microtask@^1.2.2:
|
||||||
version "1.2.3"
|
version "1.2.3"
|
||||||
resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz"
|
resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz"
|
||||||
|
|
@ -2697,6 +2794,11 @@ remove-accents@0.5.0:
|
||||||
resolved "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz"
|
resolved "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz"
|
||||||
integrity sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==
|
integrity sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==
|
||||||
|
|
||||||
|
requires-port@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz"
|
||||||
|
integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
|
||||||
|
|
||||||
resolve-from@^4.0.0:
|
resolve-from@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz"
|
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz"
|
||||||
|
|
@ -2711,6 +2813,11 @@ resolve@^1.1.7, resolve@^1.22.2, resolve@^1.22.4:
|
||||||
path-parse "^1.0.7"
|
path-parse "^1.0.7"
|
||||||
supports-preserve-symlinks-flag "^1.0.0"
|
supports-preserve-symlinks-flag "^1.0.0"
|
||||||
|
|
||||||
|
retry@^0.12.0:
|
||||||
|
version "0.12.0"
|
||||||
|
resolved "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz"
|
||||||
|
integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==
|
||||||
|
|
||||||
reusify@^1.0.4:
|
reusify@^1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz"
|
resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz"
|
||||||
|
|
@ -2834,6 +2941,11 @@ side-channel@^1.0.4:
|
||||||
get-intrinsic "^1.2.4"
|
get-intrinsic "^1.2.4"
|
||||||
object-inspect "^1.13.1"
|
object-inspect "^1.13.1"
|
||||||
|
|
||||||
|
signal-exit@^3.0.2:
|
||||||
|
version "3.0.7"
|
||||||
|
resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz"
|
||||||
|
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
|
||||||
|
|
||||||
signal-exit@^4.0.1:
|
signal-exit@^4.0.1:
|
||||||
version "4.1.0"
|
version "4.1.0"
|
||||||
resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz"
|
resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz"
|
||||||
|
|
@ -3080,6 +3192,19 @@ tslib@^2.6.2:
|
||||||
resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz"
|
resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz"
|
||||||
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
|
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
|
||||||
|
|
||||||
|
tus-js-client@^4.3.1:
|
||||||
|
version "4.3.1"
|
||||||
|
resolved "https://registry.npmjs.org/tus-js-client/-/tus-js-client-4.3.1.tgz"
|
||||||
|
integrity sha512-ZLeYmjrkaU1fUsKbIi8JML52uAocjEZtBx4DKjRrqzrZa0O4MYwT6db+oqePlspV+FxXJAyFBc/L5gwUi2OFsg==
|
||||||
|
dependencies:
|
||||||
|
buffer-from "^1.1.2"
|
||||||
|
combine-errors "^3.0.3"
|
||||||
|
is-stream "^2.0.0"
|
||||||
|
js-base64 "^3.7.2"
|
||||||
|
lodash.throttle "^4.1.1"
|
||||||
|
proper-lockfile "^4.1.2"
|
||||||
|
url-parse "^1.5.7"
|
||||||
|
|
||||||
tweetnacl-util@^0.15.1:
|
tweetnacl-util@^0.15.1:
|
||||||
version "0.15.1"
|
version "0.15.1"
|
||||||
resolved "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz"
|
resolved "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz"
|
||||||
|
|
@ -3194,6 +3319,14 @@ uri-js@^4.2.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
punycode "^2.1.0"
|
punycode "^2.1.0"
|
||||||
|
|
||||||
|
url-parse@^1.5.7:
|
||||||
|
version "1.5.10"
|
||||||
|
resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz"
|
||||||
|
integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==
|
||||||
|
dependencies:
|
||||||
|
querystringify "^2.1.1"
|
||||||
|
requires-port "^1.0.0"
|
||||||
|
|
||||||
use-sync-external-store@1.2.0:
|
use-sync-external-store@1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz"
|
resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue