| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451 |
- package database
- import (
- "database/sql"
- "fmt"
- "os"
- "path/filepath"
- "strings"
- "time"
- _ "modernc.org/sqlite"
- )
- // DB 数据库实例
- type DB struct {
- *sql.DB
- }
- // FTPUser FTP用户
- type FTPUser struct {
- ID int64 `json:"id"`
- Username string `json:"username"`
- Password string `json:"password,omitempty"`
- HomeDir string `json:"home_dir"`
- Permissions string `json:"permissions"` // "read", "write", "read,write", "admin"
- QuotaSize int64 `json:"quota_size"` // 字节, 0 = 无限制
- QuotaFiles int `json:"quota_files"` // 文件数, 0 = 无限制
- UploadRate int `json:"upload_rate"` // KB/s, 0 = 无限制
- DownloadRate int `json:"download_rate"` // KB/s, 0 = 无限制
- Enabled bool `json:"enabled"`
- CreatedAt string `json:"created_at"`
- UpdatedAt string `json:"updated_at"`
- }
- // FTPLog FTP操作日志
- type FTPLog struct {
- ID int64 `json:"id"`
- Username string `json:"username"`
- IP string `json:"ip"`
- Action string `json:"action"`
- FilePath string `json:"file_path"`
- FileSize int64 `json:"file_size"`
- Status string `json:"status"`
- CreatedAt string `json:"created_at"`
- }
- // OnlineUser 在线用户
- type OnlineUser struct {
- ID int `json:"id"`
- Username string `json:"username"`
- IP string `json:"ip"`
- LoginTime time.Time `json:"login_time"`
- LastActivity time.Time `json:"last_activity"`
- CurrentDir string `json:"current_dir"`
- }
- // IPAccessRule IP访问规则
- type IPAccessRule struct {
- ID int64 `json:"id"`
- Username string `json:"username"` // 为空表示全局规则,有值表示用户专属规则
- IP string `json:"ip"` // 支持单IP、CIDR(192.168.1.0/24)、IP范围(192.168.1.1-192.168.1.100)
- Type string `json:"type"` // "whitelist" 或 "blacklist"
- Note string `json:"note"` // 备注说明
- Enabled bool `json:"enabled"`
- CreatedAt string `json:"created_at"`
- }
- // Open 打开数据库
- func Open(dbPath string) (*DB, error) {
- dir := filepath.Dir(dbPath)
- if err := os.MkdirAll(dir, 0755); err != nil {
- return nil, fmt.Errorf("创建数据库目录失败: %w", err)
- }
- db, err := sql.Open("sqlite", dbPath+"?_journal_mode=WAL&_busy_timeout=5000")
- if err != nil {
- return nil, fmt.Errorf("打开数据库失败: %w", err)
- }
- if err := db.Ping(); err != nil {
- return nil, fmt.Errorf("连接数据库失败: %w", err)
- }
- d := &DB{db}
- if err := d.initTables(); err != nil {
- return nil, err
- }
- return d, nil
- }
- // initTables 初始化数据表
- func (db *DB) initTables() error {
- schema := `
- CREATE TABLE IF NOT EXISTS ftp_users (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- username TEXT NOT NULL UNIQUE,
- password TEXT NOT NULL,
- home_dir TEXT NOT NULL,
- permissions TEXT DEFAULT 'read',
- quota_size INTEGER DEFAULT 0,
- quota_files INTEGER DEFAULT 0,
- upload_rate INTEGER DEFAULT 0,
- download_rate INTEGER DEFAULT 0,
- enabled INTEGER DEFAULT 1,
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
- updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
- );
- CREATE TABLE IF NOT EXISTS ftp_logs (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- username TEXT DEFAULT '',
- ip TEXT DEFAULT '',
- action TEXT NOT NULL,
- file_path TEXT DEFAULT '',
- file_size INTEGER DEFAULT 0,
- status TEXT DEFAULT 'success',
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP
- );
- CREATE INDEX IF NOT EXISTS idx_logs_username ON ftp_logs(username);
- CREATE INDEX IF NOT EXISTS idx_logs_created ON ftp_logs(created_at);
- CREATE INDEX IF NOT EXISTS idx_logs_action ON ftp_logs(action);
- CREATE TABLE IF NOT EXISTS system_stats (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- total_uploads INTEGER DEFAULT 0,
- total_downloads INTEGER DEFAULT 0,
- total_upload_bytes INTEGER DEFAULT 0,
- total_download_bytes INTEGER DEFAULT 0,
- updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
- );
- CREATE TABLE IF NOT EXISTS ip_access_rules (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- username TEXT DEFAULT '',
- ip TEXT NOT NULL,
- type TEXT NOT NULL DEFAULT 'blacklist',
- note TEXT DEFAULT '',
- enabled INTEGER DEFAULT 1,
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP
- );
- CREATE INDEX IF NOT EXISTS idx_ip_rules_type ON ip_access_rules(type);
- CREATE INDEX IF NOT EXISTS idx_ip_rules_enabled ON ip_access_rules(enabled);
- CREATE INDEX IF NOT EXISTS idx_ip_rules_username ON ip_access_rules(username);
- `
- _, err := db.Exec(schema)
- return err
- }
- // --- FTP用户 CRUD ---
- // CreateUser 创建FTP用户
- func (db *DB) CreateUser(user *FTPUser) error {
- now := time.Now().Format("2006-01-02 15:04:05")
- result, err := db.Exec(`
- INSERT INTO ftp_users (username, password, home_dir, permissions, quota_size, quota_files,
- upload_rate, download_rate, enabled, created_at, updated_at)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
- user.Username, user.Password, user.HomeDir, user.Permissions,
- user.QuotaSize, user.QuotaFiles, user.UploadRate, user.DownloadRate,
- user.Enabled, now, now)
- if err != nil {
- return fmt.Errorf("创建用户失败: %w", err)
- }
- user.ID, _ = result.LastInsertId()
- user.CreatedAt = now
- user.UpdatedAt = now
- return nil
- }
- // GetUser 获取用户
- func (db *DB) GetUser(username string) (*FTPUser, error) {
- user := &FTPUser{}
- var enabled int
- err := db.QueryRow(`
- SELECT id, username, password, home_dir, permissions, quota_size, quota_files,
- upload_rate, download_rate, enabled, created_at, updated_at
- FROM ftp_users WHERE username = ?`, username,
- ).Scan(&user.ID, &user.Username, &user.Password, &user.HomeDir, &user.Permissions,
- &user.QuotaSize, &user.QuotaFiles, &user.UploadRate, &user.DownloadRate,
- &enabled, &user.CreatedAt, &user.UpdatedAt)
- if err == sql.ErrNoRows {
- return nil, nil
- }
- if err != nil {
- return nil, err
- }
- user.Enabled = enabled == 1
- return user, nil
- }
- // ListUsers 列出所有用户
- func (db *DB) ListUsers() ([]FTPUser, error) {
- rows, err := db.Query(`
- SELECT id, username, password, home_dir, permissions, quota_size, quota_files,
- upload_rate, download_rate, enabled, created_at, updated_at
- FROM ftp_users ORDER BY id`)
- if err != nil {
- return nil, err
- }
- defer rows.Close()
- var users []FTPUser
- for rows.Next() {
- var user FTPUser
- var enabled int
- err := rows.Scan(&user.ID, &user.Username, &user.Password, &user.HomeDir, &user.Permissions,
- &user.QuotaSize, &user.QuotaFiles, &user.UploadRate, &user.DownloadRate,
- &enabled, &user.CreatedAt, &user.UpdatedAt)
- if err != nil {
- return nil, err
- }
- user.Enabled = enabled == 1
- users = append(users, user)
- }
- return users, nil
- }
- // UpdateUser 更新用户
- func (db *DB) UpdateUser(user *FTPUser) error {
- now := time.Now().Format("2006-01-02 15:04:05")
- _, err := db.Exec(`
- UPDATE ftp_users SET home_dir=?, permissions=?, quota_size=?, quota_files=?,
- upload_rate=?, download_rate=?, enabled=?, updated_at=?
- WHERE username=?`,
- user.HomeDir, user.Permissions, user.QuotaSize, user.QuotaFiles,
- user.UploadRate, user.DownloadRate, user.Enabled, now, user.Username)
- return err
- }
- // UpdateUserPassword 更新用户密码
- func (db *DB) UpdateUserPassword(username, password string) error {
- now := time.Now().Format("2006-01-02 15:04:05")
- _, err := db.Exec(`UPDATE ftp_users SET password=?, updated_at=? WHERE username=?`,
- password, now, username)
- return err
- }
- // DeleteUser 删除用户
- func (db *DB) DeleteUser(username string) error {
- _, err := db.Exec(`DELETE FROM ftp_users WHERE username=?`, username)
- return err
- }
- // --- 日志 ---
- // AddLog 添加操作日志
- func (db *DB) AddLog(log *FTPLog) error {
- now := time.Now().Format("2006-01-02 15:04:05")
- result, err := db.Exec(`
- INSERT INTO ftp_logs (username, ip, action, file_path, file_size, status, created_at)
- VALUES (?, ?, ?, ?, ?, ?, ?)`,
- log.Username, log.IP, log.Action, log.FilePath, log.FileSize, log.Status, now)
- if err != nil {
- return err
- }
- log.ID, _ = result.LastInsertId()
- log.CreatedAt = now
- return nil
- }
- // QueryLogs 查询日志
- func (db *DB) QueryLogs(username, action string, page, pageSize int) ([]FTPLog, int, error) {
- where := "WHERE 1=1"
- args := []interface{}{}
- if username != "" {
- where += " AND username LIKE ?"
- args = append(args, "%"+username+"%")
- }
- if action != "" {
- where += " AND action = ?"
- args = append(args, action)
- }
- // 获取总数
- var total int
- countSQL := fmt.Sprintf("SELECT COUNT(*) FROM ftp_logs %s", where)
- err := db.QueryRow(countSQL, args...).Scan(&total)
- if err != nil {
- return nil, 0, err
- }
- // 分页查询
- offset := (page - 1) * pageSize
- querySQL := fmt.Sprintf(`
- SELECT id, username, ip, action, file_path, file_size, status, created_at
- FROM ftp_logs %s ORDER BY id DESC LIMIT ? OFFSET ?`, where)
- queryArgs := append(args, pageSize, offset)
- rows, err := db.Query(querySQL, queryArgs...)
- if err != nil {
- return nil, 0, err
- }
- defer rows.Close()
- var logs []FTPLog
- for rows.Next() {
- var log FTPLog
- err := rows.Scan(&log.ID, &log.Username, &log.IP, &log.Action, &log.FilePath,
- &log.FileSize, &log.Status, &log.CreatedAt)
- if err != nil {
- return nil, 0, err
- }
- logs = append(logs, log)
- }
- return logs, total, nil
- }
- // GetLogStats 获取日志统计
- func (db *DB) GetLogStats() (map[string]interface{}, error) {
- stats := make(map[string]interface{})
- var totalUsers int
- db.QueryRow("SELECT COUNT(*) FROM ftp_users").Scan(&totalUsers)
- stats["total_users"] = totalUsers
- var enabledUsers int
- db.QueryRow("SELECT COUNT(*) FROM ftp_users WHERE enabled=1").Scan(&enabledUsers)
- stats["enabled_users"] = enabledUsers
- var todayLogins int
- db.QueryRow("SELECT COUNT(DISTINCT username) FROM ftp_logs WHERE action='login' AND created_at >= date('now')").Scan(&todayLogins)
- stats["today_logins"] = todayLogins
- var todayUploads int
- db.QueryRow("SELECT COUNT(*) FROM ftp_logs WHERE action='upload' AND created_at >= date('now')").Scan(&todayUploads)
- stats["today_uploads"] = todayUploads
- var todayDownloads int
- db.QueryRow("SELECT COUNT(*) FROM ftp_logs WHERE action='download' AND created_at >= date('now')").Scan(&todayDownloads)
- stats["today_downloads"] = todayDownloads
- var totalUploadBytes int64
- db.QueryRow("SELECT COALESCE(SUM(file_size),0) FROM ftp_logs WHERE action='upload'").Scan(&totalUploadBytes)
- stats["total_upload_bytes"] = totalUploadBytes
- var totalDownloadBytes int64
- db.QueryRow("SELECT COALESCE(SUM(file_size),0) FROM ftp_logs WHERE action='download'").Scan(&totalDownloadBytes)
- stats["total_download_bytes"] = totalDownloadBytes
- return stats, nil
- }
- // CleanOldLogs 清理旧日志(保留最近N天)
- func (db *DB) CleanOldLogs(days int) (int64, error) {
- result, err := db.Exec(`DELETE FROM ftp_logs WHERE created_at < datetime('now', ?||' days')`,
- fmt.Sprintf("-%d", days))
- if err != nil {
- return 0, err
- }
- return result.RowsAffected()
- }
- // --- IP访问规则 CRUD ---
- // CreateIPRule 创建IP规则
- func (db *DB) CreateIPRule(rule *IPAccessRule) error {
- now := time.Now().Format("2006-01-02 15:04:05")
- result, err := db.Exec(`
- INSERT INTO ip_access_rules (username, ip, type, note, enabled, created_at)
- VALUES (?, ?, ?, ?, ?, ?)`,
- rule.Username, rule.IP, rule.Type, rule.Note, rule.Enabled, now)
- if err != nil {
- return fmt.Errorf("创建IP规则失败: %w", err)
- }
- rule.ID, _ = result.LastInsertId()
- rule.CreatedAt = now
- return nil
- }
- // ListIPRules 列出IP规则,ruleType和username为空时列出全部
- func (db *DB) ListIPRules(ruleType, username string) ([]IPAccessRule, error) {
- query := `SELECT id, username, ip, type, note, enabled, created_at FROM ip_access_rules`
- var conditions []string
- var args []interface{}
- if ruleType != "" {
- conditions = append(conditions, "type=?")
- args = append(args, ruleType)
- }
- if username != "" {
- if username == "__empty__" {
- conditions = append(conditions, "username='')")
- } else if username == "__has__" {
- conditions = append(conditions, "username!='')")
- } else {
- conditions = append(conditions, "username=?")
- args = append(args, username)
- }
- }
- if len(conditions) > 0 {
- query += " WHERE " + strings.Join(conditions, " AND ")
- }
- query += " ORDER BY id"
- rows, err := db.Query(query, args...)
- if err != nil {
- return nil, err
- }
- defer rows.Close()
- var rules []IPAccessRule
- for rows.Next() {
- var rule IPAccessRule
- var enabled int
- if err := rows.Scan(&rule.ID, &rule.Username, &rule.IP, &rule.Type, &rule.Note, &enabled, &rule.CreatedAt); err != nil {
- return nil, err
- }
- rule.Enabled = enabled == 1
- rules = append(rules, rule)
- }
- return rules, nil
- }
- // DeleteIPRule 删除IP规则
- func (db *DB) DeleteIPRule(id int64) error {
- _, err := db.Exec(`DELETE FROM ip_access_rules WHERE id=?`, id)
- return err
- }
- // UpdateIPRule 更新IP规则
- func (db *DB) UpdateIPRule(rule *IPAccessRule) error {
- _, err := db.Exec(`UPDATE ip_access_rules SET username=?, ip=?, type=?, note=?, enabled=? WHERE id=?`,
- rule.Username, rule.IP, rule.Type, rule.Note, rule.Enabled, rule.ID)
- return err
- }
- // GetEnabledIPRules 获取所有启用的IP规则(全局+指定用户)
- func (db *DB) GetEnabledIPRules(username string) ([]IPAccessRule, error) {
- rows, err := db.Query(`SELECT id, username, ip, type, note, enabled, created_at
- FROM ip_access_rules WHERE enabled=1 AND (username='' OR username=?) ORDER BY id`, username)
- if err != nil {
- return nil, err
- }
- defer rows.Close()
- var rules []IPAccessRule
- for rows.Next() {
- var rule IPAccessRule
- var enabled int
- if err := rows.Scan(&rule.ID, &rule.Username, &rule.IP, &rule.Type, &rule.Note, &enabled, &rule.CreatedAt); err != nil {
- return nil, err
- }
- rule.Enabled = enabled == 1
- rules = append(rules, rule)
- }
- return rules, nil
- }
|