Files
ocdp-go/backend/internal/adapter/output/persistence/postgres/db.go
Ivan087 29d0310f03 feat(frontend): add Helm chart browser, monitoring, chart-references and values templates pages
Add new frontend pages for the multi-tenant OCDP platform:

- Charts page (/charts): Browse Harbor OCI registries to list Helm chart repositories
  and versions, with deploy modal to launch charts on selected clusters
- Monitoring page (/monitoring): Display cluster metrics (CPU/Memory/GPU usage)
  and per-node details with resource utilization bars
- Chart References page (/chart-references): CRUD for chart metadata references
- Values Templates page (/templates): CRUD for Helm values templates with version
  history and rollback support
- Sidebar: Add Charts navigation, update Storage and Templates links
- api.ts: Add all API client functions (clusterApi, registryApi, instanceApi,
  monitoringApi, storageApi, chartReferenceApi, valuesTemplateApi,
  workspaceApi, userApi) with full TypeScript types

Note: deploy flow and values template rollback not yet end-to-end tested.
2026-04-15 16:59:31 +08:00

188 lines
5.4 KiB
Go

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);
-- Storage Backends 表
CREATE TABLE IF NOT EXISTS storage_backends (
id VARCHAR(36) PRIMARY KEY,
workspace_id VARCHAR(36),
owner_id VARCHAR(36),
name VARCHAR(255) NOT NULL,
type VARCHAR(50) NOT NULL,
config JSONB NOT NULL,
description TEXT,
is_default BOOLEAN DEFAULT FALSE,
is_shared 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_storage_workspace ON storage_backends(workspace_id);
-- Chart References 表
CREATE TABLE IF NOT EXISTS chart_references (
id VARCHAR(36) PRIMARY KEY,
workspace_id VARCHAR(36),
registry_id VARCHAR(36),
repository VARCHAR(500) NOT NULL,
chart_name VARCHAR(255) NOT NULL,
description TEXT,
is_enabled BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_chart_workspace ON chart_references(workspace_id);
CREATE INDEX IF NOT EXISTS idx_chart_registry ON chart_references(registry_id);
-- Values Templates 表 - 使用复合唯一键替代主键,允许同一模板的多个版本
CREATE TABLE IF NOT EXISTS values_templates (
id VARCHAR(36),
workspace_id VARCHAR(36),
owner_id VARCHAR(36),
chart_reference_id VARCHAR(36),
name VARCHAR(255) NOT NULL,
description TEXT,
values_yaml TEXT NOT NULL,
version INTEGER NOT NULL DEFAULT 1,
is_default BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE (chart_reference_id, name, version)
);
CREATE INDEX IF NOT EXISTS idx_values_template_chart ON values_templates(chart_reference_id);
CREATE INDEX IF NOT EXISTS idx_values_template_workspace ON values_templates(workspace_id);
`
_, err := db.conn.Exec(schema)
if err != nil {
return fmt.Errorf("failed to initialize schema: %w", err)
}
return nil
}