diff --git a/NGINX_API.md b/NGINX_API.md new file mode 100644 index 0000000..85cd00d --- /dev/null +++ b/NGINX_API.md @@ -0,0 +1,260 @@ +# Nginx 服务 API 说明 + +## 功能说明 + +1. **应用启动时自动检测 nginx 状态** + - 启动应用时会自动调用 `OnStartup()` 检测 nginx 是否在运行 + - 如果检测到 nginx 进程,会自动设置 `Running = true` + - 不会自动启动或停止 nginx + +2. **应用关闭时保持 nginx 运行** + - 关闭应用时调用 `OnShutdown()`,但不会停止 nginx + - nginx 进程继续在后台运行 + - 下次启动应用时会自动检测到运行状态 + +## 前端可调用方法 + +### 进程管理 + +#### 启动 Nginx +```typescript +import { App } from '../bindings/go-site-clone' + +try { + await App.StartNginx() + console.log('Nginx 启动成功') +} catch (error) { + console.error('Nginx 启动失败:', error) +} +``` + +#### 停止 Nginx +```typescript +try { + await App.StopNginx() + console.log('Nginx 停止成功') +} catch (error) { + console.error('Nginx 停止失败:', error) +} +``` + +#### 重启 Nginx +```typescript +try { + await App.RestartNginx() + console.log('Nginx 重启成功') +} catch (error) { + console.error('Nginx 重启失败:', error) +} +``` + +#### 重载配置 +```typescript +try { + await App.ReloadNginx() + console.log('Nginx 配置重载成功') +} catch (error) { + console.error('Nginx 配置重载失败:', error) +} +``` + +#### 检查状态 +```typescript +try { + const running = await App.CheckNginxStatus() + console.log('Nginx 运行状态:', running) +} catch (error) { + console.error('检查状态失败:', error) +} +``` + +#### 测试配置 +```typescript +try { + await App.TestNginxConfig() + console.log('配置文件有效') +} catch (error) { + console.error('配置文件测试失败:', error) +} +``` + +### 站点管理 + +#### 添加站点 +```typescript +const siteConfig = { + name: 'example.com', + domains: ['example.com', '127.0.0.1'], + port: 80, + path: '/www/example.com', + index: 'index.html', + enabled: true +} + +try { + await App.AddNginxSite(siteConfig) + console.log('站点添加成功') +} catch (error) { + console.error('站点添加失败:', error) +} +``` + +#### 删除站点 +```typescript +try { + await App.DeleteNginxSite('example.com') + console.log('站点删除成功') +} catch (error) { + console.error('站点删除失败:', error) +} +``` + +#### 更新站点 +```typescript +const updatedConfig = { + name: 'example.com', + domains: ['example.com', 'www.example.com'], + port: 8080, + path: '/www/example.com', + index: 'index.html', + enabled: true +} + +try { + await App.UpdateNginxSite(updatedConfig) + console.log('站点更新成功') +} catch (error) { + console.error('站点更新失败:', error) +} +``` + +#### 获取所有站点 +```typescript +try { + const sites = await App.GetAllNginxSites() + console.log('站点列表:', sites) +} catch (error) { + console.error('获取站点列表失败:', error) +} +``` + +#### 启用站点 +```typescript +try { + await App.EnableNginxSite('example.com') + console.log('站点启用成功') +} catch (error) { + console.error('站点启用失败:', error) +} +``` + +#### 禁用站点 +```typescript +try { + await App.DisableNginxSite('example.com') + console.log('站点禁用成功') +} catch (error) { + console.error('站点禁用失败:', error) +} +``` + +### 日志管理 + +#### 获取访问日志(最后100行) +```typescript +try { + const logs = await App.GetNginxAccessLog(100) + console.log('访问日志:', logs) +} catch (error) { + console.error('获取访问日志失败:', error) +} +``` + +#### 获取错误日志(最后100行) +```typescript +try { + const logs = await App.GetNginxErrorLog(100) + console.log('错误日志:', logs) +} catch (error) { + console.error('获取错误日志失败:', error) +} +``` + +#### 清空日志 +```typescript +try { + await App.ClearNginxLogs() + console.log('日志清空成功') +} catch (error) { + console.error('日志清空失败:', error) +} +``` + +## 数据结构 + +### NginxSiteConfig +```typescript +interface NginxSiteConfig { + ID: string // 站点唯一ID(可选) + name: string // 站点名称 + domains: string[] // 域名列表 + port: number // 监听端口 + path: string // 网站根目录路径 + index: string // 默认首页文件 + enabled: boolean // 是否启用 +} +``` + +## 工作流程示例 + +### 页面初始化时检查 nginx 状态 +```typescript +import { ref, onMounted } from 'vue' +import { App } from '../bindings/go-site-clone' + +const nginxRunning = ref(false) + +onMounted(async () => { + try { + nginxRunning.value = await App.CheckNginxStatus() + } catch (error) { + console.error('检查 nginx 状态失败:', error) + } +}) +``` + +### 完整的站点托管流程 +```typescript +// 1. 检查 nginx 状态 +const running = await App.CheckNginxStatus() + +// 2. 如果未运行,启动 nginx +if (!running) { + await App.StartNginx() +} + +// 3. 添加站点配置 +await App.AddNginxSite({ + name: 'mysite.com', + domains: ['mysite.com', '127.0.0.1'], + port: 80, + path: '/www/mysite.com', + index: 'index.html', + enabled: true +}) + +// 4. 重载配置使其生效 +await App.ReloadNginx() + +// 5. 验证站点已添加 +const sites = await App.GetAllNginxSites() +console.log('当前托管站点:', sites) +``` + +## 注意事项 + +1. **Windows 路径处理**:路径会自动转换为 nginx 兼容格式(斜杠) +2. **配置文件位置**:站点配置保存在 `plugin/nginx/conf/hosts/` 目录 +3. **禁用站点**:禁用的站点配置会移动到 `plugin/nginx/conf/hosts.disabled/` 目录 +4. **自动重载**:添加、删除、更新站点时,如果 nginx 正在运行会自动重载配置 +5. **持久化运行**:应用关闭后 nginx 继续运行,重新打开应用会自动检测状态 diff --git a/STORAGE.md b/STORAGE.md new file mode 100644 index 0000000..d6d8e53 --- /dev/null +++ b/STORAGE.md @@ -0,0 +1,275 @@ +# BBolt 本地化存储方案实施文档 + +## 概述 + +本项目已成功集成 BBolt 嵌入式数据库,实现高效的本地化持久化存储。BBolt 是纯 Go 实现的键值数据库,单文件存储,无需安装额外依赖。 + +## 架构设计 + +### 数据库文件 +- **位置**: `data/site-clone.db` +- **格式**: BBolt 数据库文件(二进制) +- **特点**: 单文件、可复制、可备份 + +### Bucket 结构 + +``` +site-clone.db +├── sites/ # 站点配置数据 +│ ├── example.com → {NginxSiteConfig + 时间戳} +│ └── test.com → {NginxSiteConfig + 时间戳} +├── download_records/ # 下载记录 +│ ├── 1234567890 → {DownloadRecord} +│ └── 1234567891 → {DownloadRecord} +├── access_logs/ # 访问日志(预留) +└── settings/ # 系统设置(预留) +``` + +## 核心功能 + +### 1. 站点配置管理 + +#### 数据结构 +```go +type NginxSiteConfig struct { + ID string `json:"id"` + Name string `json:"name"` + Domains []string `json:"domains"` + Port int `json:"port"` + Path string `json:"path"` + Index string `json:"index"` + Enabled bool `json:"enabled"` +} + +type SiteRecord struct { + NginxSiteConfig + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} +``` + +#### 可用方法 +- `AddSite(site)` - 添加站点配置 +- `GetSite(name)` - 获取单个站点 +- `GetAllSites()` - 获取所有站点 +- `UpdateSite(site)` - 更新站点配置 +- `DeleteSite(name)` - 删除站点配置 +- `UpdateSiteStatus(name, enabled)` - 更新启用状态 +- `GetSitesByStatus(enabled)` - 按状态筛选站点 +- `SiteExists(name)` - 检查站点是否存在 + +### 2. 下载记录管理 + +#### 数据结构 +```go +type DownloadRecord struct { + ID string `json:"id"` + URL string `json:"url"` + SiteName string `json:"site_name"` + Status string `json:"status"` // success, failed, processing + TotalFiles int `json:"total_files"` + Downloaded int `json:"downloaded"` + CSSCount int `json:"css_count"` + ScriptCount int `json:"script_count"` + ImageCount int `json:"image_count"` + VideoCount int `json:"video_count"` + ErrorMsg string `json:"error_msg"` + StartTime time.Time `json:"start_time"` + EndTime time.Time `json:"end_time"` + Duration int64 `json:"duration"` +} +``` + +#### 可用方法 +- `AddDownloadRecord(record)` - 添加下载记录 +- `GetDownloadRecord(id)` - 获取单条记录 +- `GetAllDownloadRecords()` - 获取所有记录 +- `UpdateDownloadRecord(record)` - 更新记录 +- `DeleteDownloadRecord(id)` - 删除记录 +- `GetDownloadRecordsBySite(name)` - 按站点筛选 +- `GetDownloadRecordsByStatus(status)` - 按状态筛选 +- `GetRecentDownloadRecords(limit)` - 获取最近 N 条 +- `GetDownloadStats()` - 获取统计信息 +- `ClearOldDownloadRecords(days)` - 清理旧记录 + +## 使用示例 + +### 前端调用示例 + +#### 1. 添加站点配置 +```typescript +import { App } from "../../../bindings/go-site-clone"; + +// 添加站点 +await App.AddNginxSite({ + name: "example.com", + domains: ["example.com", "127.0.0.1"], + port: 8080, + path: "www/example.com", + index: "index.html", + enabled: true +}); +``` + +#### 2. 获取站点列表 +```typescript +// 获取所有站点 +const sites = await App.GetAllNginxSites(); +console.log("站点列表:", sites); +``` + +#### 3. 添加下载记录 +```typescript +// 开始下载时创建记录 +const record = { + id: "", // 自动生成 + url: "https://example.com", + site_name: "example.com", + status: "processing", + total_files: 100, + downloaded: 0, + start_time: new Date() +}; +await App.AddDownloadRecord(record); +``` + +#### 4. 获取下载统计 +```typescript +// 获取统计信息 +const stats = await App.GetDownloadStats(); +console.log("下载统计:", stats); +// 输出: { total: 10, success: 8, failed: 1, processing: 1, total_files: 1234 } +``` + +### 后端使用示例 + +#### 在其他服务中使用存储 +```go +// 在 app.go 中 +func (a *App) SomeMethod() { + // 使用站点存储 + sites, err := a.store.GetAllSites() + if err != nil { + log.Printf("获取站点失败: %v", err) + return + } + + // 使用下载记录 + records, err := a.store.GetRecentDownloadRecords(10) + if err != nil { + log.Printf("获取记录失败: %v", err) + return + } +} +``` + +## 数据同步机制 + +### 双重存储 +系统同时维护两种存储: +1. **BBolt 数据库**: 用于快速查询和持久化 +2. **Nginx 配置文件**: 用于 nginx 实际运行 + +### 同步策略 +- **添加站点**: 先保存数据库 → 生成配置文件 +- **删除站点**: 先删除配置文件 → 删除数据库记录 +- **更新站点**: 同步更新数据库和配置文件 +- **查询站点**: 优先从数据库读取 → 如果为空则从配置文件迁移 + +## 数据备份 + +### 备份方法 +```typescript +// 前端调用备份 +await App.BackupDatabase("backups/site-clone-backup.db"); +``` + +### 手动备份 +直接复制 `data/site-clone.db` 文件即可 + +### 恢复数据 +将备份文件复制回 `data/site-clone.db` 即可 + +## 性能特点 + +### 优势 +- ✅ **读取速度快**: 索引查询,O(log n) 复杂度 +- ✅ **写入安全**: ACID 事务支持 +- ✅ **并发读取**: 支持多个 goroutine 并发读 +- ✅ **内存占用小**: 仅加载必要数据 +- ✅ **文件紧凑**: 自动压缩和优化 + +### 适用场景 +- ✅ 站点数量: < 10,000 +- ✅ 下载记录: < 100,000 +- ✅ 并发读取: 高 +- ✅ 并发写入: 中等 + +## 注意事项 + +### 1. 数据库初始化 +应用启动时自动初始化,无需手动操作 + +### 2. 错误处理 +所有存储操作都会返回 error,建议: +- 数据库操作失败时记录日志 +- 关键操作失败时提示用户 +- 不要因存储失败而中断主流程 + +### 3. 数据迁移 +首次启动时,系统会自动从 nginx 配置文件迁移数据到数据库 + +### 4. 并发安全 +BBolt 支持: +- ✅ 多个并发读取 +- ⚠️ 同时只能有一个写入(已内部处理) + +## 扩展功能 + +### 未来可添加的功能 +1. **访问日志存储**: 记录每个站点的访问日志 +2. **系统设置**: 存储用户配置和偏好 +3. **定时任务**: 记录定时下载任务 +4. **缓存管理**: 存储页面缓存信息 +5. **用户数据**: 多用户支持(如需要) + +### 添加新 Bucket 示例 +```go +// 在 storage/store.go 添加 +var BucketCustom = []byte("custom_data") + +// 在 NewStore 中初始化 +buckets := [][]byte{BucketSites, BucketDownload, BucketLogs, BucketSettings, BucketCustom} +``` + +## 维护建议 + +### 定期维护 +1. **清理旧记录**: 定期清理超过 30 天的下载记录 + ```go + deleted, _ := store.ClearOldDownloadRecords(30) + ``` + +2. **备份数据库**: 建议每周自动备份 + ```go + store.Backup("backups/weekly-backup.db") + ``` + +3. **监控大小**: 数据库文件超过 100MB 时考虑归档 + +### 故障恢复 +如果数据库损坏: +1. 删除 `data/site-clone.db` +2. 重启应用 +3. 系统会从 nginx 配置文件重建数据库 + +## 总结 + +BBolt 存储方案为项目提供了: +- 🎯 **高性能**: 快速的键值存储 +- 🔒 **高可靠**: ACID 事务保证 +- 📦 **易维护**: 单文件、易备份 +- 🚀 **易扩展**: 简单的 Bucket 模型 +- 💪 **零依赖**: 纯 Go 实现,无需外部数据库 + +完美满足本地化、便携式存储需求! diff --git a/STORAGE_QUICK_START.md b/STORAGE_QUICK_START.md new file mode 100644 index 0000000..b926ecf --- /dev/null +++ b/STORAGE_QUICK_START.md @@ -0,0 +1,207 @@ +# BBolt 存储快速开始 + +## 🚀 已完成的工作 + +✅ 安装 BBolt 依赖 (`go.etcd.io/bbolt@latest`) +✅ 创建存储服务基础架构 (`storage/store.go`) +✅ 实现站点数据 CRUD (`storage/site_store.go`) +✅ 实现下载记录管理 (`storage/download_store.go`) +✅ 集成到应用主服务 (`app.go`) +✅ 更新类型定义 (`types/site_config.go`) +✅ 修复前端类型绑定 (`webpage.vue`) + +## 📁 新增文件 + +``` +go-site-clone/ +├── storage/ # 存储层(新增) +│ ├── store.go # BBolt 核心封装 +│ ├── site_store.go # 站点配置存储 +│ └── download_store.go # 下载记录存储 +├── types/ +│ └── site_config.go # 站点配置类型定义(新增) +├── data/ # 数据目录(运行时自动创建) +│ └── site-clone.db # BBolt 数据库文件 +└── STORAGE.md # 存储方案文档(新增) +``` + +## 🎯 核心功能 + +### 1. 自动初始化 +应用启动时自动创建数据库和必要的 buckets: +- `sites` - 站点配置 +- `download_records` - 下载记录 +- `access_logs` - 访问日志(预留) +- `settings` - 系统设置(预留) + +### 2. 双重存储 +- **数据库**: BBolt 存储(快速查询) +- **配置文件**: Nginx conf 文件(运行时使用) +- 自动同步两者状态 + +### 3. 数据持久化 +- 站点配置自动保存 +- 下载记录自动记录 +- 支持数据查询和统计 + +## 🔧 使用方法 + +### 前端调用示例 + +```typescript +import { App } from "../../../bindings/go-site-clone"; + +// 1. 添加站点 +await App.AddNginxSite({ + ID: "", + Name: "example.com", + Domains: ["example.com", "127.0.0.1"], + Port: 8080, + Path: "www/example.com", + Index: "index.html", + Enabled: true +}); + +// 2. 获取所有站点 +const sites = await App.GetAllNginxSites(); + +// 3. 删除站点 +await App.DeleteNginxSite("example.com"); + +// 4. 添加下载记录 +await App.AddDownloadRecord({ + id: "", + url: "https://example.com", + site_name: "example.com", + status: "processing", + total_files: 100, + downloaded: 0 +}); + +// 5. 获取下载统计 +const stats = await App.GetDownloadStats(); +``` + +## 📊 可用 API + +### 站点管理 +- `AddNginxSite(site)` - 添加站点 +- `GetAllNginxSites()` - 获取所有站点 +- `UpdateNginxSite(site)` - 更新站点 +- `DeleteNginxSite(name)` - 删除站点 +- `EnableNginxSite(name)` - 启用站点 +- `DisableNginxSite(name)` - 禁用站点 + +### 下载记录 +- `AddDownloadRecord(record)` - 添加记录 +- `GetAllDownloadRecords()` - 获取所有记录 +- `GetRecentDownloadRecords(limit)` - 获取最近记录 +- `GetDownloadStats()` - 获取统计信息 +- `DeleteDownloadRecord(id)` - 删除记录 +- `ClearOldDownloadRecords(days)` - 清理旧记录 + +### 数据备份 +- `BackupDatabase(path)` - 备份数据库 + +## 🎨 数据结构 + +### 站点配置 +```go +type NginxSiteConfig struct { + ID string // 唯一标识 + Name string // 站点名称 + Domains []string // 域名列表 + Port int // 端口号 + Path string // 文件路径 + Index string // 默认首页 + Enabled bool // 是否启用 +} +``` + +### 下载记录 +```go +type DownloadRecord struct { + ID string // 记录ID + URL string // 下载URL + SiteName string // 站点名称 + Status string // 状态: success/failed/processing + TotalFiles int // 总文件数 + Downloaded int // 已下载数 + CSSCount int // CSS 数量 + ScriptCount int // JS 数量 + ImageCount int // 图片数量 + VideoCount int // 视频数量 + ErrorMsg string // 错误信息 + StartTime time.Time // 开始时间 + EndTime time.Time // 结束时间 + Duration int64 // 耗时(秒) +} +``` + +## 💡 最佳实践 + +### 1. 错误处理 +```typescript +try { + await App.AddNginxSite(siteConfig); + message.success('添加成功'); +} catch (error) { + message.error('添加失败: ' + error.message); +} +``` + +### 2. 数据同步 +添加、更新、删除站点时,系统会自动: +- 更新数据库 +- 生成/更新/删除 nginx 配置文件 +- 如果 nginx 运行中,自动重载配置 + +### 3. 定期备份 +```typescript +// 建议定期备份数据库 +const backupPath = `backups/backup-${Date.now()}.db`; +await App.BackupDatabase(backupPath); +``` + +### 4. 清理旧数据 +```typescript +// 清理 30 天前的下载记录 +const deleted = await App.ClearOldDownloadRecords(30); +console.log(`清理了 ${deleted} 条记录`); +``` + +## 📝 注意事项 + +1. **数据库位置**: `data/site-clone.db`(自动创建) +2. **备份方式**: 直接复制 db 文件或使用 API +3. **并发安全**: BBolt 内部处理,无需担心 +4. **性能**: 适合 < 10,000 站点,< 100,000 记录 +5. **迁移**: 首次运行自动从 nginx 配置迁移 + +## 🔍 调试 + +### 查看数据库内容 +可以使用 bbolt 命令行工具: +```bash +go install go.etcd.io/bbolt/cmd/bbolt@latest +bbolt dump data/site-clone.db +``` + +### 数据库统计 +```bash +bbolt stats data/site-clone.db +``` + +## 📚 更多信息 + +详细文档请查看 [STORAGE.md](./STORAGE.md) + +## ✅ 验证安装 + +运行应用后检查: +1. `data/` 目录是否创建 +2. `data/site-clone.db` 文件是否存在 +3. 添加站点后,数据库中是否有记录 +4. 控制台是否显示 "数据库初始化成功" + +完成!🎉