package postgres import ( "database/sql" "fmt" "time" _ "github.com/lib/pq" ) // DB 数据库连接包装器 type DB struct { conn *sql.DB } // NewDB 创建新的数据库连接 func NewDB(connString string) (*DB, error) { if connString == "" { return nil, fmt.Errorf("database connection string cannot be empty") } conn, err := sql.Open("postgres", connString) if err != nil { return nil, fmt.Errorf("failed to open database: %w", err) } // 配置连接池 conn.SetMaxOpenConns(25) conn.SetMaxIdleConns(5) conn.SetConnMaxLifetime(5 * time.Minute) // 测试连接 if err := conn.Ping(); err != nil { return nil, fmt.Errorf("failed to ping database: %w", err) } return &DB{conn: conn}, nil } // Close 关闭数据库连接 func (db *DB) Close() error { if db.conn != nil { return db.conn.Close() } return nil } // GetConn 获取底层连接(用于事务等高级操作) func (db *DB) GetConn() *sql.DB { return db.conn } // InitSchema 初始化数据库 schema func (db *DB) InitSchema() error { schema := ` -- Users 表 CREATE TABLE IF NOT EXISTS users ( id VARCHAR(36) PRIMARY KEY, username VARCHAR(255) NOT NULL UNIQUE, password_hash TEXT NOT NULL, email VARCHAR(255) NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_users_username ON users(username); -- Clusters 表 CREATE TABLE IF NOT EXISTS clusters ( id VARCHAR(36) PRIMARY KEY, name VARCHAR(255) NOT NULL UNIQUE, host TEXT NOT NULL, ca_data TEXT, cert_data TEXT, key_data TEXT, token TEXT, description TEXT, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_clusters_name ON clusters(name); -- Registries 表 CREATE TABLE IF NOT EXISTS registries ( id VARCHAR(36) PRIMARY KEY, name VARCHAR(255) NOT NULL UNIQUE, url TEXT NOT NULL, description TEXT, username VARCHAR(255), password TEXT, insecure BOOLEAN DEFAULT FALSE, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_registries_name ON registries(name); -- Instances 表 CREATE TABLE IF NOT EXISTS instances ( id VARCHAR(36) PRIMARY KEY, cluster_id VARCHAR(36) NOT NULL, name VARCHAR(255) NOT NULL, namespace VARCHAR(255) NOT NULL, registry_id VARCHAR(36) NOT NULL, repository TEXT NOT NULL, chart VARCHAR(255) NOT NULL, version VARCHAR(255) NOT NULL, description TEXT, values JSONB, values_yaml TEXT, status VARCHAR(50) NOT NULL, status_reason TEXT, last_operation VARCHAR(50), last_error TEXT, revision INTEGER NOT NULL DEFAULT 1, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_cluster FOREIGN KEY (cluster_id) REFERENCES clusters(id) ON DELETE CASCADE, CONSTRAINT fk_registry FOREIGN KEY (registry_id) REFERENCES registries(id) ON DELETE CASCADE, CONSTRAINT unique_cluster_name UNIQUE (cluster_id, name, namespace) ); CREATE INDEX IF NOT EXISTS idx_instances_cluster ON instances(cluster_id); CREATE INDEX IF NOT EXISTS idx_instances_registry ON instances(registry_id); CREATE INDEX IF NOT EXISTS idx_instances_name ON instances(name); ` _, err := db.conn.Exec(schema) if err != nil { return fmt.Errorf("failed to initialize schema: %w", err) } return nil }