基于腾讯云 SMH(智能媒体托管)服务构建的全栈管理系统,包含三个子项目:
| 子项目 | 类型 | 说明 |
|---|---|---|
server | Demo | 后端服务示例,演示如何签发 Token、管理空间和配额,接入方需参考此示例实现自己的后端 |
client | Demo | 管理后台示例,演示如何集成 uikit UI Kit,接入方可参考此示例了解接入方式 |
uikit | UIKit(核心产物) | 云盘文件管理组件库,可通过 npm install 直接安装到你的项目中使用 |
server和client是接入参考 Demo,不是最终产物;uikit是可独立发布和安装的 UIKit,是本仓库的核心输出。
# 如果没有安装三个项目的依赖 则需要先安装依赖
cd uikit && npm install && cd ..
cd demo/server && npm install && cd ../..
cd demo/client && npm install && cd ../..
# 1. 启动后端服务
cd demo/server && npm run dev
# 2. 打包 uikit
cd uikit && npm run build:lib
# 3. 启动 用户管理二合一端
cd demo/client && npm run dev
SMH-UI/ ├── demo/ │ ├── client/ # 管理后台 Demo(React + TDesign) │ └── server/ # 后端服务 Demo(Express + smh-node-sdk) └── uikit/ # 云盘 UIKit(React 组件库)
┌─────────────────────────────────────────────────────┐ │ client (5174) │ │ 管理后台 · React + TDesign │ │ │ │ ┌─────────────┐ ┌──────────────────────────────┐ │ │ │ 租户管理 │ │ uikit (UIKit) │ │ │ │ 配额管理 │ │ 云盘文件浏览/上传/下载 │ │ │ │ 踢下线/恢复 │ │ 以 Drawer 形式嵌入 │ │ │ └──────┬──────┘ └──────────┬───────────────────┘ │ │ │ │ │ └─────────┼────────────────────┼───────────────────────┘ │ /api/* │ SMH API (直连) ▼ ▼ ┌──────────────────┐ ┌──────────────────┐ │ server (3001) │ │ 腾讯云 SMH 服务 │ │ Express 后端 │──▶│ 文件存储 API │ │ smh-node-sdk │ └──────────────────┘ └──────────────────┘
| 职责 | 说明 |
|---|---|
| 管理员认证 | JWT 登录/登出,Cookie 会话管理 |
| 租户管理 | 创建/删除空间、查询租户列表 |
| 配额管理 | 查询/修改空间存储配额 |
| Token 签发 | 为前端生成 SMH accessToken(library_secret 仅后端持有) |
| 踢下线/恢复 | 吊销/恢复指定空间的访问权限 |
| 用户绑定 | 管理 spaceId 与 userId 的绑定关系 |
技术栈:Express + cookie-parser + jsonwebtoken + smh-node-sdk
核心文件:
src/app.js — 服务入口src/routes/admin.js — 管理端 API 路由src/routes/space.js — Token 吊销存储 & spaceId-userId 绑定src/services/smh/ — SMH SDK 封装层src/config/index.js — 环境变量配置| 职责 | 说明 |
|---|---|
| 登录页 | 管理员账号密码登录(默认 admin/admin) |
| 仪表盘 | 租户列表、统计概览、搜索过滤 |
| 租户操作 | 创建租户、删除租户、绑定/解绑用户 |
| 配额管理 | 查看/修改每个租户的存储配额 |
| 云盘预览 | 以抽屉方式打开任意租户的云盘,浏览/管理文件 |
| 踢下线/恢复 | 禁用/恢复租户的访问权限 |
技术栈:React 18 + React Router + TDesign + Tailwind CSS + Vite
核心文件:
src/pages/Login.jsx — 登录页src/pages/Dashboard.jsx — 主仪表盘(租户管理 + 云盘入口)src/styles/globals.css — 全局样式| 职责 | 说明 |
|---|---|
| 文件浏览 | 目录树导航、列表/网格视图 |
| 文件操作 | 上传、下载、删除、重命名、移动、复制 |
| Token 管理 | 自动续期、过期检测、并发保护 |
| 可嵌入组件 | 作为 npm 包被 client 或其他项目引用 |
技术栈:React 18 + TDesign + Tailwind CSS + smh-js-sdk
核心文件:
src/index.js — 库入口,导出 SpaceDrive 组件和工具函数src/pages/SpaceDrive.jsx — 顶层云盘组件src/components/cloudFile/file.jsx — 文件管理核心组件src/services/request.js — Token 管理与自动续期src/services/smh.js — SMH API 调用封装两种运行模式:
npm run dev,端口 5177,可独立调试npm run build:lib,打包为 ES Module,供 client 项目引用LIBRARY_ID 和 LIBRARY_SECRET本项目依赖腾讯云 SMH 官方提供的两个 SDK,分别用于服务端和客户端:
| 项目 | 说明 |
|---|---|
| 使用方 | server |
| 版本要求 | ^1.0.0 |
| 用途 | 服务端调用 SMH API,管理令牌、空间、配额、使用量等 |
| 安装 | npm install smh-node-sdk |
核心能力:
smh.token.createToken():签发 admin / space_admin 级别的 accessTokensmh.space.listSpace() / createSpace() / deleteSpace():租户空间的增删查smh.quota.getQuota() / createQuota() / updateQuota():存储配额的查询与设置smh.usage.getUsage():获取空间容量使用情况使用示例:
const { SMHClient } = require('smh-node-sdk')
const smh = new SMHClient({ basePath: 'https://smhxxx.api.tencentsmh.cn' })
smh.setDefaultLibraryId('smhxxx-xxxxx')
// 签发 admin token
const tokenData = await smh.token.createToken({
libraryId: 'smhxxx-xxxxx',
librarySecret: 'your_secret',
grant: 'admin',
period: 3000,
})
| 项目 | 说明 |
|---|---|
| 使用方 | uikit |
| 版本要求 | ^1.0.5 |
| 用途 | 前端直连 SMH API,完成文件浏览、上传、下载、删除等操作 |
| 安装 | npm install smh-js-sdk |
核心能力:
client.directory.listDirectoryByPage() / createDirectory() / deleteDirectory():文件列表、创建/删除目录client.file.deleteFile() / infoFile():文件删除、获取文件信息client.createUploadTask():支持分片上传、断点续传、秒传检测,提供进度回调client.usage.getUsage():获取当前空间的配额和使用量使用示例:
import { SMHClient, TaskStatus } from 'smh-js-sdk'
const client = new SMHClient({
basePath: 'https://smhxxx.api.tencentsmh.cn',
libraryId: 'smhxxx-xxxxx',
spaceId: 'spaceyyy',
accessToken: 'your_access_token',
})
// 获取文件列表
const res = await client.directory.listDirectoryByPage({
filePath: '',
byPage: 1,
page: 1,
pageSize: 100,
})
// 上传文件(支持分片、断点续传)
const uploader = client.createUploadTask({
filePath: 'docs/hello.txt',
file: fileObject,
conflictResolutionStrategy: 'overwrite',
onStateChange: (checkpoint, state, error) => {
if (state === TaskStatus.SUCCESS) console.log('上传成功')
},
onProgress: (info) => console.log(`进度: ${Math.floor(info.progress * 100)}%`),
})
uploader.start()
┌──────────────────────────────────────────────────────────┐ │ 浏览器 (前端) │ │ │ │ uikit 使用 smh-js-sdk 直连 SMH API │ │ ├── 文件浏览、上传、下载、删除、重命名、移动 │ │ └── 前端持有 accessToken(由后端签发,不含 secret) │ │ │ └──────────────────────────┬───────────────────────────────┘ │ 获取 accessToken ▼ ┌──────────────────────────────────────────────────────────┐ │ Node.js 服务端 (后端) │ │ │ │ server 使用 smh-node-sdk 调用 SMH 管理 API │ │ ├── 签发 accessToken(需要 library_secret,仅后端持有) │ │ ├── 空间管理(创建/删除租户) │ │ ├── 配额管理(查询/修改存储上限) │ │ └── 使用量统计 │ │ │ └──────────────────────────────────────────────────────────┘
⚠️ 安全原则:
library_secret仅存在于服务端,前端通过后端签发的accessToken访问 SMH API,永远不会接触到密钥。
cd demo/server
cp .env.example .env
编辑 demo/server/.env,填入实际的 SMH 配置:
# SMH 智能媒体托管 LIBRARY_ID=smhxxx-xxxxx # 媒体库 ID LIBRARY_SECRET=your_secret # 媒体库密钥(重要,不要泄露) SMH_HOST=https://smhxxx-xxxxx.api.tencentsmh.cn # SMH API 地址 # JWT JWT_SECRET=your_jwt_secret # JWT 签名密钥,生产环境请使用强随机字符串 # 服务端口 PORT=3001 # 前端地址(用于 CORS) CLIENT_ORIGIN=http://localhost:5173 ADMIN_ORIGIN=http://localhost:5174
# 安装三个项目的依赖
cd demo/server && npm install && cd ../..
cd uikit && npm install && cd ..
cd demo/client && npm install && cd ../..
admin 引用的是 uikit 的打包产物,首次使用或 uikit 源码变更后,需要先构建:
cd uikit
npm run build:lib
cd ..
⚠️ 每次修改 uikit 源码后,都需要重新执行
npm run build:lib,admin 才能获取到最新的组件。
需要同时启动 server 和 client 两个服务(uikit 作为 npm 包被 client 引用其打包产物,无需单独启动):
# 终端 1:启动后端服务(端口 3001)
cd demo/server
npm run dev
# 终端 2:启动管理后台(端口 5174)
cd demo/client
npm run dev
启动后访问 http://localhost:5174 即可进入管理后台。
💡 client 的 Vite 开发服务器已配置代理,所有
/api/*请求会自动转发到http://localhost:3001。
# 终端 3:启动云盘 UIKit 独立开发模式(端口 5177)
cd uikit
npm run dev
1. 访问 http://localhost:5174 2. 使用 admin/admin 登录 3. 在仪表盘中管理租户: ├── 创建租户 → 输入 userId,系统自动创建 SMH 空间 ├── 配额管理 → 点击配额区域,设置存储上限(GB) ├── 绑定用户 → 为租户绑定业务系统的 userId ├── 进入云盘 → 点击「云盘」按钮,以抽屉方式浏览/管理文件 ├── 踢下线 → 禁止租户获取新的访问令牌 ├── 恢复 → 恢复被踢下线的租户的访问权限 └── 删除租户 → 永久删除租户及其所有数据
uikit 可以作为独立的 UIKit 被任何 React 项目引用:
# 1. 构建 UIKit
cd uikit
npm run build:lib
# 2. 在目标项目中引用(本地路径或发布到 npm)
npm install ../uikit # 本地引用
import { SpaceDrive } from 'smh-web-uikit'
import 'smh-web-uikit/dist/style.css'
<SpaceDrive
basePath="https://smhxxx.api.tencentsmh.cn"
libraryId="smhxxx-xxxxx"
spaceId="spaceyyy"
getAccessToken={async () => {
// 调用你的后端获取 SMH accessToken
const res = await fetch('/api/your-backend/get-token')
const data = await res.json()
return { accessToken: data.accessToken, expiresAt: data.expiresAt }
}}
/>
详细接入文档见 uikit/接入文档.md
本项目附带一个 OpenClaw Skill(cloud-upload-backup),让你在 AI 对话中直接说一句话就能把文件上传到 SMH 云存储并拿到下载链接,无需手动调用 API。
第一步:安装 SDK
npm install -g smh-node-sdk
第二步:写入凭证
在 ~/.openclaw/openclaw.json 的 env 字段中填入你的 SMH 空间信息:
{
"env": {
"smh-basePath": "https://<your-library-id>.api.tencentsmh.cn",
"smh-libraryId": "<your-library-id>",
"smh-spaceId": "<your-space-id>",
"smh-accessToken": "<space_admin 权限的 accessToken>"
}
}
也可以在当前工作目录的 .env 文件中配置:
smh-basePath=https://<your-library-id>.api.tencentsmh.cn smh-libraryId=<your-library-id> smh-spaceId=<your-space-id> smh-accessToken=<space_admin 权限的 accessToken>
accessToken可通过本项目的server后端接口POST /api/admin/generate-space-token获取,也可直接用smh-node-sdk的smh.token.createToken()手动签发。
第三步:加载 Skill
将 cloud-upload-backup/SKILL.md 添加到你的 OpenClaw 配置中,重启后即可使用。
配置完成后,直接在对话中说:
"把
/tmp/report.pdf上传到云,给我下载链接" "备份一下桌面上的photo.jpg" "把这个文件发给我"
AI 会自动调用脚本完成上传,并回复类似:
链接已生成,有效期 2 小时,可直接在浏览器或手机中打开。
📎 report.pdf (2.3 MB) — https://cos.ap-guangzhou.myqcloud.com/report.pdf?sign=xxx
| 模式 | 配置方式 | 适用场景 |
|---|---|---|
| 直接凭证(推荐) | openclaw.json env 字段或 .env 文件中填 smh-accessToken | 长期使用,配置一次即可 |
| 自动换取 Token | openclaw.json env 字段或 .env 文件中填 smh-librarySecret | 需要自动刷新 Token 的场景 |
| 命令行传参 | 每次命令中直接传入参数 | 临时覆盖配置文件 |
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/admin/login | 管理员登录 |
| POST | /api/admin/logout | 管理员登出 |
| POST | /api/admin/getUserInfo | 获取当前登录信息 |
| GET | /api/admin/tenants | 获取租户列表 |
| POST | /api/admin/tenants/refresh | 刷新租户列表 |
| POST | /api/admin/tenants/create | 创建租户 |
| DELETE | /api/admin/tenants/:spaceId | 删除租户 |
| GET | /api/admin/tenants/:spaceId/quota | 查询配额 |
| PUT | /api/admin/tenants/:spaceId/quota | 修改配额 |
| POST | /api/admin/generate-space-token | 生成空间访问令牌 |
| POST | /api/admin/revoke-space | 踢下线(吊销令牌) |
| POST | /api/admin/unrevoke-space | 恢复访问权限 |
| GET | /api/admin/space-status/:spaceId | 查询吊销状态 |
| POST | /api/admin/bind-user | 绑定 spaceId-userId |
| POST | /api/admin/unbind-user | 解绑 spaceId-userId |
| GET | /api/health | 健康检查 |
| 服务 | 端口 | 说明 |
|---|---|---|
| server | 3001 | 后端 API 服务 |
| admin | 5174 | 管理后台前端 |
| uikit | 5177 | 云盘 UIKit 独立开发模式(可选) |