From 08009822248c93782377cb5f8e73b7262b8022e4 Mon Sep 17 00:00:00 2001 From: whm <973418690@qq.com> Date: Mon, 13 Apr 2026 14:50:27 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=86=E7=89=87=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E6=96=AD=E7=82=B9=E7=BB=AD=E4=BC=A0=E3=80=81=E4=B8=B4=E6=97=B6?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E5=90=8E=E5=8F=B0=E9=85=8D=E7=BD=AE=E4=B8=8E?= =?UTF-8?q?=E6=B8=85=E6=89=AB=E3=80=81=E5=AE=87=E6=81=92=E4=BA=91=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 管理端大文件分片上传与 sessionStorage 续传;Nginx 大请求体/超时 - .chunk-uploads 定期清扫;system_config 后台配置保留时长与扫描间隔 - 宇恒云 POST /register 对接与 yuheng_cloud_register_records 留痕;yuheng_cloud:manage 权限 Made-with: Cursor --- MongoDB/create_collections.js | 10 + admin/src/api/admin.js | 36 +- admin/src/layouts/AdminLayout.vue | 4 +- admin/src/router/index.js | 12 + admin/src/utils/siteAssetResumableUpload.js | 152 ++++++ admin/src/views/files/FileManage.vue | 35 +- .../src/views/settings/ChunkUploadCleanup.vue | 91 ++++ .../settings/YuhengCloudAccountManage.vue | 154 ++++++ admin/src/views/sites/ModuleUpload.vue | 5 +- nginx/nginx.conf | 7 +- nginx/yuheng.docker.conf.tpl | 5 + nginx/yuheng.host.conf | 6 +- .../handlers/chunk_upload_cleanup_config.go | 163 ++++++ server/handlers/module_upload.go | 73 +-- server/handlers/multipart_upload.go | 501 ++++++++++++++++++ server/handlers/yuheng_cloud_register.go | 182 +++++++ server/main.go | 11 + server/models/permission.go | 2 + server/models/yuheng_cloud_register.go | 9 + server/pkg/schema/sync.go | 2 + 20 files changed, 1413 insertions(+), 47 deletions(-) create mode 100644 admin/src/utils/siteAssetResumableUpload.js create mode 100644 admin/src/views/settings/ChunkUploadCleanup.vue create mode 100644 admin/src/views/settings/YuhengCloudAccountManage.vue create mode 100644 server/handlers/chunk_upload_cleanup_config.go create mode 100644 server/handlers/multipart_upload.go create mode 100644 server/handlers/yuheng_cloud_register.go create mode 100644 server/models/yuheng_cloud_register.go diff --git a/MongoDB/create_collections.js b/MongoDB/create_collections.js index 3d60b54..2640ae1 100644 --- a/MongoDB/create_collections.js +++ b/MongoDB/create_collections.js @@ -88,4 +88,14 @@ if (!db.getCollectionNames().includes("site_users")) { } db.site_users.createIndex({ username: 1 }, { unique: true, name: "idx_username", background: true }); +// 12. yuheng_cloud_register_records(宇恒云 POST /register 本地留痕:username、password) +if (!db.getCollectionNames().includes("yuheng_cloud_register_records")) { + db.createCollection("yuheng_cloud_register_records"); + print("已创建集合: yuheng_cloud_register_records"); +} +db.yuheng_cloud_register_records.createIndex( + { created_at: -1 }, + { name: "idx_created_at", background: true } +); + print("集合与索引处理完成。"); diff --git a/admin/src/api/admin.js b/admin/src/api/admin.js index cdbabf2..f4f3995 100644 --- a/admin/src/api/admin.js +++ b/admin/src/api/admin.js @@ -28,6 +28,11 @@ export const createUser = (data) => request.post('/admin/users', data) export const updateUser = (id, data) => request.put(`/admin/users/${id}`, data) export const deleteUser = (id) => request.delete(`/admin/users/${id}`) +// 宇恒云账号(POST 云端 /register + 本地 Mongo 仅记 username/password) +export const createYuhengCloudAccount = (data) => + request.post('/admin/yuheng-cloud-accounts', data, { timeout: 60000 }) +export const listYuhengCloudAccounts = (params) => request.get('/admin/yuheng-cloud-accounts', { params }) + // 工作空间 export const getWorkspaces = (params) => request.get('/admin/workspaces', { params }) @@ -46,6 +51,10 @@ export const updatePaymentConfig = (data) => request.put('/admin/payment-config' export const getOfficialSite = () => request.get('/admin/system/official-site') export const setOfficialSite = (siteId) => request.put('/admin/system/official-site', { site_id: siteId }) +/** 分片上传临时目录清理:保留时长、扫描间隔(需 site:manage) */ +export const getChunkUploadCleanup = () => request.get('/admin/system/chunk-upload-cleanup') +export const updateChunkUploadCleanup = (data) => request.put('/admin/system/chunk-upload-cleanup', data) + // 站点管理 export const getSites = () => request.get('/admin/sites') export const getSiteById = (id) => request.get(`/admin/sites/${id}`) @@ -79,8 +88,33 @@ export const uploadSiteAsset = (siteId, file, opts = {}) => { if (opts.folder != null) form.append('folder', opts.folder) form.append('downloadable', opts.downloadable ? 'true' : 'false') if (opts.preserveFilename) form.append('preserve_filename', 'true') - return request.post(`/admin/sites/${siteId}/assets`, form, { headers: { 'Content-Type': 'multipart/form-data' } }) + // 大文件上传:timeout 0 = Axios 不设置请求超时(仍可能受浏览器/系统/代理断开影响) + // 超大文件请用分片 API(见 uploadSiteAssetWithResume) + return request.post(`/admin/sites/${siteId}/assets`, form, { + headers: { 'Content-Type': 'multipart/form-data' }, + timeout: 0 + }) } + +/** 分片上传:创建会话(断点续传) */ +export const initMultipartUpload = (siteId, body) => + request.post(`/admin/sites/${siteId}/assets/init-multipart`, body, { timeout: 60000 }) + +export const getMultipartUploadStatus = (siteId, uploadId) => + request.get(`/admin/sites/${siteId}/assets/multipart/${uploadId}/status`, { timeout: 60000 }) + +export const putMultipartChunk = (siteId, uploadId, chunkIndex, blob) => + request.put(`/admin/sites/${siteId}/assets/multipart/${uploadId}/chunk/${chunkIndex}`, blob, { + headers: { 'Content-Type': 'application/octet-stream' }, + timeout: 180000 + }) + +export const completeMultipartUpload = (siteId, uploadId) => + request.post(`/admin/sites/${siteId}/assets/multipart/${uploadId}/complete`, {}, { timeout: 600000 }) + +export const abortMultipartUpload = (siteId, uploadId) => + request.delete(`/admin/sites/${siteId}/assets/multipart/${uploadId}`, { timeout: 60000 }) + export const createSiteFolder = (siteId, path) => request.post(`/admin/sites/${siteId}/folders`, { path }) export const deleteSiteAsset = (siteId, id) => request.delete(`/admin/sites/${siteId}/assets/${id}`) diff --git a/admin/src/layouts/AdminLayout.vue b/admin/src/layouts/AdminLayout.vue index 32f1830..a840e65 100644 --- a/admin/src/layouts/AdminLayout.vue +++ b/admin/src/layouts/AdminLayout.vue @@ -43,7 +43,7 @@ + + diff --git a/admin/src/views/settings/YuhengCloudAccountManage.vue b/admin/src/views/settings/YuhengCloudAccountManage.vue new file mode 100644 index 0000000..4c9377a --- /dev/null +++ b/admin/src/views/settings/YuhengCloudAccountManage.vue @@ -0,0 +1,154 @@ + + + + + 宇恒云账号管理 + + + 调用云端 + POST /register(默认 + http://www.cloud.yuxindazhineng.com:3001/register,可通过环境变量 + YH_CLOUD_REGISTER_URL 覆盖)。成功后在 Mongo 集合 + yuheng_cloud_register_records 写入一条记录,仅保存账号与密码;邮箱仅用于提交云端。 + + + + + + + + + + + + + + 提交注册 + + + + + + + + + + + {{ row._showPwd ? row.password : '••••••••' }} + + {{ row._showPwd ? '隐藏' : '显示' }} + + + + + + + + + + + + + diff --git a/admin/src/views/sites/ModuleUpload.vue b/admin/src/views/sites/ModuleUpload.vue index 953576a..fd29814 100644 --- a/admin/src/views/sites/ModuleUpload.vue +++ b/admin/src/views/sites/ModuleUpload.vue @@ -43,7 +43,8 @@
+ 调用云端 + POST /register(默认 + http://www.cloud.yuxindazhineng.com:3001/register,可通过环境变量 + YH_CLOUD_REGISTER_URL 覆盖)。成功后在 Mongo 集合 + yuheng_cloud_register_records 写入一条记录,仅保存账号与密码;邮箱仅用于提交云端。 +
POST /register
http://www.cloud.yuxindazhineng.com:3001/register
YH_CLOUD_REGISTER_URL
yuheng_cloud_register_records