From e306d2d06bc2f74ed0a1478e7df8ffacc541cbd1 Mon Sep 17 00:00:00 2001 From: MengMengCode <227010654+MengMengCode@users.noreply.github.com> Date: Fri, 5 Jun 2026 19:23:28 +0800 Subject: [PATCH] first commit --- .github/workflows/build.yml | 66 + .gitignore | 64 + README.md | 129 + backend/go.mod | 12 + backend/go.sum | 10 + backend/internal/api/apikey.go | 263 ++ backend/internal/api/auth.go | 213 ++ backend/internal/api/handlers.go | 440 +++ backend/internal/api/host.go | 376 ++ backend/internal/api/images.go | 320 ++ backend/internal/api/ipv6.go | 21 + backend/internal/api/oversell.go | 224 ++ backend/internal/api/resource_validation.go | 38 + backend/internal/api/security.go | 771 +++++ backend/internal/api/settings.go | 146 + backend/internal/api/ssh.go | 308 ++ backend/internal/api/subuser.go | 422 +++ backend/internal/api/swap.go | 189 ++ backend/internal/api/taskqueue.go | 650 ++++ backend/internal/api/websocket.go | 35 + backend/internal/cli/cli.go | 426 +++ backend/internal/config/config.go | 586 ++++ backend/internal/lxc/expiry.go | 137 + backend/internal/lxc/ipv6.go | 553 +++ backend/internal/lxc/lxc.go | 2374 +++++++++++++ backend/internal/lxc/portmap.go | 196 ++ backend/internal/lxc/templates.go | 74 + backend/internal/server/embed.go | 19 + backend/internal/server/server.go | 138 + backend/internal/server/web/.gitkeep | 1 + backend/main.go | 102 + build.sh | 76 + frontend/index.html | 13 + frontend/package-lock.json | 3023 +++++++++++++++++ frontend/package.json | 30 + frontend/postcss.config.js | 6 + frontend/public/favicon.svg | 1 + frontend/src/App.tsx | 69 + frontend/src/components/AppIcon.tsx | 14 + frontend/src/components/ContainerCard.tsx | 142 + .../src/components/CreateContainerModal.tsx | 342 ++ frontend/src/components/Dialog.tsx | 94 + frontend/src/components/Layout.tsx | 18 + .../src/components/ResourceStatsPanel.tsx | 224 ++ frontend/src/components/RingStats.tsx | 132 + frontend/src/components/Sidebar.tsx | 189 ++ frontend/src/components/WebSSHViewer.tsx | 220 ++ frontend/src/contexts/AuthContext.tsx | 151 + frontend/src/index.css | 32 + frontend/src/main.tsx | 19 + frontend/src/pages/ApiIntegration.tsx | 306 ++ frontend/src/pages/AuditLogs.tsx | 120 + frontend/src/pages/ContainerDetail.tsx | 1482 ++++++++ frontend/src/pages/Containers.tsx | 736 ++++ frontend/src/pages/Dashboard.tsx | 220 ++ frontend/src/pages/ImageManagement.tsx | 283 ++ frontend/src/pages/Login.tsx | 120 + frontend/src/pages/Oversell.tsx | 490 +++ frontend/src/pages/Security.tsx | 131 + frontend/src/pages/Settings.tsx | 188 + frontend/src/services/api.ts | 454 +++ frontend/src/utils/labels.ts | 36 + frontend/tailwind.config.js | 11 + frontend/tsconfig.json | 21 + frontend/vite.config.ts | 18 + install.sh | 111 + 66 files changed, 18825 insertions(+) create mode 100644 .github/workflows/build.yml create mode 100644 .gitignore create mode 100644 README.md create mode 100644 backend/go.mod create mode 100644 backend/go.sum create mode 100644 backend/internal/api/apikey.go create mode 100644 backend/internal/api/auth.go create mode 100644 backend/internal/api/handlers.go create mode 100644 backend/internal/api/host.go create mode 100644 backend/internal/api/images.go create mode 100644 backend/internal/api/ipv6.go create mode 100644 backend/internal/api/oversell.go create mode 100644 backend/internal/api/resource_validation.go create mode 100644 backend/internal/api/security.go create mode 100644 backend/internal/api/settings.go create mode 100644 backend/internal/api/ssh.go create mode 100644 backend/internal/api/subuser.go create mode 100644 backend/internal/api/swap.go create mode 100644 backend/internal/api/taskqueue.go create mode 100644 backend/internal/api/websocket.go create mode 100644 backend/internal/cli/cli.go create mode 100644 backend/internal/config/config.go create mode 100644 backend/internal/lxc/expiry.go create mode 100644 backend/internal/lxc/ipv6.go create mode 100644 backend/internal/lxc/lxc.go create mode 100644 backend/internal/lxc/portmap.go create mode 100644 backend/internal/lxc/templates.go create mode 100644 backend/internal/server/embed.go create mode 100644 backend/internal/server/server.go create mode 100644 backend/internal/server/web/.gitkeep create mode 100644 backend/main.go create mode 100644 build.sh create mode 100644 frontend/index.html create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/postcss.config.js create mode 100644 frontend/public/favicon.svg create mode 100644 frontend/src/App.tsx create mode 100644 frontend/src/components/AppIcon.tsx create mode 100644 frontend/src/components/ContainerCard.tsx create mode 100644 frontend/src/components/CreateContainerModal.tsx create mode 100644 frontend/src/components/Dialog.tsx create mode 100644 frontend/src/components/Layout.tsx create mode 100644 frontend/src/components/ResourceStatsPanel.tsx create mode 100644 frontend/src/components/RingStats.tsx create mode 100644 frontend/src/components/Sidebar.tsx create mode 100644 frontend/src/components/WebSSHViewer.tsx create mode 100644 frontend/src/contexts/AuthContext.tsx create mode 100644 frontend/src/index.css create mode 100644 frontend/src/main.tsx create mode 100644 frontend/src/pages/ApiIntegration.tsx create mode 100644 frontend/src/pages/AuditLogs.tsx create mode 100644 frontend/src/pages/ContainerDetail.tsx create mode 100644 frontend/src/pages/Containers.tsx create mode 100644 frontend/src/pages/Dashboard.tsx create mode 100644 frontend/src/pages/ImageManagement.tsx create mode 100644 frontend/src/pages/Login.tsx create mode 100644 frontend/src/pages/Oversell.tsx create mode 100644 frontend/src/pages/Security.tsx create mode 100644 frontend/src/pages/Settings.tsx create mode 100644 frontend/src/services/api.ts create mode 100644 frontend/src/utils/labels.ts create mode 100644 frontend/tailwind.config.js create mode 100644 frontend/tsconfig.json create mode 100644 frontend/vite.config.ts create mode 100644 install.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..9e7dcbb --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,66 @@ +name: Build + +on: + push: + branches: + - main + - master + tags: + - "v*" + pull_request: + workflow_dispatch: + +permissions: + contents: write + +jobs: + linux-amd64: + name: Linux amd64 + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: npm + cache-dependency-path: frontend/package-lock.json + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: "1.22.x" + cache-dependency-path: backend/go.sum + + - name: Build + shell: bash + run: bash build.sh + + - name: Package + shell: bash + run: | + mkdir -p dist package/clicd-linux-amd64 + cp build/clicd package/clicd-linux-amd64/clicd + cp build/install.sh package/clicd-linux-amd64/install.sh + chmod +x package/clicd-linux-amd64/clicd package/clicd-linux-amd64/install.sh + tar -C package -czf dist/clicd-linux-amd64.tar.gz clicd-linux-amd64 + cp build/clicd dist/clicd-linux-amd64 + sha256sum dist/* > dist/SHA256SUMS + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: clicd-linux-amd64 + path: dist/* + + - name: Publish GitHub Release + if: startsWith(github.ref, 'refs/tags/v') + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + gh release create "$GITHUB_REF_NAME" dist/* --generate-notes || \ + gh release upload "$GITHUB_REF_NAME" dist/* --clobber diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f631566 --- /dev/null +++ b/.gitignore @@ -0,0 +1,64 @@ +# Dependencies +node_modules/ +frontend/node_modules/ + +# Frontend build output +/frontend/dist/ +/web/ + +# Go embedded frontend build output. +# Keep only the placeholder so `go build` can compile before frontend assets exist. +backend/internal/server/web/* +!backend/internal/server/web/.gitkeep + +# Build artifacts +/build/ +*.exe +*.dll +*.so +*.dylib +*.test +*.out +*.prof + +# Local deploy and debug scripts +deploy.py +check_*.py +reset_pass.py + +# Runtime/config data +.clicd/ +config.json +*.db +*.sqlite +*.sqlite3 + +# Environment and secrets +.env +.env.* +*.pem +*.key +id_rsa* + +# Logs +*.log +logs/ + +# Python cache +__pycache__/ +*.py[cod] + +# Go +backend/vendor/ +backend/tmp/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db diff --git a/README.md b/README.md new file mode 100644 index 0000000..2d7155a --- /dev/null +++ b/README.md @@ -0,0 +1,129 @@ +
+
+
+
+
+
+
+
+
+
将创建 {batchCount} 个容器:{form.name}-1 至 {form.name}-{batchCount}
} + +不选择则长期有效;选择日期后,到期会自动关机。
+{dialog.message}
+{chart.detail}
} +管理 API Key 与查看接口文档
+此 Key 仅显示一次,请立即复制保存。
+{newKey}
+
+ | 名称 | +Key 前缀 | +IP 白名单 | +创建时间 | +最后使用 | +操作 | +
|---|---|---|---|---|---|
| {k.name} | +{k.prefix} | +{k.ip_whitelist || '不限制'} | +{k.created_at} | +{k.last_used || '未使用'} | ++ + | +
API 文档
+ 所有 API 使用 POST 方法,在请求头中携带 API Key:
+{path}
+ {desc}
+ {body && (
+ {body}
+ 共 {logs.length} 条操作记录
+| 时间 | +用户 | +操作 | +目标 | +详情 | +
|---|---|---|---|---|
| {log.time} | ++ {log.user === 'admin' ? ( + 管理员 + ) : log.user?.startsWith('user:') ? ( + 用户 + ) : ( + {log.user || '-'} + )} + | +{actionLabel(log.action)} | +{log.target || '-'} | +{log.detail || '-'} | +
容器不存在
+ +打开管理地址,输入用户名和密码即可管理该容器。链接不含 token,无法被截获后直接使用。
+重装系统会删除容器内所有数据,请谨慎操作。
+当前:{formatExpiration(container.expires_at)}
+磁盘容量不支持动态修改。修改后运行中的容器会立即应用新的 cgroup 限制。
+暂无端口映射
+ } + + return ( +| 操作 | } +||||
|---|---|---|---|---|
| + {pm.description} + {isSSH && 默认} + | +{pm.protocol.toUpperCase()} | +{publicHost}:{pm.host_port} | +{pm.container_port} | + {!compact && ( +
+
+
+ {!isSubUser && (
+
+ )}
+
+ |
+ )}
+
共 {displayContainers.length} 个容器{selected.size > 0 && `,已选 ${selected.size} 个`}
+点击"创建容器"开始
+| + {!isSubUser && ( + 0 && selected.size === displayContainers.filter((container) => !container.isPlaceholder && !taskStatusMap[container.id] && !taskNameMap[container.name]).length} + onChange={toggleAll} + className="w-4 h-4 rounded border-gray-300 text-black focus:ring-black accent-black" + /> + )} + | +|||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| + {!isSubUser && ( + toggleSelect(container.id)} + disabled={isPlaceholder || !!taskStatusMap[container.id] || !!taskNameMap[container.name]} + className="w-3.5 h-3.5 rounded border-gray-300 text-black focus:ring-black accent-black disabled:opacity-30" + /> + )} + | ++ #{container.id} + | ++ + | +
+ |
+ + + {getTemplateIcon(container.template)} + {getTemplateName(container.template)} + + | +
+ |
+
+ |
+
+ |
+
+
+
+
+
+
+
+ |
+ + {container.vcpu}核/{formatRAM(container.ram_mb)}/{container.disk_gb}GB + | ++ {container.expires_at ? getRemaining(container.expires_at) : 永久} + | +
+
+ {task?.status === 'failed' ? (
+
+ ) : (
+
+ )}
+
+ |
+
共 {tasks.length} 个任务
+| 状态 | +操作 | +容器 | +创建时间 | +错误 | ++ |
|---|---|---|---|---|---|
| + + {taskStatusLabel(task.status)} + + | +{actionLabel(task.type)} | +{task.container_name} | +{task.created_at} | +{task.error || '-'} | ++ {task.status === 'pending' && ( + + )} + | +
宿主机资源状态与容器概览
++ 管理 LXC 系统镜像模板,下载后的镜像才能用于创建容器。 + 已下载 {downloadedCount}/{images.length} +
+| + 系统镜像 + | ++ 发行版 + | ++ 架构 + | ++ 大小 + | ++ 状态 + | ++ 操作 + | +
|---|---|---|---|---|---|
|
+
+
+ {getTemplateIcon(img.id)}
+
+
+
+ {img.name}
+
+ {img.description} + |
+ + {img.distro} {img.release} + | ++ {img.arch} + | ++ {formatSize(img.size_bytes)} + | +
+ |
+
+
+ {!img.downloaded && !img.downloading && (
+
+ )}
+
+ {img.downloading && (
+
+
+ |
+
{isAccessCodeLogin ? '容器管理登录' : 'LXC Container Manager'}
+CLICD v1.0.0
+超售容量、KSM 与宿主机内存参数
+| 资源 | +实际 | +超售后 | +已分配 | +剩余可开 | +
|---|---|---|---|---|
| {row.label} | +{row.actual} | +{row.capacity} | +{row.allocated} | +{row.remainingCount} | +
| 时间 | +等级 | +类型 | +容器 | +源IP | +目标 | +次数 | +详情 | +
|---|---|---|---|---|---|---|---|
| {alert.timestamp} | +
+ |
+ {typeLabels[alert.type] || alert.type} | +{alert.container_name} | +{alert.source_ip} | ++ {formatTarget(alert)} + | +{alert.count} | +{alert.detail} | +
账号管理与登录日志
+暂无登录记录
+ ) : ( + <> +| 用户名 | +结果 | +|||
|---|---|---|---|---|
| {log.time} | +{log.username} | +{log.ip} | +{formatUA(log.user_agent)} | ++ + {log.success ? '成功' : '失败'} + + | +