后端: - InstanceService.list_databases: Redis 通过 CONFIG GET databases 返回 db0-db15 - InstanceService.list_databases: MongoDB 通过 pymongo list_database_names 返回库列表 - InstanceService.list_tables: MongoDB 通过 list_collection_names 返回集合列表 - PostgreSQL 数据库/表列表已有支持 前端: - TicketCreatePage: 所有数据库类型统一走后端 listDatabases API - Redis 数据库改为下拉选择(db0-db15),替代手动输入 - 浏览面板: MySQL/PG 显示表结构+索引, MongoDB 显示集合, Redis 显示 Keys - 新增 renderCollectionsTab 用于 MongoDB 集合浏览 - isMySQL 重命名为 isSQL, 新增 isMongo 类型判断
135 lines
5.4 KiB
Python
135 lines
5.4 KiB
Python
from typing import List
|
|
import aiomysql
|
|
|
|
from app.models.instance import Instance
|
|
from app.services.db_connector import DBConnector
|
|
|
|
|
|
class InstanceService:
|
|
|
|
@staticmethod
|
|
async def test_connection(instance: Instance) -> dict:
|
|
"""Test database connection using DBConnector."""
|
|
result = await DBConnector.test_connection(instance)
|
|
|
|
# Update instance status based on result
|
|
if result.success:
|
|
instance.status = "connected"
|
|
instance.version = result.version
|
|
else:
|
|
instance.status = "error"
|
|
|
|
return result.to_dict()
|
|
|
|
@staticmethod
|
|
async def list_databases(instance: Instance) -> List[str]:
|
|
"""List databases for the given instance type."""
|
|
if instance.db_type == "mysql":
|
|
conn = await aiomysql.connect(
|
|
host=instance.host, port=instance.port,
|
|
user=instance.username, password=instance.password,
|
|
)
|
|
try:
|
|
async with conn.cursor() as cur:
|
|
await cur.execute("SHOW DATABASES")
|
|
rows = await cur.fetchall()
|
|
return [row[0] for row in rows if row[0] not in ("information_schema", "mysql", "performance_schema", "sys")]
|
|
finally:
|
|
conn.close()
|
|
elif instance.db_type in ("postgres", "postgresql"):
|
|
import asyncpg
|
|
dsn = f"postgresql://{instance.username}:{instance.password}@{instance.host}:{instance.port}/postgres"
|
|
conn = await asyncpg.connect(dsn)
|
|
try:
|
|
rows = await conn.fetch("SELECT datname FROM pg_database WHERE datistemplate = false")
|
|
return [r["datname"] for r in rows]
|
|
finally:
|
|
await conn.close()
|
|
elif instance.db_type == "redis":
|
|
from app.services.redis_connector import RedisConnector
|
|
conn = await RedisConnector.get_connection(instance)
|
|
try:
|
|
db_count = await conn.config_get("databases")
|
|
num = int(db_count.get("databases", 16))
|
|
return [f"db{i}" for i in range(num)]
|
|
finally:
|
|
await conn.close()
|
|
elif instance.db_type == "mongo":
|
|
from pymongo import MongoClient
|
|
pwd = instance.password
|
|
if pwd and not pwd.startswith("$"):
|
|
from app.core.crypto import decrypt_password
|
|
try:
|
|
pwd = decrypt_password(pwd)
|
|
except Exception:
|
|
pass
|
|
uri = f"mongodb://{instance.username}:{pwd}@{instance.host}:{instance.port}/admin"
|
|
client = MongoClient(uri, serverSelectionTimeoutMS=5000)
|
|
try:
|
|
dbs = client.list_database_names()
|
|
return [d for d in dbs if d not in ("admin", "config", "local")]
|
|
finally:
|
|
client.close()
|
|
elif instance.db_type == "sqlite":
|
|
return [instance.database or "main"]
|
|
else:
|
|
return []
|
|
|
|
@staticmethod
|
|
async def list_tables(instance: Instance, database: str) -> List[dict]:
|
|
"""List tables in a database."""
|
|
if instance.db_type == "mysql":
|
|
conn = await aiomysql.connect(
|
|
host=instance.host, port=instance.port,
|
|
user=instance.username, password=instance.password,
|
|
db=database,
|
|
)
|
|
try:
|
|
async with conn.cursor(aiomysql.DictCursor) as cur:
|
|
await cur.execute(
|
|
"SELECT TABLE_NAME as name, TABLE_ROWS as rows, TABLE_TYPE as type "
|
|
"FROM information_schema.TABLES WHERE TABLE_SCHEMA = %s",
|
|
(database,)
|
|
)
|
|
return await cur.fetchall()
|
|
finally:
|
|
conn.close()
|
|
elif instance.db_type in ("postgres", "postgresql"):
|
|
import asyncpg
|
|
dsn = f"postgresql://{instance.username}:{instance.password}@{instance.host}:{instance.port}/{database}"
|
|
conn = await asyncpg.connect(dsn)
|
|
try:
|
|
rows = await conn.fetch(
|
|
"SELECT tablename as name FROM pg_tables WHERE schemaname = 'public'"
|
|
)
|
|
return [dict(r) for r in rows]
|
|
finally:
|
|
await conn.close()
|
|
elif instance.db_type == "mongo":
|
|
from pymongo import MongoClient
|
|
pwd = instance.password
|
|
if pwd and not pwd.startswith("$"):
|
|
from app.core.crypto import decrypt_password
|
|
try:
|
|
pwd = decrypt_password(pwd)
|
|
except Exception:
|
|
pass
|
|
uri = f"mongodb://{instance.username}:{pwd}@{instance.host}:{instance.port}/{database}"
|
|
client = MongoClient(uri, serverSelectionTimeoutMS=5000)
|
|
try:
|
|
db_obj = client[database]
|
|
colls = db_obj.list_collection_names()
|
|
return [{"name": c} for c in colls]
|
|
finally:
|
|
client.close()
|
|
elif instance.db_type == "sqlite":
|
|
import sqlite3
|
|
db_path = instance.host
|
|
conn = sqlite3.connect(db_path)
|
|
cur = conn.execute("SELECT name FROM sqlite_master WHERE type='table'")
|
|
tables = [{"name": row[0]} for row in cur.fetchall()]
|
|
conn.close()
|
|
return tables
|
|
else:
|
|
return []
|