db.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. package database
  2. import (
  3. "database/sql"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "time"
  9. _ "modernc.org/sqlite"
  10. )
  11. // DB 数据库实例
  12. type DB struct {
  13. *sql.DB
  14. }
  15. // FTPUser FTP用户
  16. type FTPUser struct {
  17. ID int64 `json:"id"`
  18. Username string `json:"username"`
  19. Password string `json:"password,omitempty"`
  20. HomeDir string `json:"home_dir"`
  21. Permissions string `json:"permissions"` // "read", "write", "read,write", "admin"
  22. QuotaSize int64 `json:"quota_size"` // 字节, 0 = 无限制
  23. QuotaFiles int `json:"quota_files"` // 文件数, 0 = 无限制
  24. UploadRate int `json:"upload_rate"` // KB/s, 0 = 无限制
  25. DownloadRate int `json:"download_rate"` // KB/s, 0 = 无限制
  26. Enabled bool `json:"enabled"`
  27. CreatedAt string `json:"created_at"`
  28. UpdatedAt string `json:"updated_at"`
  29. }
  30. // FTPLog FTP操作日志
  31. type FTPLog struct {
  32. ID int64 `json:"id"`
  33. Username string `json:"username"`
  34. IP string `json:"ip"`
  35. Action string `json:"action"`
  36. FilePath string `json:"file_path"`
  37. FileSize int64 `json:"file_size"`
  38. Status string `json:"status"`
  39. CreatedAt string `json:"created_at"`
  40. }
  41. // OnlineUser 在线用户
  42. type OnlineUser struct {
  43. ID int `json:"id"`
  44. Username string `json:"username"`
  45. IP string `json:"ip"`
  46. LoginTime time.Time `json:"login_time"`
  47. LastActivity time.Time `json:"last_activity"`
  48. CurrentDir string `json:"current_dir"`
  49. }
  50. // IPAccessRule IP访问规则
  51. type IPAccessRule struct {
  52. ID int64 `json:"id"`
  53. Username string `json:"username"` // 为空表示全局规则,有值表示用户专属规则
  54. IP string `json:"ip"` // 支持单IP、CIDR(192.168.1.0/24)、IP范围(192.168.1.1-192.168.1.100)
  55. Type string `json:"type"` // "whitelist" 或 "blacklist"
  56. Note string `json:"note"` // 备注说明
  57. Enabled bool `json:"enabled"`
  58. CreatedAt string `json:"created_at"`
  59. }
  60. // Open 打开数据库
  61. func Open(dbPath string) (*DB, error) {
  62. dir := filepath.Dir(dbPath)
  63. if err := os.MkdirAll(dir, 0755); err != nil {
  64. return nil, fmt.Errorf("创建数据库目录失败: %w", err)
  65. }
  66. db, err := sql.Open("sqlite", dbPath+"?_journal_mode=WAL&_busy_timeout=5000")
  67. if err != nil {
  68. return nil, fmt.Errorf("打开数据库失败: %w", err)
  69. }
  70. if err := db.Ping(); err != nil {
  71. return nil, fmt.Errorf("连接数据库失败: %w", err)
  72. }
  73. d := &DB{db}
  74. if err := d.initTables(); err != nil {
  75. return nil, err
  76. }
  77. return d, nil
  78. }
  79. // initTables 初始化数据表
  80. func (db *DB) initTables() error {
  81. schema := `
  82. CREATE TABLE IF NOT EXISTS ftp_users (
  83. id INTEGER PRIMARY KEY AUTOINCREMENT,
  84. username TEXT NOT NULL UNIQUE,
  85. password TEXT NOT NULL,
  86. home_dir TEXT NOT NULL,
  87. permissions TEXT DEFAULT 'read',
  88. quota_size INTEGER DEFAULT 0,
  89. quota_files INTEGER DEFAULT 0,
  90. upload_rate INTEGER DEFAULT 0,
  91. download_rate INTEGER DEFAULT 0,
  92. enabled INTEGER DEFAULT 1,
  93. created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  94. updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
  95. );
  96. CREATE TABLE IF NOT EXISTS ftp_logs (
  97. id INTEGER PRIMARY KEY AUTOINCREMENT,
  98. username TEXT DEFAULT '',
  99. ip TEXT DEFAULT '',
  100. action TEXT NOT NULL,
  101. file_path TEXT DEFAULT '',
  102. file_size INTEGER DEFAULT 0,
  103. status TEXT DEFAULT 'success',
  104. created_at DATETIME DEFAULT CURRENT_TIMESTAMP
  105. );
  106. CREATE INDEX IF NOT EXISTS idx_logs_username ON ftp_logs(username);
  107. CREATE INDEX IF NOT EXISTS idx_logs_created ON ftp_logs(created_at);
  108. CREATE INDEX IF NOT EXISTS idx_logs_action ON ftp_logs(action);
  109. CREATE TABLE IF NOT EXISTS system_stats (
  110. id INTEGER PRIMARY KEY AUTOINCREMENT,
  111. total_uploads INTEGER DEFAULT 0,
  112. total_downloads INTEGER DEFAULT 0,
  113. total_upload_bytes INTEGER DEFAULT 0,
  114. total_download_bytes INTEGER DEFAULT 0,
  115. updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
  116. );
  117. CREATE TABLE IF NOT EXISTS ip_access_rules (
  118. id INTEGER PRIMARY KEY AUTOINCREMENT,
  119. username TEXT DEFAULT '',
  120. ip TEXT NOT NULL,
  121. type TEXT NOT NULL DEFAULT 'blacklist',
  122. note TEXT DEFAULT '',
  123. enabled INTEGER DEFAULT 1,
  124. created_at DATETIME DEFAULT CURRENT_TIMESTAMP
  125. );
  126. CREATE INDEX IF NOT EXISTS idx_ip_rules_type ON ip_access_rules(type);
  127. CREATE INDEX IF NOT EXISTS idx_ip_rules_enabled ON ip_access_rules(enabled);
  128. CREATE INDEX IF NOT EXISTS idx_ip_rules_username ON ip_access_rules(username);
  129. `
  130. _, err := db.Exec(schema)
  131. return err
  132. }
  133. // --- FTP用户 CRUD ---
  134. // CreateUser 创建FTP用户
  135. func (db *DB) CreateUser(user *FTPUser) error {
  136. now := time.Now().Format("2006-01-02 15:04:05")
  137. result, err := db.Exec(`
  138. INSERT INTO ftp_users (username, password, home_dir, permissions, quota_size, quota_files,
  139. upload_rate, download_rate, enabled, created_at, updated_at)
  140. VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
  141. user.Username, user.Password, user.HomeDir, user.Permissions,
  142. user.QuotaSize, user.QuotaFiles, user.UploadRate, user.DownloadRate,
  143. user.Enabled, now, now)
  144. if err != nil {
  145. return fmt.Errorf("创建用户失败: %w", err)
  146. }
  147. user.ID, _ = result.LastInsertId()
  148. user.CreatedAt = now
  149. user.UpdatedAt = now
  150. return nil
  151. }
  152. // GetUser 获取用户
  153. func (db *DB) GetUser(username string) (*FTPUser, error) {
  154. user := &FTPUser{}
  155. var enabled int
  156. err := db.QueryRow(`
  157. SELECT id, username, password, home_dir, permissions, quota_size, quota_files,
  158. upload_rate, download_rate, enabled, created_at, updated_at
  159. FROM ftp_users WHERE username = ?`, username,
  160. ).Scan(&user.ID, &user.Username, &user.Password, &user.HomeDir, &user.Permissions,
  161. &user.QuotaSize, &user.QuotaFiles, &user.UploadRate, &user.DownloadRate,
  162. &enabled, &user.CreatedAt, &user.UpdatedAt)
  163. if err == sql.ErrNoRows {
  164. return nil, nil
  165. }
  166. if err != nil {
  167. return nil, err
  168. }
  169. user.Enabled = enabled == 1
  170. return user, nil
  171. }
  172. // ListUsers 列出所有用户
  173. func (db *DB) ListUsers() ([]FTPUser, error) {
  174. rows, err := db.Query(`
  175. SELECT id, username, password, home_dir, permissions, quota_size, quota_files,
  176. upload_rate, download_rate, enabled, created_at, updated_at
  177. FROM ftp_users ORDER BY id`)
  178. if err != nil {
  179. return nil, err
  180. }
  181. defer rows.Close()
  182. var users []FTPUser
  183. for rows.Next() {
  184. var user FTPUser
  185. var enabled int
  186. err := rows.Scan(&user.ID, &user.Username, &user.Password, &user.HomeDir, &user.Permissions,
  187. &user.QuotaSize, &user.QuotaFiles, &user.UploadRate, &user.DownloadRate,
  188. &enabled, &user.CreatedAt, &user.UpdatedAt)
  189. if err != nil {
  190. return nil, err
  191. }
  192. user.Enabled = enabled == 1
  193. users = append(users, user)
  194. }
  195. return users, nil
  196. }
  197. // UpdateUser 更新用户
  198. func (db *DB) UpdateUser(user *FTPUser) error {
  199. now := time.Now().Format("2006-01-02 15:04:05")
  200. _, err := db.Exec(`
  201. UPDATE ftp_users SET home_dir=?, permissions=?, quota_size=?, quota_files=?,
  202. upload_rate=?, download_rate=?, enabled=?, updated_at=?
  203. WHERE username=?`,
  204. user.HomeDir, user.Permissions, user.QuotaSize, user.QuotaFiles,
  205. user.UploadRate, user.DownloadRate, user.Enabled, now, user.Username)
  206. return err
  207. }
  208. // UpdateUserPassword 更新用户密码
  209. func (db *DB) UpdateUserPassword(username, password string) error {
  210. now := time.Now().Format("2006-01-02 15:04:05")
  211. _, err := db.Exec(`UPDATE ftp_users SET password=?, updated_at=? WHERE username=?`,
  212. password, now, username)
  213. return err
  214. }
  215. // DeleteUser 删除用户
  216. func (db *DB) DeleteUser(username string) error {
  217. _, err := db.Exec(`DELETE FROM ftp_users WHERE username=?`, username)
  218. return err
  219. }
  220. // --- 日志 ---
  221. // AddLog 添加操作日志
  222. func (db *DB) AddLog(log *FTPLog) error {
  223. now := time.Now().Format("2006-01-02 15:04:05")
  224. result, err := db.Exec(`
  225. INSERT INTO ftp_logs (username, ip, action, file_path, file_size, status, created_at)
  226. VALUES (?, ?, ?, ?, ?, ?, ?)`,
  227. log.Username, log.IP, log.Action, log.FilePath, log.FileSize, log.Status, now)
  228. if err != nil {
  229. return err
  230. }
  231. log.ID, _ = result.LastInsertId()
  232. log.CreatedAt = now
  233. return nil
  234. }
  235. // QueryLogs 查询日志
  236. func (db *DB) QueryLogs(username, action string, page, pageSize int) ([]FTPLog, int, error) {
  237. where := "WHERE 1=1"
  238. args := []interface{}{}
  239. if username != "" {
  240. where += " AND username LIKE ?"
  241. args = append(args, "%"+username+"%")
  242. }
  243. if action != "" {
  244. where += " AND action = ?"
  245. args = append(args, action)
  246. }
  247. // 获取总数
  248. var total int
  249. countSQL := fmt.Sprintf("SELECT COUNT(*) FROM ftp_logs %s", where)
  250. err := db.QueryRow(countSQL, args...).Scan(&total)
  251. if err != nil {
  252. return nil, 0, err
  253. }
  254. // 分页查询
  255. offset := (page - 1) * pageSize
  256. querySQL := fmt.Sprintf(`
  257. SELECT id, username, ip, action, file_path, file_size, status, created_at
  258. FROM ftp_logs %s ORDER BY id DESC LIMIT ? OFFSET ?`, where)
  259. queryArgs := append(args, pageSize, offset)
  260. rows, err := db.Query(querySQL, queryArgs...)
  261. if err != nil {
  262. return nil, 0, err
  263. }
  264. defer rows.Close()
  265. var logs []FTPLog
  266. for rows.Next() {
  267. var log FTPLog
  268. err := rows.Scan(&log.ID, &log.Username, &log.IP, &log.Action, &log.FilePath,
  269. &log.FileSize, &log.Status, &log.CreatedAt)
  270. if err != nil {
  271. return nil, 0, err
  272. }
  273. logs = append(logs, log)
  274. }
  275. return logs, total, nil
  276. }
  277. // GetLogStats 获取日志统计
  278. func (db *DB) GetLogStats() (map[string]interface{}, error) {
  279. stats := make(map[string]interface{})
  280. var totalUsers int
  281. db.QueryRow("SELECT COUNT(*) FROM ftp_users").Scan(&totalUsers)
  282. stats["total_users"] = totalUsers
  283. var enabledUsers int
  284. db.QueryRow("SELECT COUNT(*) FROM ftp_users WHERE enabled=1").Scan(&enabledUsers)
  285. stats["enabled_users"] = enabledUsers
  286. var todayLogins int
  287. db.QueryRow("SELECT COUNT(DISTINCT username) FROM ftp_logs WHERE action='login' AND created_at >= date('now')").Scan(&todayLogins)
  288. stats["today_logins"] = todayLogins
  289. var todayUploads int
  290. db.QueryRow("SELECT COUNT(*) FROM ftp_logs WHERE action='upload' AND created_at >= date('now')").Scan(&todayUploads)
  291. stats["today_uploads"] = todayUploads
  292. var todayDownloads int
  293. db.QueryRow("SELECT COUNT(*) FROM ftp_logs WHERE action='download' AND created_at >= date('now')").Scan(&todayDownloads)
  294. stats["today_downloads"] = todayDownloads
  295. var totalUploadBytes int64
  296. db.QueryRow("SELECT COALESCE(SUM(file_size),0) FROM ftp_logs WHERE action='upload'").Scan(&totalUploadBytes)
  297. stats["total_upload_bytes"] = totalUploadBytes
  298. var totalDownloadBytes int64
  299. db.QueryRow("SELECT COALESCE(SUM(file_size),0) FROM ftp_logs WHERE action='download'").Scan(&totalDownloadBytes)
  300. stats["total_download_bytes"] = totalDownloadBytes
  301. return stats, nil
  302. }
  303. // CleanOldLogs 清理旧日志(保留最近N天)
  304. func (db *DB) CleanOldLogs(days int) (int64, error) {
  305. result, err := db.Exec(`DELETE FROM ftp_logs WHERE created_at < datetime('now', ?||' days')`,
  306. fmt.Sprintf("-%d", days))
  307. if err != nil {
  308. return 0, err
  309. }
  310. return result.RowsAffected()
  311. }
  312. // --- IP访问规则 CRUD ---
  313. // CreateIPRule 创建IP规则
  314. func (db *DB) CreateIPRule(rule *IPAccessRule) error {
  315. now := time.Now().Format("2006-01-02 15:04:05")
  316. result, err := db.Exec(`
  317. INSERT INTO ip_access_rules (username, ip, type, note, enabled, created_at)
  318. VALUES (?, ?, ?, ?, ?, ?)`,
  319. rule.Username, rule.IP, rule.Type, rule.Note, rule.Enabled, now)
  320. if err != nil {
  321. return fmt.Errorf("创建IP规则失败: %w", err)
  322. }
  323. rule.ID, _ = result.LastInsertId()
  324. rule.CreatedAt = now
  325. return nil
  326. }
  327. // ListIPRules 列出IP规则,ruleType和username为空时列出全部
  328. func (db *DB) ListIPRules(ruleType, username string) ([]IPAccessRule, error) {
  329. query := `SELECT id, username, ip, type, note, enabled, created_at FROM ip_access_rules`
  330. var conditions []string
  331. var args []interface{}
  332. if ruleType != "" {
  333. conditions = append(conditions, "type=?")
  334. args = append(args, ruleType)
  335. }
  336. if username != "" {
  337. if username == "__empty__" {
  338. conditions = append(conditions, "username='')")
  339. } else if username == "__has__" {
  340. conditions = append(conditions, "username!='')")
  341. } else {
  342. conditions = append(conditions, "username=?")
  343. args = append(args, username)
  344. }
  345. }
  346. if len(conditions) > 0 {
  347. query += " WHERE " + strings.Join(conditions, " AND ")
  348. }
  349. query += " ORDER BY id"
  350. rows, err := db.Query(query, args...)
  351. if err != nil {
  352. return nil, err
  353. }
  354. defer rows.Close()
  355. var rules []IPAccessRule
  356. for rows.Next() {
  357. var rule IPAccessRule
  358. var enabled int
  359. if err := rows.Scan(&rule.ID, &rule.Username, &rule.IP, &rule.Type, &rule.Note, &enabled, &rule.CreatedAt); err != nil {
  360. return nil, err
  361. }
  362. rule.Enabled = enabled == 1
  363. rules = append(rules, rule)
  364. }
  365. return rules, nil
  366. }
  367. // DeleteIPRule 删除IP规则
  368. func (db *DB) DeleteIPRule(id int64) error {
  369. _, err := db.Exec(`DELETE FROM ip_access_rules WHERE id=?`, id)
  370. return err
  371. }
  372. // UpdateIPRule 更新IP规则
  373. func (db *DB) UpdateIPRule(rule *IPAccessRule) error {
  374. _, err := db.Exec(`UPDATE ip_access_rules SET username=?, ip=?, type=?, note=?, enabled=? WHERE id=?`,
  375. rule.Username, rule.IP, rule.Type, rule.Note, rule.Enabled, rule.ID)
  376. return err
  377. }
  378. // GetEnabledIPRules 获取所有启用的IP规则(全局+指定用户)
  379. func (db *DB) GetEnabledIPRules(username string) ([]IPAccessRule, error) {
  380. rows, err := db.Query(`SELECT id, username, ip, type, note, enabled, created_at
  381. FROM ip_access_rules WHERE enabled=1 AND (username='' OR username=?) ORDER BY id`, username)
  382. if err != nil {
  383. return nil, err
  384. }
  385. defer rows.Close()
  386. var rules []IPAccessRule
  387. for rows.Next() {
  388. var rule IPAccessRule
  389. var enabled int
  390. if err := rows.Scan(&rule.ID, &rule.Username, &rule.IP, &rule.Type, &rule.Note, &enabled, &rule.CreatedAt); err != nil {
  391. return nil, err
  392. }
  393. rule.Enabled = enabled == 1
  394. rules = append(rules, rule)
  395. }
  396. return rules, nil
  397. }