Commit remaining workspace changes
@@ -9,6 +9,7 @@
|
|||||||
"lint": "eslint"
|
"lint": "eslint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@aws-sdk/client-s3": "^3.1075.0",
|
||||||
"@radix-ui/react-label": "^2.1.10",
|
"@radix-ui/react-label": "^2.1.10",
|
||||||
"@radix-ui/react-select": "^2.3.1",
|
"@radix-ui/react-select": "^2.3.1",
|
||||||
"@radix-ui/react-separator": "^1.1.10",
|
"@radix-ui/react-separator": "^1.1.10",
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@aws-sdk/client-s3':
|
||||||
|
specifier: ^3.1075.0
|
||||||
|
version: 3.1075.0
|
||||||
'@radix-ui/react-label':
|
'@radix-ui/react-label':
|
||||||
specifier: ^2.1.10
|
specifier: ^2.1.10
|
||||||
version: 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
version: 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
@@ -94,6 +97,109 @@ packages:
|
|||||||
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
|
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
'@aws-crypto/crc32@5.2.0':
|
||||||
|
resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==}
|
||||||
|
engines: {node: '>=16.0.0'}
|
||||||
|
|
||||||
|
'@aws-crypto/crc32c@5.2.0':
|
||||||
|
resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==}
|
||||||
|
|
||||||
|
'@aws-crypto/sha1-browser@5.2.0':
|
||||||
|
resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==}
|
||||||
|
|
||||||
|
'@aws-crypto/sha256-browser@5.2.0':
|
||||||
|
resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==}
|
||||||
|
|
||||||
|
'@aws-crypto/sha256-js@5.2.0':
|
||||||
|
resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==}
|
||||||
|
engines: {node: '>=16.0.0'}
|
||||||
|
|
||||||
|
'@aws-crypto/supports-web-crypto@5.2.0':
|
||||||
|
resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==}
|
||||||
|
|
||||||
|
'@aws-crypto/util@5.2.0':
|
||||||
|
resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==}
|
||||||
|
|
||||||
|
'@aws-sdk/checksums@3.1000.8':
|
||||||
|
resolution: {integrity: sha512-v0U9S7gBIme3OTgt1LdbAF4RpvavCc+4GK1+1xqAcqtbrHsEhjQo6R45LKcjhs/+WrRJij1Y0Gztw7QPAIeUfA==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
|
'@aws-sdk/client-s3@3.1075.0':
|
||||||
|
resolution: {integrity: sha512-h1A6nIl1YX6Y45enGsTK7ef3ZrOnBiQJ1qF5R2K/nMWfsu6A9mc2Y5T66nxerABzyjjyyvign3MrzafnFoQKmA==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
|
'@aws-sdk/core@3.974.23':
|
||||||
|
resolution: {integrity: sha512-MiWR/uWjxjFXGzrE0Ghc5lWxUxzHsUWFhV+OX7M4cR9SrmrnZs6TXavnCWnzzdwJeFri34xQo81rvGNzK3c4BQ==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
|
'@aws-sdk/credential-provider-env@3.972.49':
|
||||||
|
resolution: {integrity: sha512-liB3yQNHCM9k/gu/w36XHMKPluT7HTlnGUhRbBGSISDQkcr/Sy1zsZabiuvQj8WG5yW573u9RehrBvvnIQ9OEQ==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
|
'@aws-sdk/credential-provider-http@3.972.51':
|
||||||
|
resolution: {integrity: sha512-XET0H2oofciJ5lMRWNIvRjAP7Q3wv2XT+JtJJEdhPWUMwe3TvQ9qcxonpu7vXmNngncvFpi4E2It+Tamas/naA==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
|
'@aws-sdk/credential-provider-ini@3.972.56':
|
||||||
|
resolution: {integrity: sha512-IAmc61hbgQiHht9U3x0tnRwz0lzdwOwD/i9voRgdJrKamF+JtmrBOsW9GwB7mfFonNWOWL4qARWYrF8veEMe3w==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
|
'@aws-sdk/credential-provider-login@3.972.55':
|
||||||
|
resolution: {integrity: sha512-hBBkANo3cDn+h2qxxzER4a+J8JCO9o9Z/YYmU7iky6AcaarX5RRdRcHNC6SLdwY0vAXQygn6soUbDqPn3GghaA==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
|
'@aws-sdk/credential-provider-node@3.972.58':
|
||||||
|
resolution: {integrity: sha512-OyCLVmSI7pZO8hxwNVX6pXhTVlJqRBTp+ijdEfJSUj0RyjHnF602OfAarOzGq6wkGodeFkYBt8MmJ6A6ycRgWw==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
|
'@aws-sdk/credential-provider-process@3.972.49':
|
||||||
|
resolution: {integrity: sha512-C8h36lBuC/RnBSsjlO+dn6xZm3KbAl5vpJaVPAfQnMmz2/OISmKOc8XZcqMQgO2ADwBYNRMM6Kf3vz9G/TulMQ==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
|
'@aws-sdk/credential-provider-sso@3.972.55':
|
||||||
|
resolution: {integrity: sha512-1FkOz74Ea5QGS9jtIoXp55T/IkSS3spv+nLTT07fRY/+T5xmEOqaYBVIaEmX4zTNvbV6g2lrtlaVKWEoNyJt3w==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
|
'@aws-sdk/credential-provider-web-identity@3.972.55':
|
||||||
|
resolution: {integrity: sha512-g2BoECD1q01kTPByi56+VLVvdWDzMkKIcr77qixpqH0okw2t0U5CoPv+6S8v/D1Y2Wa6QKKtn6XAtDzP+Kfpvg==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
|
'@aws-sdk/middleware-flexible-checksums@3.974.33':
|
||||||
|
resolution: {integrity: sha512-qMgQSPemQq2/eW/e/0+SpY4kYR5L7dUgBiVdEc5bd+ztHNv07ZMYiI+sTiir3TgKndFfglSw/VFi7oZJ6bZ63g==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
|
'@aws-sdk/middleware-sdk-s3@3.972.54':
|
||||||
|
resolution: {integrity: sha512-GDfDQ0gwLFRKN9gWIKcmVrHJ3e7XagnY7N1LLzMVNgnOnuY7f/ALgmy3CuBjosWD95T/Z6e+gs1IeWmLPkyLKQ==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
|
'@aws-sdk/nested-clients@3.997.23':
|
||||||
|
resolution: {integrity: sha512-gO93ZPsI2bxeFZD42f1/qjDw6FAZkNZcKRO94LIiT03fzOmcJ9e/tunxjVjA1Rl69ClmVJzz8H3G9CdKef10PA==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
|
'@aws-sdk/signature-v4-multi-region@3.996.35':
|
||||||
|
resolution: {integrity: sha512-6L/VWs+Wch2stHemCGTmUNqKLMzURxQDK5boNG3Jn3kAOp71meDUuS5sbObpEvFxHDq0uWeSLFDNSYsjNt+Dlg==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
|
'@aws-sdk/token-providers@3.1074.0':
|
||||||
|
resolution: {integrity: sha512-pv80IzgGW4RnXWtft692chZOM9i6PhebVsLCcnaM4dBEPZva2fE6FXAHs76G7Rc7s3yGyX/68G0nZMrUy+Vmpg==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
|
'@aws-sdk/types@3.973.13':
|
||||||
|
resolution: {integrity: sha512-pEHZqRkAlHfnfAU9tK+WpKv/gBNjGJrHMgA3A0iYRGyswBS2t0pfez+lWlwktb3Bqa0ovh7w/QJTFwp3fDxLNg==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
|
'@aws-sdk/util-locate-window@3.965.8':
|
||||||
|
resolution: {integrity: sha512-uUbMs1cBZPafD0ohUj6EwNf0fPZ534NvBxHox4hjX+0Rxq5paSYUem7+hi833pYrzrcnBATKIYpR02MDXT5M9g==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
|
'@aws-sdk/xml-builder@3.972.31':
|
||||||
|
resolution: {integrity: sha512-SzE4Pgyl+hDF+BuyuzxUSpwnuUu9lJuO1YGgteG89/4Qv0+2IQiVQqdbPV32IozLvXWQChPQcdkk/sKvb1QHiQ==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
|
'@aws/lambda-invoke-store@0.2.4':
|
||||||
|
resolution: {integrity: sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
|
||||||
'@babel/code-frame@7.29.7':
|
'@babel/code-frame@7.29.7':
|
||||||
resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==}
|
resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@@ -851,6 +957,42 @@ packages:
|
|||||||
'@rtsao/scc@1.1.0':
|
'@rtsao/scc@1.1.0':
|
||||||
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
|
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
|
||||||
|
|
||||||
|
'@smithy/core@3.26.0':
|
||||||
|
resolution: {integrity: sha512-mLUktFAn+Pa2agl1J7VgtYNFWCX8/b4GMJSK1hCu4YCvtBfM6F8Os3EP4ry+DFFlXOf3wyvlgXhuUdFoy52D3g==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
|
||||||
|
'@smithy/credential-provider-imds@4.4.2':
|
||||||
|
resolution: {integrity: sha512-18UMDMyrAbDcpmL1gLUA7ww0fRTcdCrSjSJOi2Sbld+tVjwD/pW+OAwjlScFLR7vvBnhZrIPQ7kVuTf1mnJLug==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
|
||||||
|
'@smithy/fetch-http-handler@5.5.2':
|
||||||
|
resolution: {integrity: sha512-Ei/UK/QMhq0rKaMqGPlOAkE2yS9DZeYmZdk1RAKc3vp3zxgleZHZyBLlZv8yLsxljX4svCRuMTD6u3LLIcU4Bg==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
|
||||||
|
'@smithy/is-array-buffer@2.2.0':
|
||||||
|
resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
|
'@smithy/node-http-handler@4.8.2':
|
||||||
|
resolution: {integrity: sha512-wfl1uwrAqMH9/pi4kqBo5LBcFwrJLxuDLqL7p7qNcJIFcyZDUc6pzhYk4CYv+DP7fIUpQCZumwNnkhPKS52osQ==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
|
||||||
|
'@smithy/signature-v4@5.5.2':
|
||||||
|
resolution: {integrity: sha512-7xHpmPY4rt0IOmeAA8EfjgEH8isT+587TCdy9H6a7d4OMi5CQ0oEHhWllunvPu4j4Cq0vTFwdxXN/kABWPjdyA==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
|
||||||
|
'@smithy/types@4.15.0':
|
||||||
|
resolution: {integrity: sha512-Z5TAOxygoFvybJV3igo5SloFflSokHx2hu1eFA+DxDTcn+FtKxUSui+rbTRG1pAafMA888Z3MVvCWUuvCrTXjg==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
|
||||||
|
'@smithy/util-buffer-from@2.2.0':
|
||||||
|
resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
|
'@smithy/util-utf8@2.3.0':
|
||||||
|
resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
'@swc/helpers@0.5.15':
|
'@swc/helpers@0.5.15':
|
||||||
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
|
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
|
||||||
|
|
||||||
@@ -1141,6 +1283,9 @@ packages:
|
|||||||
bl@2.2.0:
|
bl@2.2.0:
|
||||||
resolution: {integrity: sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==}
|
resolution: {integrity: sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==}
|
||||||
|
|
||||||
|
bowser@2.14.1:
|
||||||
|
resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==}
|
||||||
|
|
||||||
brace-expansion@1.1.15:
|
brace-expansion@1.1.15:
|
||||||
resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==}
|
resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==}
|
||||||
|
|
||||||
@@ -2634,6 +2779,235 @@ snapshots:
|
|||||||
|
|
||||||
'@alloc/quick-lru@5.2.0': {}
|
'@alloc/quick-lru@5.2.0': {}
|
||||||
|
|
||||||
|
'@aws-crypto/crc32@5.2.0':
|
||||||
|
dependencies:
|
||||||
|
'@aws-crypto/util': 5.2.0
|
||||||
|
'@aws-sdk/types': 3.973.13
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-crypto/crc32c@5.2.0':
|
||||||
|
dependencies:
|
||||||
|
'@aws-crypto/util': 5.2.0
|
||||||
|
'@aws-sdk/types': 3.973.13
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-crypto/sha1-browser@5.2.0':
|
||||||
|
dependencies:
|
||||||
|
'@aws-crypto/supports-web-crypto': 5.2.0
|
||||||
|
'@aws-crypto/util': 5.2.0
|
||||||
|
'@aws-sdk/types': 3.973.13
|
||||||
|
'@aws-sdk/util-locate-window': 3.965.8
|
||||||
|
'@smithy/util-utf8': 2.3.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-crypto/sha256-browser@5.2.0':
|
||||||
|
dependencies:
|
||||||
|
'@aws-crypto/sha256-js': 5.2.0
|
||||||
|
'@aws-crypto/supports-web-crypto': 5.2.0
|
||||||
|
'@aws-crypto/util': 5.2.0
|
||||||
|
'@aws-sdk/types': 3.973.13
|
||||||
|
'@aws-sdk/util-locate-window': 3.965.8
|
||||||
|
'@smithy/util-utf8': 2.3.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-crypto/sha256-js@5.2.0':
|
||||||
|
dependencies:
|
||||||
|
'@aws-crypto/util': 5.2.0
|
||||||
|
'@aws-sdk/types': 3.973.13
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-crypto/supports-web-crypto@5.2.0':
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-crypto/util@5.2.0':
|
||||||
|
dependencies:
|
||||||
|
'@aws-sdk/types': 3.973.13
|
||||||
|
'@smithy/util-utf8': 2.3.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-sdk/checksums@3.1000.8':
|
||||||
|
dependencies:
|
||||||
|
'@aws-crypto/crc32': 5.2.0
|
||||||
|
'@aws-crypto/crc32c': 5.2.0
|
||||||
|
'@aws-crypto/util': 5.2.0
|
||||||
|
'@aws-sdk/core': 3.974.23
|
||||||
|
'@aws-sdk/types': 3.973.13
|
||||||
|
'@smithy/core': 3.26.0
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-sdk/client-s3@3.1075.0':
|
||||||
|
dependencies:
|
||||||
|
'@aws-crypto/sha1-browser': 5.2.0
|
||||||
|
'@aws-crypto/sha256-browser': 5.2.0
|
||||||
|
'@aws-crypto/sha256-js': 5.2.0
|
||||||
|
'@aws-sdk/core': 3.974.23
|
||||||
|
'@aws-sdk/credential-provider-node': 3.972.58
|
||||||
|
'@aws-sdk/middleware-flexible-checksums': 3.974.33
|
||||||
|
'@aws-sdk/middleware-sdk-s3': 3.972.54
|
||||||
|
'@aws-sdk/signature-v4-multi-region': 3.996.35
|
||||||
|
'@aws-sdk/types': 3.973.13
|
||||||
|
'@smithy/core': 3.26.0
|
||||||
|
'@smithy/fetch-http-handler': 5.5.2
|
||||||
|
'@smithy/node-http-handler': 4.8.2
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-sdk/core@3.974.23':
|
||||||
|
dependencies:
|
||||||
|
'@aws-sdk/types': 3.973.13
|
||||||
|
'@aws-sdk/xml-builder': 3.972.31
|
||||||
|
'@aws/lambda-invoke-store': 0.2.4
|
||||||
|
'@smithy/core': 3.26.0
|
||||||
|
'@smithy/signature-v4': 5.5.2
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
bowser: 2.14.1
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-sdk/credential-provider-env@3.972.49':
|
||||||
|
dependencies:
|
||||||
|
'@aws-sdk/core': 3.974.23
|
||||||
|
'@aws-sdk/types': 3.973.13
|
||||||
|
'@smithy/core': 3.26.0
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-sdk/credential-provider-http@3.972.51':
|
||||||
|
dependencies:
|
||||||
|
'@aws-sdk/core': 3.974.23
|
||||||
|
'@aws-sdk/types': 3.973.13
|
||||||
|
'@smithy/core': 3.26.0
|
||||||
|
'@smithy/fetch-http-handler': 5.5.2
|
||||||
|
'@smithy/node-http-handler': 4.8.2
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-sdk/credential-provider-ini@3.972.56':
|
||||||
|
dependencies:
|
||||||
|
'@aws-sdk/core': 3.974.23
|
||||||
|
'@aws-sdk/credential-provider-env': 3.972.49
|
||||||
|
'@aws-sdk/credential-provider-http': 3.972.51
|
||||||
|
'@aws-sdk/credential-provider-login': 3.972.55
|
||||||
|
'@aws-sdk/credential-provider-process': 3.972.49
|
||||||
|
'@aws-sdk/credential-provider-sso': 3.972.55
|
||||||
|
'@aws-sdk/credential-provider-web-identity': 3.972.55
|
||||||
|
'@aws-sdk/nested-clients': 3.997.23
|
||||||
|
'@aws-sdk/types': 3.973.13
|
||||||
|
'@smithy/core': 3.26.0
|
||||||
|
'@smithy/credential-provider-imds': 4.4.2
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-sdk/credential-provider-login@3.972.55':
|
||||||
|
dependencies:
|
||||||
|
'@aws-sdk/core': 3.974.23
|
||||||
|
'@aws-sdk/nested-clients': 3.997.23
|
||||||
|
'@aws-sdk/types': 3.973.13
|
||||||
|
'@smithy/core': 3.26.0
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-sdk/credential-provider-node@3.972.58':
|
||||||
|
dependencies:
|
||||||
|
'@aws-sdk/credential-provider-env': 3.972.49
|
||||||
|
'@aws-sdk/credential-provider-http': 3.972.51
|
||||||
|
'@aws-sdk/credential-provider-ini': 3.972.56
|
||||||
|
'@aws-sdk/credential-provider-process': 3.972.49
|
||||||
|
'@aws-sdk/credential-provider-sso': 3.972.55
|
||||||
|
'@aws-sdk/credential-provider-web-identity': 3.972.55
|
||||||
|
'@aws-sdk/types': 3.973.13
|
||||||
|
'@smithy/core': 3.26.0
|
||||||
|
'@smithy/credential-provider-imds': 4.4.2
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-sdk/credential-provider-process@3.972.49':
|
||||||
|
dependencies:
|
||||||
|
'@aws-sdk/core': 3.974.23
|
||||||
|
'@aws-sdk/types': 3.973.13
|
||||||
|
'@smithy/core': 3.26.0
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-sdk/credential-provider-sso@3.972.55':
|
||||||
|
dependencies:
|
||||||
|
'@aws-sdk/core': 3.974.23
|
||||||
|
'@aws-sdk/nested-clients': 3.997.23
|
||||||
|
'@aws-sdk/token-providers': 3.1074.0
|
||||||
|
'@aws-sdk/types': 3.973.13
|
||||||
|
'@smithy/core': 3.26.0
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-sdk/credential-provider-web-identity@3.972.55':
|
||||||
|
dependencies:
|
||||||
|
'@aws-sdk/core': 3.974.23
|
||||||
|
'@aws-sdk/nested-clients': 3.997.23
|
||||||
|
'@aws-sdk/types': 3.973.13
|
||||||
|
'@smithy/core': 3.26.0
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-sdk/middleware-flexible-checksums@3.974.33':
|
||||||
|
dependencies:
|
||||||
|
'@aws-sdk/checksums': 3.1000.8
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-sdk/middleware-sdk-s3@3.972.54':
|
||||||
|
dependencies:
|
||||||
|
'@aws-sdk/core': 3.974.23
|
||||||
|
'@aws-sdk/signature-v4-multi-region': 3.996.35
|
||||||
|
'@aws-sdk/types': 3.973.13
|
||||||
|
'@smithy/core': 3.26.0
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-sdk/nested-clients@3.997.23':
|
||||||
|
dependencies:
|
||||||
|
'@aws-crypto/sha256-browser': 5.2.0
|
||||||
|
'@aws-crypto/sha256-js': 5.2.0
|
||||||
|
'@aws-sdk/core': 3.974.23
|
||||||
|
'@aws-sdk/signature-v4-multi-region': 3.996.35
|
||||||
|
'@aws-sdk/types': 3.973.13
|
||||||
|
'@smithy/core': 3.26.0
|
||||||
|
'@smithy/fetch-http-handler': 5.5.2
|
||||||
|
'@smithy/node-http-handler': 4.8.2
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-sdk/signature-v4-multi-region@3.996.35':
|
||||||
|
dependencies:
|
||||||
|
'@aws-sdk/types': 3.973.13
|
||||||
|
'@smithy/signature-v4': 5.5.2
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-sdk/token-providers@3.1074.0':
|
||||||
|
dependencies:
|
||||||
|
'@aws-sdk/core': 3.974.23
|
||||||
|
'@aws-sdk/nested-clients': 3.997.23
|
||||||
|
'@aws-sdk/types': 3.973.13
|
||||||
|
'@smithy/core': 3.26.0
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-sdk/types@3.973.13':
|
||||||
|
dependencies:
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-sdk/util-locate-window@3.965.8':
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws-sdk/xml-builder@3.972.31':
|
||||||
|
dependencies:
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@aws/lambda-invoke-store@0.2.4': {}
|
||||||
|
|
||||||
'@babel/code-frame@7.29.7':
|
'@babel/code-frame@7.29.7':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/helper-validator-identifier': 7.29.7
|
'@babel/helper-validator-identifier': 7.29.7
|
||||||
@@ -3327,6 +3701,54 @@ snapshots:
|
|||||||
|
|
||||||
'@rtsao/scc@1.1.0': {}
|
'@rtsao/scc@1.1.0': {}
|
||||||
|
|
||||||
|
'@smithy/core@3.26.0':
|
||||||
|
dependencies:
|
||||||
|
'@aws-crypto/crc32': 5.2.0
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@smithy/credential-provider-imds@4.4.2':
|
||||||
|
dependencies:
|
||||||
|
'@smithy/core': 3.26.0
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@smithy/fetch-http-handler@5.5.2':
|
||||||
|
dependencies:
|
||||||
|
'@smithy/core': 3.26.0
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@smithy/is-array-buffer@2.2.0':
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@smithy/node-http-handler@4.8.2':
|
||||||
|
dependencies:
|
||||||
|
'@smithy/core': 3.26.0
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@smithy/signature-v4@5.5.2':
|
||||||
|
dependencies:
|
||||||
|
'@smithy/core': 3.26.0
|
||||||
|
'@smithy/types': 4.15.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@smithy/types@4.15.0':
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@smithy/util-buffer-from@2.2.0':
|
||||||
|
dependencies:
|
||||||
|
'@smithy/is-array-buffer': 2.2.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@smithy/util-utf8@2.3.0':
|
||||||
|
dependencies:
|
||||||
|
'@smithy/util-buffer-from': 2.2.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
'@swc/helpers@0.5.15':
|
'@swc/helpers@0.5.15':
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
@@ -3643,6 +4065,8 @@ snapshots:
|
|||||||
readable-stream: 2.3.8
|
readable-stream: 2.3.8
|
||||||
safe-buffer: 5.2.1
|
safe-buffer: 5.2.1
|
||||||
|
|
||||||
|
bowser@2.14.1: {}
|
||||||
|
|
||||||
brace-expansion@1.1.15:
|
brace-expansion@1.1.15:
|
||||||
dependencies:
|
dependencies:
|
||||||
balanced-match: 1.0.0
|
balanced-match: 1.0.0
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
allowBuilds:
|
allowBuilds:
|
||||||
better-sqlite3: true
|
better-sqlite3: true
|
||||||
sharp: true
|
sharp: true
|
||||||
|
|
||||||
|
onlyBuiltDependencies:
|
||||||
|
- better-sqlite3
|
||||||
|
- sharp
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 2.5 MiB |
|
Before Width: | Height: | Size: 2.5 MiB |
|
Before Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 2.2 MiB |
@@ -20,7 +20,7 @@ export async function GET(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
item: createCanvasProjectExport({
|
item: await createCanvasProjectExport({
|
||||||
title: project.title,
|
title: project.title,
|
||||||
description: project.description,
|
description: project.description,
|
||||||
data: project.data,
|
data: project.data,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export async function POST(request: Request) {
|
|||||||
const payload = await request.json().catch(() => null);
|
const payload = await request.json().catch(() => null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const project = importCanvasProjectExport(payload);
|
const project = await importCanvasProjectExport(payload);
|
||||||
return NextResponse.json({ item: project }, { status: 201 });
|
return NextResponse.json({ item: project }, { status: 201 });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export async function POST(request: Request) {
|
|||||||
|
|
||||||
const buffer = Buffer.from(await image.arrayBuffer());
|
const buffer = Buffer.from(await image.arrayBuffer());
|
||||||
const id = crypto.randomUUID();
|
const id = crypto.randomUUID();
|
||||||
const persisted = persistUploadedImageFile(id, image.name || "canvas-upload.png", buffer);
|
const persisted = await persistUploadedImageFile(id, image.name || "canvas-upload.png", buffer);
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
item: {
|
item: {
|
||||||
|
|||||||
@@ -609,29 +609,30 @@ export function DirectStudio({ activeView }: { activeView: ActiveView }) {
|
|||||||
) : history.length ? (
|
) : history.length ? (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{history.map((item) => (
|
{history.map((item) => (
|
||||||
<button
|
<div
|
||||||
key={item.id}
|
key={item.id}
|
||||||
className="flex w-full items-center gap-3 rounded-md border border-zinc-200 bg-white p-2 text-left hover:bg-zinc-50"
|
className="flex w-full items-center gap-3 rounded-md border border-zinc-200 bg-white p-2 hover:bg-zinc-50"
|
||||||
onClick={() => selectHistoryItem(item)}
|
|
||||||
type="button"
|
|
||||||
>
|
>
|
||||||
<img alt={item.prompt} className="size-14 rounded object-cover" src={item.imageUrl} />
|
<button
|
||||||
<div className="min-w-0 flex-1">
|
className="flex min-w-0 flex-1 items-center gap-3 text-left"
|
||||||
<div className="truncate text-xs font-medium">{item.prompt}</div>
|
onClick={() => selectHistoryItem(item)}
|
||||||
<div className="text-[11px] text-zinc-500">{formatDate(item.createdAt)}</div>
|
type="button"
|
||||||
</div>
|
>
|
||||||
|
<img alt={item.prompt} className="size-14 rounded object-cover" src={item.imageUrl} />
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
<div className="truncate text-xs font-medium">{item.prompt}</div>
|
||||||
|
<div className="text-[11px] text-zinc-500">{formatDate(item.createdAt)}</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
<Button
|
<Button
|
||||||
size="icon"
|
size="icon"
|
||||||
type="button"
|
type="button"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={(event) => {
|
onClick={() => void placeImageForEdit(item)}
|
||||||
event.stopPropagation();
|
|
||||||
void placeImageForEdit(item);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Brush />
|
<Brush />
|
||||||
</Button>
|
</Button>
|
||||||
</button>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -643,35 +644,36 @@ export function DirectStudio({ activeView }: { activeView: ActiveView }) {
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{history.length ? (
|
{history.length ? (
|
||||||
history.map((item) => (
|
history.map((item) => (
|
||||||
<button
|
<div
|
||||||
key={item.id}
|
key={item.id}
|
||||||
className="flex w-full items-center gap-3 rounded-md border border-zinc-200 bg-white p-2 text-left hover:bg-zinc-50"
|
className="flex w-full items-center gap-3 rounded-md border border-zinc-200 bg-white p-2 hover:bg-zinc-50"
|
||||||
onClick={() =>
|
|
||||||
setPreviewImage({
|
|
||||||
title: item.prompt.slice(0, 24) || "作品",
|
|
||||||
subtitle: formatDate(item.createdAt),
|
|
||||||
url: item.imageUrl,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
type="button"
|
|
||||||
>
|
>
|
||||||
<img alt={item.prompt} className="size-16 rounded object-cover" src={item.imageUrl} />
|
<button
|
||||||
<div className="min-w-0 flex-1">
|
className="flex min-w-0 flex-1 items-center gap-3 text-left"
|
||||||
<div className="truncate text-sm font-medium">{item.prompt}</div>
|
onClick={() =>
|
||||||
<div className="text-xs text-zinc-500">{item.model} · {item.outputFormat}</div>
|
setPreviewImage({
|
||||||
</div>
|
title: item.prompt.slice(0, 24) || "作品",
|
||||||
|
subtitle: formatDate(item.createdAt),
|
||||||
|
url: item.imageUrl,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<img alt={item.prompt} className="size-16 rounded object-cover" src={item.imageUrl} />
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
<div className="truncate text-sm font-medium">{item.prompt}</div>
|
||||||
|
<div className="text-xs text-zinc-500">{item.model} · {item.outputFormat}</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
<Button
|
<Button
|
||||||
size="icon"
|
size="icon"
|
||||||
type="button"
|
type="button"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={(event) => {
|
onClick={() => void placeImageForEdit(item)}
|
||||||
event.stopPropagation();
|
|
||||||
void placeImageForEdit(item);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Brush />
|
<Brush />
|
||||||
</Button>
|
</Button>
|
||||||
</button>
|
</div>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<div className="text-sm text-zinc-500">暂无历史记录</div>
|
<div className="text-sm text-zinc-500">暂无历史记录</div>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { existsSync, readFileSync } from "node:fs";
|
|
||||||
import { extname, basename } from "node:path";
|
import { extname, basename } from "node:path";
|
||||||
import type { CanvasImageNodeMetadata, CanvasProjectData } from "@/lib/canvas/types";
|
import type { CanvasImageNodeMetadata, CanvasProjectData } from "@/lib/canvas/types";
|
||||||
import { normalizeCanvasProjectData } from "@/lib/canvas/types";
|
import { normalizeCanvasProjectData } from "@/lib/canvas/types";
|
||||||
@@ -6,6 +5,7 @@ import { createCanvasProject } from "@/lib/server/repositories/canvas-project-re
|
|||||||
import {
|
import {
|
||||||
getGeneratedImagePathFromUrl,
|
getGeneratedImagePathFromUrl,
|
||||||
persistImageBuffer,
|
persistImageBuffer,
|
||||||
|
readPersistedImageFile,
|
||||||
} from "@/lib/server/storage/image-file-storage";
|
} from "@/lib/server/storage/image-file-storage";
|
||||||
|
|
||||||
const CANVAS_EXPORT_KIND = "imagegen.canvas.project";
|
const CANVAS_EXPORT_KIND = "imagegen.canvas.project";
|
||||||
@@ -29,11 +29,11 @@ export type CanvasProjectExportV1 = {
|
|||||||
assets: CanvasProjectExportAsset[];
|
assets: CanvasProjectExportAsset[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export function createCanvasProjectExport(input: {
|
export async function createCanvasProjectExport(input: {
|
||||||
title: string;
|
title: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
data: CanvasProjectData;
|
data: CanvasProjectData;
|
||||||
}): CanvasProjectExportV1 {
|
}): Promise<CanvasProjectExportV1> {
|
||||||
return {
|
return {
|
||||||
kind: CANVAS_EXPORT_KIND,
|
kind: CANVAS_EXPORT_KIND,
|
||||||
version: CANVAS_EXPORT_VERSION,
|
version: CANVAS_EXPORT_VERSION,
|
||||||
@@ -41,11 +41,11 @@ export function createCanvasProjectExport(input: {
|
|||||||
title: input.title,
|
title: input.title,
|
||||||
description: input.description ?? "",
|
description: input.description ?? "",
|
||||||
data: input.data,
|
data: input.data,
|
||||||
assets: collectExportAssets(input.data),
|
assets: await collectExportAssets(input.data),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function importCanvasProjectExport(value: unknown) {
|
export async function importCanvasProjectExport(value: unknown) {
|
||||||
const payload = normalizeCanvasProjectExport(value);
|
const payload = normalizeCanvasProjectExport(value);
|
||||||
if (!payload) {
|
if (!payload) {
|
||||||
throw new Error("导入 JSON 不是有效的画布导出格式");
|
throw new Error("导入 JSON 不是有效的画布导出格式");
|
||||||
@@ -54,12 +54,12 @@ export function importCanvasProjectExport(value: unknown) {
|
|||||||
const assetsByNodeId = new Map(payload.assets.map((asset) => [asset.nodeId, asset]));
|
const assetsByNodeId = new Map(payload.assets.map((asset) => [asset.nodeId, asset]));
|
||||||
const data: CanvasProjectData = {
|
const data: CanvasProjectData = {
|
||||||
...payload.data,
|
...payload.data,
|
||||||
nodes: payload.data.nodes.map((node) => {
|
nodes: await Promise.all(payload.data.nodes.map(async (node) => {
|
||||||
if (node.type !== "image") return node;
|
if (node.type !== "image") return node;
|
||||||
const asset = assetsByNodeId.get(node.id);
|
const asset = assetsByNodeId.get(node.id);
|
||||||
if (!asset) return node;
|
if (!asset) return node;
|
||||||
|
|
||||||
const persisted = persistDataUrl(asset.dataUrl, asset.fileName);
|
const persisted = await persistDataUrl(asset.dataUrl, asset.fileName);
|
||||||
return {
|
return {
|
||||||
...node,
|
...node,
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -68,7 +68,7 @@ export function importCanvasProjectExport(value: unknown) {
|
|||||||
filePath: persisted.filePath,
|
filePath: persisted.filePath,
|
||||||
} satisfies CanvasImageNodeMetadata,
|
} satisfies CanvasImageNodeMetadata,
|
||||||
};
|
};
|
||||||
}),
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
const title = payload.title.trim()
|
const title = payload.title.trim()
|
||||||
@@ -82,7 +82,7 @@ export function importCanvasProjectExport(value: unknown) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function collectExportAssets(data: CanvasProjectData) {
|
async function collectExportAssets(data: CanvasProjectData) {
|
||||||
const assets: CanvasProjectExportAsset[] = [];
|
const assets: CanvasProjectExportAsset[] = [];
|
||||||
|
|
||||||
for (const node of data.nodes) {
|
for (const node of data.nodes) {
|
||||||
@@ -91,11 +91,15 @@ function collectExportAssets(data: CanvasProjectData) {
|
|||||||
if (!metadata.imageUrl) continue;
|
if (!metadata.imageUrl) continue;
|
||||||
|
|
||||||
const filePath = metadata.filePath ?? getGeneratedImagePathFromUrl(metadata.imageUrl);
|
const filePath = metadata.filePath ?? getGeneratedImagePathFromUrl(metadata.imageUrl);
|
||||||
if (!filePath || !existsSync(filePath)) {
|
if (!filePath) {
|
||||||
|
throw new Error(`图片文件不存在:${node.title || node.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const buffer = await readPersistedImageFile(filePath);
|
||||||
|
if (!buffer) {
|
||||||
throw new Error(`图片文件不存在:${node.title || node.id}`);
|
throw new Error(`图片文件不存在:${node.title || node.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const buffer = readFileSync(filePath);
|
|
||||||
const mimeType = getMimeType(filePath);
|
const mimeType = getMimeType(filePath);
|
||||||
assets.push({
|
assets.push({
|
||||||
nodeId: node.id,
|
nodeId: node.id,
|
||||||
|
|||||||
@@ -455,7 +455,7 @@ async function runImageJob(
|
|||||||
});
|
});
|
||||||
|
|
||||||
const id = crypto.randomUUID();
|
const id = crypto.randomUUID();
|
||||||
const persisted = persistBase64Image(id, input.outputFormat, b64);
|
const persisted = await persistBase64Image(id, input.outputFormat, b64);
|
||||||
const createdAt = new Date().toISOString();
|
const createdAt = new Date().toISOString();
|
||||||
const resultPayload = {
|
const resultPayload = {
|
||||||
id,
|
id,
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
import { GetObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
||||||
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
||||||
import { basename, join } from "node:path";
|
import { basename, join } from "node:path";
|
||||||
|
import { Readable } from "node:stream";
|
||||||
|
|
||||||
const imageDir = join(
|
const imageDir = join(
|
||||||
/* turbopackIgnore: true */ process.cwd(),
|
/* turbopackIgnore: true */ process.cwd(),
|
||||||
@@ -13,6 +15,23 @@ export type PersistedImageFile = {
|
|||||||
imageUrl: string;
|
imageUrl: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type S3StorageConfig = {
|
||||||
|
endpoint: string;
|
||||||
|
accessKeyId: string;
|
||||||
|
secretAccessKey: string;
|
||||||
|
bucket: string;
|
||||||
|
region: string;
|
||||||
|
publicUrl?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const s3ObjectPrefix = "generated";
|
||||||
|
const defaultBucket = "imagegen-tools";
|
||||||
|
const defaultRegion = "us-east-1";
|
||||||
|
|
||||||
|
const globalForS3Storage = globalThis as typeof globalThis & {
|
||||||
|
imageStorageS3Client?: S3Client;
|
||||||
|
};
|
||||||
|
|
||||||
export function getImageDir() {
|
export function getImageDir() {
|
||||||
if (!existsSync(imageDir)) {
|
if (!existsSync(imageDir)) {
|
||||||
mkdirSync(imageDir, { recursive: true });
|
mkdirSync(imageDir, { recursive: true });
|
||||||
@@ -21,27 +40,54 @@ export function getImageDir() {
|
|||||||
return imageDir;
|
return imageDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function persistBase64Image(
|
export async function persistBase64Image(
|
||||||
id: string,
|
id: string,
|
||||||
extension: string,
|
extension: string,
|
||||||
base64: string,
|
base64: string,
|
||||||
): PersistedImageFile {
|
): Promise<PersistedImageFile> {
|
||||||
const normalizedExtension = extension === "jpeg" ? "jpg" : extension;
|
const normalizedExtension = extension === "jpeg" ? "jpg" : extension;
|
||||||
const fileName = `${id}.${normalizedExtension}`;
|
const fileName = `${id}.${normalizedExtension}`;
|
||||||
const filePath = join(getImageDir(), fileName);
|
return persistImageBufferWithFileName(fileName, Buffer.from(base64, "base64"));
|
||||||
const imageUrl = `/generated/${fileName}`;
|
|
||||||
writeFileSync(filePath, Buffer.from(base64, "base64"));
|
|
||||||
|
|
||||||
return { fileName, filePath, imageUrl };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function persistUploadedImageFile(
|
export async function persistUploadedImageFile(
|
||||||
id: string,
|
id: string,
|
||||||
filename: string,
|
filename: string,
|
||||||
buffer: Buffer,
|
buffer: Buffer,
|
||||||
): PersistedImageFile {
|
): Promise<PersistedImageFile> {
|
||||||
const extension = getExtensionFromFilename(filename);
|
const extension = getExtensionFromFilename(filename);
|
||||||
const fileName = `${id}.${extension}`;
|
const fileName = `${id}.${extension}`;
|
||||||
|
return persistImageBufferWithFileName(fileName, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function persistImageBuffer(
|
||||||
|
id: string,
|
||||||
|
extension: string,
|
||||||
|
buffer: Buffer,
|
||||||
|
): Promise<PersistedImageFile> {
|
||||||
|
const normalizedExtension = normalizeExtension(extension);
|
||||||
|
const fileName = `${id}.${normalizedExtension}`;
|
||||||
|
return persistImageBufferWithFileName(fileName, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function readPersistedImageFile(filePath: string): Promise<Buffer | null> {
|
||||||
|
if (filePath.startsWith("s3://")) {
|
||||||
|
return readS3Object(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { readFileSync } = await import("node:fs");
|
||||||
|
if (!existsSync(filePath)) return null;
|
||||||
|
return readFileSync(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isLocalImageFile(filePath: string) {
|
||||||
|
return !filePath.startsWith("s3://");
|
||||||
|
}
|
||||||
|
|
||||||
|
function persistImageBufferLocally(
|
||||||
|
fileName: string,
|
||||||
|
buffer: Buffer,
|
||||||
|
): PersistedImageFile {
|
||||||
const filePath = join(getImageDir(), fileName);
|
const filePath = join(getImageDir(), fileName);
|
||||||
const imageUrl = `/generated/${fileName}`;
|
const imageUrl = `/generated/${fileName}`;
|
||||||
writeFileSync(filePath, buffer);
|
writeFileSync(filePath, buffer);
|
||||||
@@ -49,18 +95,33 @@ export function persistUploadedImageFile(
|
|||||||
return { fileName, filePath, imageUrl };
|
return { fileName, filePath, imageUrl };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function persistImageBuffer(
|
async function persistImageBufferWithFileName(
|
||||||
id: string,
|
fileName: string,
|
||||||
extension: string,
|
|
||||||
buffer: Buffer,
|
buffer: Buffer,
|
||||||
): PersistedImageFile {
|
): Promise<PersistedImageFile> {
|
||||||
const normalizedExtension = normalizeExtension(extension);
|
const config = getS3StorageConfig();
|
||||||
const fileName = `${id}.${normalizedExtension}`;
|
if (!config) {
|
||||||
const filePath = join(getImageDir(), fileName);
|
return persistImageBufferLocally(fileName, buffer);
|
||||||
const imageUrl = `/generated/${fileName}`;
|
}
|
||||||
writeFileSync(filePath, buffer);
|
|
||||||
|
|
||||||
return { fileName, filePath, imageUrl };
|
const key = `${s3ObjectPrefix}/${fileName}`;
|
||||||
|
const contentType = getMimeTypeFromFileName(fileName);
|
||||||
|
const client = getS3Client(config);
|
||||||
|
|
||||||
|
await client.send(
|
||||||
|
new PutObjectCommand({
|
||||||
|
Bucket: config.bucket,
|
||||||
|
Key: key,
|
||||||
|
Body: buffer,
|
||||||
|
ContentType: contentType,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
fileName,
|
||||||
|
filePath: `s3://${config.bucket}/${key}`,
|
||||||
|
imageUrl: createPublicImageUrl(config, key),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getGeneratedImagePathFromUrl(imageUrl: string) {
|
export function getGeneratedImagePathFromUrl(imageUrl: string) {
|
||||||
@@ -68,6 +129,104 @@ export function getGeneratedImagePathFromUrl(imageUrl: string) {
|
|||||||
return join(getImageDir(), basename(imageUrl));
|
return join(getImageDir(), basename(imageUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getS3StorageConfig(): S3StorageConfig | null {
|
||||||
|
const endpoint = process.env.FILE_STORAGE_API?.trim();
|
||||||
|
const accessKeyId = process.env.FILE_STORAGE_ACCESS_KEY?.trim();
|
||||||
|
const secretAccessKey = process.env.FILE_STORAGE_SECRET_KEY?.trim();
|
||||||
|
const bucket = process.env.FILE_STORAGE_BUCKET?.trim() || defaultBucket;
|
||||||
|
|
||||||
|
// ✅ 添加调试日志
|
||||||
|
console.log('S3 Config Check:', {
|
||||||
|
hasEndpoint: !!endpoint,
|
||||||
|
hasAccessKey: !!accessKeyId,
|
||||||
|
hasSecretKey: !!secretAccessKey,
|
||||||
|
bucket,
|
||||||
|
endpoint: endpoint ? endpoint.replace(/\/+$/, "") : 'not set',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!endpoint || !accessKeyId || !secretAccessKey) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
endpoint: endpoint.replace(/\/+$/, ""),
|
||||||
|
accessKeyId,
|
||||||
|
secretAccessKey,
|
||||||
|
bucket: process.env.FILE_STORAGE_BUCKET?.trim() || defaultBucket,
|
||||||
|
region: process.env.FILE_STORAGE_REGION?.trim() || defaultRegion,
|
||||||
|
publicUrl: process.env.FILE_STORAGE_PUBLIC_URL?.trim().replace(/\/+$/, ""),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getS3Client(config: S3StorageConfig) {
|
||||||
|
if (!globalForS3Storage.imageStorageS3Client) {
|
||||||
|
globalForS3Storage.imageStorageS3Client = new S3Client({
|
||||||
|
endpoint: config.endpoint,
|
||||||
|
region: config.region,
|
||||||
|
credentials: {
|
||||||
|
accessKeyId: config.accessKeyId,
|
||||||
|
secretAccessKey: config.secretAccessKey,
|
||||||
|
},
|
||||||
|
forcePathStyle: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return globalForS3Storage.imageStorageS3Client;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readS3Object(filePath: string) {
|
||||||
|
const config = getS3StorageConfig();
|
||||||
|
if (!config) return null;
|
||||||
|
|
||||||
|
const object = parseS3FilePath(filePath);
|
||||||
|
if (!object) return null;
|
||||||
|
|
||||||
|
const response = await getS3Client(config).send(
|
||||||
|
new GetObjectCommand({
|
||||||
|
Bucket: object.bucket,
|
||||||
|
Key: object.key,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.Body) return null;
|
||||||
|
if (response.Body instanceof Readable) {
|
||||||
|
return streamToBuffer(response.Body);
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = response.Body as { transformToByteArray?: () => Promise<Uint8Array> };
|
||||||
|
if (body.transformToByteArray) {
|
||||||
|
return Buffer.from(await body.transformToByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseS3FilePath(filePath: string) {
|
||||||
|
const match = /^s3:\/\/([^/]+)\/(.+)$/.exec(filePath);
|
||||||
|
if (!match) return null;
|
||||||
|
return {
|
||||||
|
bucket: match[1],
|
||||||
|
key: match[2],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function streamToBuffer(stream: Readable) {
|
||||||
|
const chunks: Buffer[] = [];
|
||||||
|
for await (const chunk of stream) {
|
||||||
|
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
||||||
|
}
|
||||||
|
return Buffer.concat(chunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPublicImageUrl(config: S3StorageConfig, key: string) {
|
||||||
|
const encodedKey = key.split("/").map(encodeURIComponent).join("/");
|
||||||
|
if (config.publicUrl) {
|
||||||
|
return `${config.publicUrl}/${encodedKey}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${config.endpoint}/${config.bucket}/${encodedKey}`;
|
||||||
|
}
|
||||||
|
|
||||||
function getExtensionFromFilename(filename: string) {
|
function getExtensionFromFilename(filename: string) {
|
||||||
const parts = filename.split(".");
|
const parts = filename.split(".");
|
||||||
const extension = parts.at(-1)?.toLowerCase();
|
const extension = parts.at(-1)?.toLowerCase();
|
||||||
@@ -78,3 +237,10 @@ function normalizeExtension(extension: string | undefined) {
|
|||||||
if (!extension || extension.length > 5) return "png";
|
if (!extension || extension.length > 5) return "png";
|
||||||
return extension === "jpeg" ? "jpg" : extension;
|
return extension === "jpeg" ? "jpg" : extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMimeTypeFromFileName(fileName: string) {
|
||||||
|
const extension = getExtensionFromFilename(fileName);
|
||||||
|
if (extension === "jpg" || extension === "jpeg") return "image/jpeg";
|
||||||
|
if (extension === "webp") return "image/webp";
|
||||||
|
return "image/png";
|
||||||
|
}
|
||||||
|
|||||||