feat: add TableList component with styles and functionality for displaying database tables

feat: implement NewConnectionDialog component for creating and editing database connections with form validation

chore: generate TypeScript definitions and JavaScript bindings for app functions

chore: add models for configuration, connection requests, and database entities
This commit is contained in:
loveuer
2026-04-06 21:45:28 +08:00
parent 9874561410
commit 347ecd0f1b
22 changed files with 2475 additions and 315 deletions

View File

@@ -3,6 +3,7 @@ package services
import (
"context"
"fmt"
"strings"
"time"
"go.uber.org/zap"
@@ -16,9 +17,9 @@ import (
// ConnectionService manages database connections
type ConnectionService struct {
db *gorm.DB
connManager *database.ConnectionManager
encryptSvc *EncryptionService
db *gorm.DB
connManager *database.ConnectionManager
encryptSvc *EncryptionService
}
// NewConnectionService creates a new connection service
@@ -37,7 +38,7 @@ func NewConnectionService(
// GetAllConnections returns all user connections
func (s *ConnectionService) GetAllConnections(ctx context.Context) ([]models.UserConnection, error) {
var connections []models.UserConnection
result := s.db.WithContext(ctx).Find(&connections)
if result.Error != nil {
return nil, fmt.Errorf("failed to get connections: %w", result.Error)
@@ -57,7 +58,7 @@ func (s *ConnectionService) GetAllConnections(ctx context.Context) ([]models.Use
// GetConnectionByID returns a connection by ID
func (s *ConnectionService) GetConnectionByID(ctx context.Context, id string) (*models.UserConnection, error) {
var conn models.UserConnection
result := s.db.WithContext(ctx).First(&conn, "id = ?", id)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
@@ -87,16 +88,16 @@ func (s *ConnectionService) CreateConnection(ctx context.Context, req *models.Cr
}
conn := &models.UserConnection{
ID: utils.GenerateID(),
Name: req.Name,
Type: req.Type,
Host: req.Host,
Port: req.Port,
Username: req.Username,
Password: encryptedPassword,
Database: req.Database,
SSLMode: req.SSLMode,
Timeout: req.Timeout,
ID: utils.GenerateID(),
Name: req.Name,
Type: req.Type,
Host: req.Host,
Port: req.Port,
Username: req.Username,
Password: encryptedPassword,
Database: req.Database,
SSLMode: req.SSLMode,
Timeout: req.Timeout,
}
if conn.Timeout <= 0 {
@@ -204,6 +205,17 @@ func (s *ConnectionService) DeleteConnection(ctx context.Context, id string) err
return nil
}
// DisconnectConnection removes an active connection from the connection manager
func (s *ConnectionService) DisconnectConnection(ctx context.Context, id string) error {
if err := s.connManager.RemoveConnection(id); err != nil {
return fmt.Errorf("failed to disconnect connection: %w", err)
}
config.GetLogger().Info("connection disconnected", zap.String("id", id))
return nil
}
// TestConnection tests a database connection
func (s *ConnectionService) TestConnection(ctx context.Context, id string) (*models.ConnectionTestResult, error) {
// Get connection config
@@ -266,7 +278,7 @@ func (s *ConnectionService) ExecuteQuery(ctx context.Context, connectionID, sql
// Execute query
startTime := time.Now()
var result *models.QueryResult
if utils.IsReadOnlyQuery(sql) {
result, err = dbConn.ExecuteQuery(sql)
@@ -283,7 +295,7 @@ func (s *ConnectionService) ExecuteQuery(ctx context.Context, connectionID, sql
Duration: duration.Milliseconds(),
Success: err == nil,
}
if result != nil {
history.RowsAffected = result.AffectedRows
}
@@ -343,21 +355,35 @@ func (s *ConnectionService) GetTableData(
return nil, err
}
var query string
switch conn.Type {
case models.ConnectionTypeMySQL:
query = fmt.Sprintf("SELECT * FROM `%s` LIMIT %d OFFSET %d", tableName, limit, offset)
case models.ConnectionTypePostgreSQL:
query = fmt.Sprintf(`SELECT * FROM "%s" LIMIT %d OFFSET %d`, tableName, limit, offset)
case models.ConnectionTypeSQLite:
query = fmt.Sprintf(`SELECT * FROM "%s" LIMIT %d OFFSET %d`, tableName, limit, offset)
default:
return nil, models.ErrValidationFailed
}
// Build a properly-quoted table reference that handles 'schema.table' notation
tableRef := buildTableRef(conn.Type, tableName)
query := fmt.Sprintf("SELECT * FROM %s LIMIT %d OFFSET %d", tableRef, limit, offset)
return s.ExecuteQuery(ctx, connectionID, query)
}
// buildTableRef returns a properly-quoted table reference.
// tableName may be plain 'table' or schema-qualified 'schema.table'.
func buildTableRef(dbType models.ConnectionType, tableName string) string {
if strings.Contains(tableName, ".") {
parts := strings.SplitN(tableName, ".", 2)
schema, table := parts[0], parts[1]
switch dbType {
case models.ConnectionTypeMySQL:
return fmt.Sprintf("`%s`.`%s`", schema, table)
default:
return fmt.Sprintf(`"%s"."%s"`, schema, table)
}
}
switch dbType {
case models.ConnectionTypeMySQL:
return fmt.Sprintf("`%s`", tableName)
default:
return fmt.Sprintf(`"%s"`, tableName)
}
}
// GetTableStructure returns the structure of a table
func (s *ConnectionService) GetTableStructure(ctx context.Context, connectionID, tableName string) (*models.TableStructure, error) {
// Get connection config